hypertune
Advanced tools
Comparing version 1.12.0 to 1.13.0
# Changelog | ||
## 1.13.0 | ||
- Improved bundle size, reduced dependencies and improved analytics logging. | ||
## 1.12.0 | ||
@@ -4,0 +8,0 @@ |
import { CAC } from "cac"; | ||
import { Endpoints, EndpointsPreset } from "../../shared"; | ||
import { Schema } from "../helpers"; | ||
export type GenerateOptions = { | ||
token?: string; | ||
queryFilePath?: string; | ||
outputFilePath?: string; | ||
includeToken?: boolean; | ||
includeFallback?: boolean; | ||
language?: "ts" | "js"; | ||
endpoints?: EndpointsPreset | Endpoints; | ||
}; | ||
export declare const defaultOptions: { | ||
queryFilePath: string; | ||
outputFilePath: string; | ||
includeToken: boolean; | ||
includeFallback: boolean; | ||
language: string; | ||
}; | ||
export declare const generateOptionsSchema: Schema; | ||
export declare function registerGenerateCommand(cli: CAC): void; | ||
export declare const generate: import("../helpers").Handler<{ | ||
token: string; | ||
queryFilePath?: string | undefined; | ||
outputFilePath?: string | undefined; | ||
includeToken?: boolean | undefined; | ||
includeFallback?: boolean | undefined; | ||
language?: "js" | "ts" | undefined; | ||
endpoints?: "local" | "prod" | "staging" | { | ||
logs: string; | ||
edge: string; | ||
backend: string; | ||
} | undefined; | ||
}, void>; | ||
export declare const generate: import("../helpers").Handler<GenerateOptions, void>; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -15,3 +15,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.generate = exports.registerGenerateCommand = void 0; | ||
exports.generate = exports.registerGenerateCommand = exports.generateOptionsSchema = exports.defaultOptions = void 0; | ||
const node_fs_1 = __importDefault(require("node:fs")); | ||
@@ -23,5 +23,20 @@ const node_path_1 = __importDefault(require("node:path")); | ||
const helpers_1 = require("../helpers"); | ||
const options_1 = require("./options"); | ||
exports.defaultOptions = { | ||
queryFilePath: "hypertune.graphql", | ||
outputFilePath: "generated/generated.ts", | ||
includeToken: false, | ||
includeFallback: false, | ||
language: "ts", | ||
}; | ||
exports.generateOptionsSchema = { | ||
token: "string", | ||
queryFilePath: "string", | ||
outputFilePath: "string", | ||
includeToken: "boolean", | ||
includeFallback: "boolean", | ||
language: "string", | ||
endpoints: "any", | ||
}; | ||
function registerGenerateCommand(cli) { | ||
const command = cli | ||
cli | ||
// TODO: make generate a sub command, after https://github.com/cacjs/cac/pull/152 | ||
@@ -35,16 +50,30 @@ // .command("generate", "Generate Hypertune files for your project", { allowUnknownOptions: true }) | ||
.example((bin) => `${bin} --token U2FsdGVkX1abcdef`) | ||
.action((0, helpers_1.withOtherOptionSources)(exports.generate, options_1.generateOptionsSchema)); | ||
(0, helpers_1.registerDocs)(command, options_1.generateOptionsSchema); | ||
.action((0, helpers_1.withOtherOptionSources)(exports.generate, exports.generateOptionsSchema)) | ||
.option("--token [value]", "Project token") | ||
.option("--queryFilePath [value]", "File path to the GraphQL initialization query", { default: exports.defaultOptions.queryFilePath }) | ||
.option("--outputFilePath [value]", "Where to write the generated file", { | ||
default: exports.defaultOptions.outputFilePath, | ||
}) | ||
.option("--includeToken", "Include your token in the generated code", { | ||
default: exports.defaultOptions.includeToken, | ||
}) | ||
.option("--includeFallback", "Embed a snapshot of your configuration logic in the generated code so the SDK can reliably, locally initialize first, before fetching the latest logic from the server, and can work even if the server is unreachable", { | ||
default: exports.defaultOptions.includeFallback, | ||
}) | ||
.option("--language [value]", "Target language (ts or js).", { | ||
default: exports.defaultOptions.language, | ||
}); | ||
} | ||
exports.registerGenerateCommand = registerGenerateCommand; | ||
exports.generate = (0, helpers_1.withValidation)(options_1.generateOptionsSchema, (options) => __awaiter(void 0, void 0, void 0, function* () { | ||
exports.generate = (0, helpers_1.withValidation)(exports.generateOptionsSchema, (options) => __awaiter(void 0, void 0, void 0, function* () { | ||
var _a, _b, _c, _d, _e; | ||
console.log("Starting Hypertune code generation..."); | ||
try { | ||
yield generateCode({ | ||
token: options.token, | ||
queryCode: getQueryCode(options.queryFilePath), | ||
outputFilePath: options.outputFilePath, | ||
includeToken: options.includeToken, | ||
includeFallback: options.includeFallback, | ||
language: options.language, | ||
token: (0, helpers_1.throwIfOptionIsUndefined)("token", options.token), | ||
queryCode: getQueryCode((_a = options.queryFilePath) !== null && _a !== void 0 ? _a : exports.defaultOptions.queryFilePath), | ||
outputFilePath: (_b = options.outputFilePath) !== null && _b !== void 0 ? _b : exports.defaultOptions.outputFilePath, | ||
includeToken: (_c = options.includeToken) !== null && _c !== void 0 ? _c : exports.defaultOptions.includeToken, | ||
includeFallback: (_d = options.includeFallback) !== null && _d !== void 0 ? _d : exports.defaultOptions.includeFallback, | ||
language: (_e = options.language) !== null && _e !== void 0 ? _e : exports.defaultOptions.language, | ||
endpoints: (0, getEndpoints_1.default)(options.endpoints), | ||
@@ -51,0 +80,0 @@ }); |
@@ -1,9 +0,6 @@ | ||
import { Command } from "cac"; | ||
import { ZodTypeAny, z } from "zod"; | ||
export type Schema<Options extends object = object, OptionsInput extends object = object> = z.ZodObject<z.ZodRawShape, "strip", z.ZodTypeAny, Options, OptionsInput>; | ||
type ValueType = "string" | "boolean" | "number" | "any"; | ||
export type Schema = { | ||
[optionName: string]: ValueType; | ||
}; | ||
/** | ||
* Add docs to CAC command based on Zod Schema (the ones that appear when you run --help) | ||
*/ | ||
export declare function registerDocs(command: Command, optionsSchema: Schema): void; | ||
/** | ||
* Handler for a CLI command. Given `Options`, it asynchronously returns a `Result` | ||
@@ -15,3 +12,3 @@ */ | ||
*/ | ||
export type Wrapper = <Options extends object, OptionsInput extends object, Result>(handler: Handler<Options, Result>, schema?: Schema<Options, OptionsInput>) => Handler<Options, Result>; | ||
export type Wrapper = <Options extends object, Result>(handler: Handler<Options, Result>, schema?: Schema) => Handler<Options, Result>; | ||
/** | ||
@@ -30,4 +27,6 @@ * Processes default options behavior: | ||
*/ | ||
export declare function withValidation<Options extends object, OptionsInput extends object, HandlerResult>(schema: Schema<Options, OptionsInput>, handler: Handler<Options, HandlerResult>): Handler<OptionsInput, HandlerResult>; | ||
export declare function parseOptionValueWithSchema(schema: ZodTypeAny, value: string): number | boolean | string; | ||
export declare function withValidation<Options extends object, HandlerResult>(schema: Schema, handler: Handler<Options, HandlerResult>): Handler<Options, HandlerResult>; | ||
export declare function throwIfOptionIsUndefined<T>(optionName: string, value: T | undefined): T; | ||
export declare function parseOptionValueWithSchema(valueType: ValueType, value: string): number | boolean | string; | ||
export {}; | ||
//# sourceMappingURL=helpers.d.ts.map |
@@ -15,27 +15,6 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.parseOptionValueWithSchema = exports.withValidation = exports.withOtherOptionSources = exports.registerDocs = void 0; | ||
exports.parseOptionValueWithSchema = exports.throwIfOptionIsUndefined = exports.withValidation = exports.withOtherOptionSources = void 0; | ||
/* eslint-disable no-underscore-dangle */ | ||
const joycon_1 = __importDefault(require("joycon")); | ||
const zod_1 = require("zod"); | ||
/** | ||
* Add docs to CAC command based on Zod Schema (the ones that appear when you run --help) | ||
*/ | ||
function registerDocs(command, optionsSchema) { | ||
Object.entries(optionsSchema.shape).forEach(([name, optionSchema]) => { | ||
if (optionSchema.description && | ||
!optionSchema.description.startsWith("[hidden")) { | ||
const defaultValue = optionSchema instanceof zod_1.z.ZodDefault | ||
? optionSchema.parse(undefined) | ||
: undefined; | ||
const typeName = optionSchema instanceof zod_1.z.ZodDefault | ||
? optionSchema._def.innerType._def.typeName | ||
: optionSchema._def.typeName; | ||
const isBoolean = typeName === "ZodBoolean"; | ||
command.option(`${optionNameToCliFlag(name)}${isBoolean ? "" : " [value]"}`, optionSchema.description, { | ||
default: defaultValue, | ||
}); | ||
} | ||
}); | ||
} | ||
exports.registerDocs = registerDocs; | ||
/** | ||
* Processes default options behavior: | ||
@@ -106,4 +85,4 @@ * - removes `--` option as we never use it | ||
k, | ||
schema && schema.shape[k] | ||
? parseOptionValueWithSchema(schema.shape[k], v) | ||
schema && schema[k] | ||
? parseOptionValueWithSchema(schema[k], v) | ||
: v, | ||
@@ -130,33 +109,43 @@ ])); | ||
return (options) => { | ||
const parseResult = schema.safeParse(options); | ||
if (!parseResult.success) { | ||
throw new Error(`Invalid options:\n${parseResult.error.errors | ||
.map(formatZodIssue) | ||
.map((l) => ` ${l}`) | ||
.join("\n")}`); | ||
} | ||
const strictParseResult = schema.strict().safeParse(options); | ||
if (!strictParseResult.success) { | ||
console.warn(`Warning: Ignoring unrecognized option(s) ${strictParseResult.error.errors | ||
.map((error) => error.keys.join(".")) | ||
.join(" and ")}`); | ||
} | ||
return handler(parseResult.data); | ||
Object.entries(options).forEach(([option, value]) => { | ||
if (!(option in schema)) { | ||
console.warn(`Warning: Ignoring unrecognized option ${option}`); | ||
return; | ||
} | ||
switch (schema[option]) { | ||
case "any": | ||
return; | ||
case "string": | ||
if (typeof value !== "string") { | ||
throw new Error(`Option "${option}" must be a string, but ${typeof value} was provided`); | ||
} | ||
return; | ||
case "boolean": | ||
if (typeof value !== "boolean") { | ||
throw new Error(`Option "${option}" must be a boolean, but ${typeof value} was provided`); | ||
} | ||
return; | ||
case "number": | ||
if (typeof value !== "number") { | ||
throw new Error(`Option "${option}" must be a number, but ${typeof value} was provided`); | ||
} | ||
return; | ||
default: | ||
throw new Error(`Unexpected option type "${schema[option]}" for option "${option}"`); | ||
} | ||
}); | ||
return handler(options); | ||
}; | ||
} | ||
exports.withValidation = withValidation; | ||
/** Custom formatting for issues raised by Zod when validating options */ | ||
function formatZodIssue(issue) { | ||
if (issue.message === "Required") { | ||
const optionName = issue.path.join("."); | ||
return `${optionName}: Missing required argument. Set it in your hypertune config (such as hypertune.json) as ${optionName}, use the ${optionNameToCliFlag(optionName)} argument, or the ${optionNameToEnvName(optionName)} environment variable.`; | ||
function throwIfOptionIsUndefined(optionName, value) { | ||
if (value === undefined) { | ||
throw new Error(`${optionName}: Missing required argument. Set it in your hypertune config (such as hypertune.json) as ${optionName}, use the ${optionNameToCliFlag(optionName)} argument, or the ${optionNameToEnvName(optionName)} environment variable.`); | ||
} | ||
return `${String(issue.path)}: ${issue.message}`; | ||
return value; | ||
} | ||
function parseOptionValueWithSchema(schema, value) { | ||
const typeName = schema._def.innerType | ||
? schema._def.innerType._def.typeName | ||
: schema._def.typeName; | ||
switch (typeName) { | ||
case zod_1.ZodFirstPartyTypeKind.ZodBoolean: { | ||
exports.throwIfOptionIsUndefined = throwIfOptionIsUndefined; | ||
function parseOptionValueWithSchema(valueType, value) { | ||
switch (valueType) { | ||
case "boolean": { | ||
switch (value.toLowerCase().trim()) { | ||
@@ -175,3 +164,3 @@ case "1": | ||
} | ||
case zod_1.ZodFirstPartyTypeKind.ZodNumber: { | ||
case "number": { | ||
if (/^-?\d+\.?\d*$/.test(value)) { | ||
@@ -178,0 +167,0 @@ return Number(value); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const vitest_1 = require("vitest"); | ||
const zod_1 = require("zod"); | ||
const helpers_1 = require("./helpers"); | ||
const testSchema = zod_1.z.object({ | ||
stringNoDefault: zod_1.z.string(), | ||
stringWithDefault: zod_1.z.string().default("default"), | ||
booleanNoDefault: zod_1.z.boolean(), | ||
booleanWithDefault: zod_1.z.boolean().default(false), | ||
unionNoDefault: zod_1.z.union([zod_1.z.literal("val1"), zod_1.z.literal("val2")]), | ||
unionWithDefault: zod_1.z | ||
.union([zod_1.z.literal("val1"), zod_1.z.literal("val2")]) | ||
.default("val1"), | ||
}); | ||
const testSchema = { | ||
stringValue: "string", | ||
booleanValue: "boolean", | ||
numberValue: "number", | ||
}; | ||
(0, vitest_1.test)("parseOptionValueWithSchema doesn't throw an exception", () => { | ||
Object.entries(testSchema.shape).forEach(([, optionSchema]) => { | ||
(0, helpers_1.parseOptionValueWithSchema)(optionSchema, "1"); | ||
Object.entries(testSchema).forEach(([, valueType]) => { | ||
(0, helpers_1.parseOptionValueWithSchema)(valueType, "1"); | ||
}); | ||
}); | ||
//# sourceMappingURL=helpers.test.js.map |
@@ -6,5 +6,5 @@ import "regenerator-runtime/runtime"; | ||
export type { InitResponseBody, ObjectValueWithVariables, Query, CustomLogger, VercelEdgeConfigClient, LoggingMode, InitializeOptions, InternalInitializeOptions, DehydratedState, }; | ||
export { LogLevel } from "./generated/graphql"; | ||
export { LogLevel } from "./shared/types"; | ||
export { initialize, Node }; | ||
export { BooleanNode, FloatNode, IntNode, StringNode, VoidNode, } from "./lib/primitiveNodes"; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -12,4 +12,4 @@ "use strict"; | ||
exports.Node = Node_1.default; | ||
var graphql_1 = require("./generated/graphql"); | ||
Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return graphql_1.LogLevel; } }); | ||
var types_1 = require("./shared/types"); | ||
Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return types_1.LogLevel; } }); | ||
var primitiveNodes_1 = require("./lib/primitiveNodes"); | ||
@@ -16,0 +16,0 @@ Object.defineProperty(exports, "BooleanNode", { enumerable: true, get: function () { return primitiveNodes_1.BooleanNode; } }); |
@@ -22,3 +22,2 @@ "use strict"; | ||
const LRUCache_1 = __importDefault(require("../shared/helpers/LRUCache")); | ||
const graphql_1 = require("../generated/graphql"); | ||
const getMetadata_1 = __importDefault(require("../shared/helpers/getMetadata")); | ||
@@ -63,3 +62,3 @@ /** @internal: Not part of the Hypertune public API */ | ||
else { | ||
this.log(graphql_1.LogLevel.Info, "Not initializing from server."); | ||
this.log(shared_1.LogLevel.Info, "Not initializing from server."); | ||
} | ||
@@ -70,3 +69,3 @@ if (shouldStartIntervals) { | ||
else { | ||
this.log(graphql_1.LogLevel.Info, "Not starting intervals."); | ||
this.log(shared_1.LogLevel.Info, "Not starting intervals."); | ||
} | ||
@@ -86,6 +85,6 @@ if (environment_1.isBrowser) { | ||
if (!initData) { | ||
this.log(graphql_1.LogLevel.Info, `Not initializing from ${initSourceName} as it is null.`); | ||
this.log(shared_1.LogLevel.Info, `Not initializing from ${initSourceName} as it is null.`); | ||
return; | ||
} | ||
this.log(graphql_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
this.log(shared_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
this.init("local", initData); | ||
@@ -98,3 +97,3 @@ } | ||
if (newInitData.commitId <= ((_b = (_a = this.initData) === null || _a === void 0 ? void 0 : _a.commitId) !== null && _b !== void 0 ? _b : -1)) { | ||
this.log(graphql_1.LogLevel.Info, `Skipped initialization from ${initSourceName} as commit with id "${newInitData.commitId}" isn't newer.`); | ||
this.log(shared_1.LogLevel.Info, `Skipped initialization from ${initSourceName} as commit with id "${newInitData.commitId}" isn't newer.`); | ||
return; | ||
@@ -114,3 +113,3 @@ } | ||
this.logger.setCommitId(newInitData.commitId.toString()); | ||
this.log(graphql_1.LogLevel.Info, `Initialized successfully from ${initSourceName}.`); | ||
this.log(shared_1.LogLevel.Info, `Initialized successfully from ${initSourceName}.`); | ||
(_c = this.getFieldCache) === null || _c === void 0 ? void 0 : _c.purge(); | ||
@@ -122,3 +121,3 @@ (_d = this.getItemsCache) === null || _d === void 0 ? void 0 : _d.purge(); | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `Error initializing from ${initSourceName}.`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Error initializing from ${initSourceName}.`, (0, getMetadata_1.default)(error)); | ||
} | ||
@@ -131,3 +130,3 @@ } | ||
if (!shouldGetUpdatesFromServer) { | ||
this.log(graphql_1.LogLevel.Debug, `Not initializing from server as have already and not getting updates.`); | ||
this.log(shared_1.LogLevel.Debug, `Not initializing from server as have already and not getting updates.`); | ||
return Promise.resolve(); | ||
@@ -138,3 +137,3 @@ } | ||
if (msSinceLastServerInit < updateIntervalMs) { | ||
this.log(graphql_1.LogLevel.Debug, `Not initializing from server as already did ${msSinceLastServerInit}ms ago which is less than the update interval of ${updateIntervalMs}ms.`); | ||
this.log(shared_1.LogLevel.Debug, `Not initializing from server as already did ${msSinceLastServerInit}ms ago which is less than the update interval of ${updateIntervalMs}ms.`); | ||
return Promise.resolve(); | ||
@@ -172,10 +171,10 @@ } | ||
if (this.initData.commitHash === latestCommitHash) { | ||
this.log(graphql_1.LogLevel.Debug, "Commit hash is already latest."); | ||
this.log(shared_1.LogLevel.Debug, "Commit hash is already latest."); | ||
return; | ||
} | ||
this.log(graphql_1.LogLevel.Info, `Commit hash (${this.initData.commitHash}) is not latest (${latestCommitHash}).`); | ||
this.log(shared_1.LogLevel.Info, `Commit hash (${this.initData.commitHash}) is not latest (${latestCommitHash}).`); | ||
} | ||
this.log(graphql_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
this.log(shared_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
const newInitData = yield (0, p_retry_1.default)((attemptNumber) => { | ||
this.log(graphql_1.LogLevel.Debug, `Attempt ${attemptNumber} to initialize from ${initSourceName}...`); | ||
this.log(shared_1.LogLevel.Debug, `Attempt ${attemptNumber} to initialize from ${initSourceName}...`); | ||
return (0, edge_1.initRequest)(this.token, this.queryCode, this.variableValues, this.schemaVersion, this.endpoints, this.fetchFunction); | ||
@@ -186,3 +185,3 @@ }, { | ||
onFailedAttempt: (error) => { | ||
this.log(graphql_1.LogLevel.Error, `Attempt ${error.attemptNumber} to initialize from ${initSourceName} failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Attempt ${error.attemptNumber} to initialize from ${initSourceName} failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
if (this.shouldClose) { | ||
@@ -196,3 +195,3 @@ throw new p_retry_1.default.AbortError(`Stopped trying to initialize from ${initSourceName}.`); | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `All attempts to initialize from ${initSourceName} failed.`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `All attempts to initialize from ${initSourceName} failed.`, (0, getMetadata_1.default)(error)); | ||
} | ||
@@ -203,5 +202,5 @@ }); | ||
return __awaiter(this, void 0, void 0, function* () { | ||
this.log(graphql_1.LogLevel.Debug, "Getting latest commit hash..."); | ||
this.log(shared_1.LogLevel.Debug, "Getting latest commit hash..."); | ||
const hashResponse = yield (0, p_retry_1.default)((attemptNumber) => { | ||
this.log(graphql_1.LogLevel.Debug, `Attempt ${attemptNumber} to get latest commit hash...`); | ||
this.log(shared_1.LogLevel.Debug, `Attempt ${attemptNumber} to get latest commit hash...`); | ||
return (0, edge_1.hashRequest)(this.token, this.queryCode, this.variableValues, this.schemaVersion, this.endpoints, this.fetchFunction); | ||
@@ -212,3 +211,3 @@ }, { | ||
onFailedAttempt: (error) => { | ||
this.log(graphql_1.LogLevel.Error, `Attempt ${error.attemptNumber} to get latest commit hash failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Attempt ${error.attemptNumber} to get latest commit hash failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
if (this.shouldClose) { | ||
@@ -227,3 +226,3 @@ throw new p_retry_1.default.AbortError(`Stopped trying to get latest commit hash.`); | ||
if (!vercelEdgeConfigClient || !vercelEdgeConfigItemKey) { | ||
this.log(graphql_1.LogLevel.Info, `Cannot initialize from ${initSourceName} as no client or no item key.`); | ||
this.log(shared_1.LogLevel.Info, `Cannot initialize from ${initSourceName} as no client or no item key.`); | ||
return; | ||
@@ -233,3 +232,3 @@ } | ||
const newInitData = yield (0, p_retry_1.default)((attemptNumber) => { | ||
this.log(graphql_1.LogLevel.Debug, `Attempt ${attemptNumber} to initialize from ${initSourceName}...`); | ||
this.log(shared_1.LogLevel.Debug, `Attempt ${attemptNumber} to initialize from ${initSourceName}...`); | ||
return (0, vercel_1.initFromEdgeConfig)(vercelEdgeConfigClient, vercelEdgeConfigItemKey); | ||
@@ -240,3 +239,3 @@ }, { | ||
onFailedAttempt: (error) => { | ||
this.log(graphql_1.LogLevel.Error, `Attempt ${error.attemptNumber} to initialize from ${initSourceName} failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Attempt ${error.attemptNumber} to initialize from ${initSourceName} failed. There are ${error.retriesLeft} retries left.`, (0, getMetadata_1.default)(error)); | ||
if (this.shouldClose) { | ||
@@ -248,10 +247,10 @@ throw new p_retry_1.default.AbortError(`Stopped trying to initialize from ${initSourceName}.`); | ||
if (this.initData && this.initData.commitId === newInitData.commitId) { | ||
this.log(graphql_1.LogLevel.Debug, `Already have latest commit from ${initSourceName}.`); | ||
this.log(shared_1.LogLevel.Debug, `Already have latest commit from ${initSourceName}.`); | ||
return; | ||
} | ||
this.log(graphql_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
this.log(shared_1.LogLevel.Info, `Initializing from ${initSourceName}...`); | ||
this.init("vercelEdgeConfig", newInitData); | ||
} | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `All attempts to initialize from ${initSourceName} failed. Error:`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `All attempts to initialize from ${initSourceName} failed. Error:`, (0, getMetadata_1.default)(error)); | ||
} | ||
@@ -266,3 +265,3 @@ }); | ||
if (this.shouldClose) { | ||
this.log(graphql_1.LogLevel.Debug, "Stopped getting updates from the server."); | ||
this.log(shared_1.LogLevel.Debug, "Stopped getting updates from the server."); | ||
return; | ||
@@ -281,3 +280,3 @@ } | ||
if (this.shouldClose) { | ||
this.log(graphql_1.LogLevel.Debug, "Stopped flushing log queue."); | ||
this.log(shared_1.LogLevel.Debug, "Stopped flushing log queue."); | ||
return; | ||
@@ -319,7 +318,7 @@ } | ||
if ((0, shared_1.stableStringify)(override) === (0, shared_1.stableStringify)(this.override)) { | ||
this.log(graphql_1.LogLevel.Info, "Skipped setting override as it's equal to the one already set."); | ||
this.log(shared_1.LogLevel.Info, "Skipped setting override as it's equal to the one already set."); | ||
return; | ||
} | ||
this.override = override; | ||
this.log(graphql_1.LogLevel.Info, "Set override.", { override }); | ||
this.log(shared_1.LogLevel.Info, "Set override.", { override }); | ||
this.notifyUpdateListeners(); | ||
@@ -334,3 +333,3 @@ } | ||
hydrate(dehydratedState) { | ||
this.log(graphql_1.LogLevel.Info, "Hydrating..."); | ||
this.log(shared_1.LogLevel.Info, "Hydrating..."); | ||
this.initFromData(dehydratedState.initData); | ||
@@ -337,0 +336,0 @@ this.setOverride(dehydratedState.override); |
@@ -1,3 +0,2 @@ | ||
import { ReductionLogs, Expression, CustomLogger, Endpoints, Fetch } from "../shared"; | ||
import { LogLevel } from "../generated/graphql"; | ||
import { ReductionLogs, Expression, CustomLogger, Endpoints, Fetch, LogLevel } from "../shared"; | ||
import { LoggingMode } from ".."; | ||
@@ -4,0 +3,0 @@ export default class Logger { |
@@ -16,3 +16,2 @@ "use strict"; | ||
const shared_1 = require("../shared"); | ||
const graphql_1 = require("../generated/graphql"); | ||
const BackendLogger_1 = __importDefault(require("../shared/helpers/BackendLogger")); | ||
@@ -34,3 +33,3 @@ const toConsoleFunction_1 = __importDefault(require("../shared/helpers/toConsoleFunction")); | ||
commitId: null, | ||
createLogs: getCreateLogsFunction(`${endpoints.logs}${shared_1.backendGraphqlEndpoint}`, fetchFunction), | ||
createLogs: getCreateLogsFunction(`${endpoints.logs}${shared_1.logsCreationEndpoint}`, fetchFunction), | ||
localLog: this.localLog.bind(this), | ||
@@ -47,5 +46,5 @@ }); | ||
this.localLog(level, logMessage, logMetadata); | ||
if ((level === graphql_1.LogLevel.Warn || level === graphql_1.LogLevel.Error) && | ||
if ((level === shared_1.LogLevel.Warn || level === shared_1.LogLevel.Error) && | ||
this.shouldNodeLogToBackend(commitHash, nodePath, message)) { | ||
this.backendLogger.log(graphql_1.LogType.SdkNode, level, logMessage, logMetadata); | ||
this.backendLogger.log(shared_1.LogType.SdkNode, level, logMessage, logMetadata); | ||
} | ||
@@ -70,3 +69,3 @@ if (reductionLogs) { | ||
if (this.backendLogCache.get(cacheKey)) { | ||
this.localLog(graphql_1.LogLevel.Debug, `Backend log cache hit.`, | ||
this.localLog(shared_1.LogLevel.Debug, `Backend log cache hit.`, | ||
/* metadata */ { commitHash, nodePath, cacheKeySuffix }); | ||
@@ -89,4 +88,4 @@ return false; | ||
this.localLog(level, message, metadata); | ||
if (level === graphql_1.LogLevel.Warn || level === graphql_1.LogLevel.Error) { | ||
this.backendLogger.log(graphql_1.LogType.SdkMessage, level, message, metadata); | ||
if (level === shared_1.LogLevel.Warn || level === shared_1.LogLevel.Error) { | ||
this.backendLogger.log(shared_1.LogType.SdkMessage, level, message, metadata); | ||
} | ||
@@ -100,3 +99,3 @@ } | ||
} | ||
if (level === graphql_1.LogLevel.Debug) { | ||
if (level === shared_1.LogLevel.Debug) { | ||
return; | ||
@@ -114,12 +113,7 @@ } | ||
exports.default = Logger; | ||
function getCreateLogsFunction(graphqlUrl, fetchFunction) { | ||
function getCreateLogsFunction(logsUrl, fetchFunction) { | ||
return (input) => __awaiter(this, void 0, void 0, function* () { | ||
const body = { | ||
query: "mutation CreateLogsMutation($input: CreateLogsInput!) {\n createLogs(input: $input)\n}", | ||
variables: { input }, | ||
operationName: "CreateLogsMutation", | ||
}; | ||
const bodyJson = JSON.stringify(body); | ||
const bodyJson = JSON.stringify(input); | ||
const bodyBlob = new Blob([bodyJson]); | ||
const response = yield fetchFunction(graphqlUrl, { | ||
const response = yield fetchFunction(logsUrl, { | ||
method: "POST", | ||
@@ -126,0 +120,0 @@ headers: { |
@@ -7,6 +7,5 @@ "use strict"; | ||
/* eslint-disable no-underscore-dangle */ | ||
const graphql_1 = require("../generated/graphql"); | ||
const shared_1 = require("../shared"); | ||
const getDeepestZodIssue_1 = __importDefault(require("../shared/helpers/getDeepestZodIssue")); | ||
const getMetadata_1 = __importDefault(require("../shared/helpers/getMetadata")); | ||
const throwIfObjectValueIsInvalid_1 = __importDefault(require("../shared/helpers/throwIfObjectValueIsInvalid")); | ||
const getLocalLogArguments_1 = __importDefault(require("./getLocalLogArguments")); | ||
@@ -88,11 +87,7 @@ const getNodeCacheKey_1 = __importDefault(require("./getNodeCacheKey")); | ||
try { | ||
const parseResult = shared_1.objectValueSchema.safeParse(fieldArguments); | ||
if (!parseResult.success) { | ||
const issue = (0, getDeepestZodIssue_1.default)(parseResult.error); | ||
throw new Error(`Invalid field arguments: "${issue.message}" at "${issue.path.join(".")}"`); | ||
} | ||
(0, shared_1.prefixError)(() => (0, throwIfObjectValueIsInvalid_1.default)(fieldArguments), "Invalid field arguments: "); | ||
this.updateIfNeeded(); | ||
const { expression } = this.props; | ||
if (!expression) { | ||
this.log(graphql_1.LogLevel.Debug, `Using fallback for field "${fieldName}" as expression is null. This is expected before initialization.`); | ||
this.log(shared_1.LogLevel.Debug, `Using fallback for field "${fieldName}" as expression is null. This is expected before initialization.`); | ||
return null; | ||
@@ -119,3 +114,3 @@ } | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `Error getting field "${fieldName}" with arguments ${JSON.stringify(fieldArguments)}: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Error getting field "${fieldName}" with arguments ${JSON.stringify(fieldArguments)}: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
return null; | ||
@@ -150,3 +145,3 @@ } | ||
if (!expression) { | ||
this.log(graphql_1.LogLevel.Debug, "Using fallback for array items as expression is null. This is expected before initialization."); | ||
this.log(shared_1.LogLevel.Debug, "Using fallback for array items as expression is null. This is expected before initialization."); | ||
return null; | ||
@@ -166,3 +161,3 @@ } | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `Error getting items: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Error getting items: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
return null; | ||
@@ -181,3 +176,3 @@ } | ||
const { value, logs: reductionLogs } = valueAndLogs; | ||
this.log(graphql_1.LogLevel.Debug, `Evaluated to ${JSON.stringify(value)}`, | ||
this.log(shared_1.LogLevel.Debug, `Evaluated to ${JSON.stringify(value)}`, | ||
/* metadata */ { query }, reductionLogs); | ||
@@ -239,3 +234,3 @@ return value; | ||
if (!expression) { | ||
this.log(graphql_1.LogLevel.Debug, `Using fallback while evaluating as expression is null. This is expected before initialization.`); | ||
this.log(shared_1.LogLevel.Debug, `Using fallback while evaluating as expression is null. This is expected before initialization.`); | ||
return null; | ||
@@ -248,3 +243,3 @@ } | ||
catch (error) { | ||
this.log(graphql_1.LogLevel.Error, `Error getting value and logs: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
this.log(shared_1.LogLevel.Error, `Error getting value and logs: ${(0, shared_1.asError)(error).message}`, (0, getMetadata_1.default)(error)); | ||
return null; | ||
@@ -264,9 +259,9 @@ } | ||
if (!this.props.expression) { | ||
this.log(graphql_1.LogLevel.Debug, `Unexpected expression type as expression is null but this is expected before initialization.`); | ||
this.log(shared_1.LogLevel.Debug, `Unexpected expression type as expression is null but this is expected before initialization.`); | ||
return; | ||
} | ||
this.log(graphql_1.LogLevel.Error, "Unexpected expression type."); | ||
this.log(shared_1.LogLevel.Error, "Unexpected expression type."); | ||
} | ||
logUnexpectedValueError(value) { | ||
this.log(graphql_1.LogLevel.Error, `Evaluated to unexpected value: ${JSON.stringify(value)}`); | ||
this.log(shared_1.LogLevel.Error, `Evaluated to unexpected value: ${JSON.stringify(value)}`); | ||
} | ||
@@ -296,3 +291,3 @@ log(level, message, metadata = {}, reductionLogs = null) { | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot get state hash."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot get state hash."); | ||
return null; | ||
@@ -309,3 +304,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot get commit hash."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot get commit hash."); | ||
return null; | ||
@@ -318,3 +313,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot add update listener."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot add update listener."); | ||
return; | ||
@@ -327,3 +322,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot remove update listener."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot remove update listener."); | ||
return; | ||
@@ -339,3 +334,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot initialize from the server."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot initialize from the server."); | ||
return Promise.resolve(); | ||
@@ -357,3 +352,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot check if we've initialized from the server."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot check if we've initialized from the server."); | ||
return false; | ||
@@ -366,3 +361,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot flush logs."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot flush logs."); | ||
return Promise.resolve(); | ||
@@ -375,3 +370,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot set override."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot set override."); | ||
return; | ||
@@ -384,3 +379,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot dehydrate."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot dehydrate."); | ||
return null; | ||
@@ -393,3 +388,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot hydrate."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot hydrate."); | ||
return; | ||
@@ -405,3 +400,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot get init data."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot get init data."); | ||
return null; | ||
@@ -417,3 +412,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot initialize from data."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot initialize from data."); | ||
return; | ||
@@ -426,3 +421,3 @@ } | ||
if (!context) { | ||
this.log(graphql_1.LogLevel.Error, "No context so cannot close."); | ||
this.log(shared_1.LogLevel.Error, "No context so cannot close."); | ||
return; | ||
@@ -429,0 +424,0 @@ } |
export declare const localBackendBaseUrl = "http://localhost:3001"; | ||
export declare const localEdgeWorkerBaseUrl = "http://localhost:3002"; | ||
export declare const localLogsBaseUrl = "http://localhost:3001"; | ||
export declare const localLogsBaseUrl = "http://localhost:3003"; | ||
export declare const stagingBackendBaseUrl = "https://backend.staging.hypertune.com"; | ||
@@ -13,2 +13,3 @@ export declare const stagingEdgeCdnBaseUrl = "https://hypertune-edge-staging.b-cdn.net"; | ||
export declare const backendGraphqlEndpoint = "/graphql"; | ||
export declare const logsCreationEndpoint = "/logs"; | ||
export declare const configFileName = "hypertune.json"; | ||
@@ -15,0 +16,0 @@ export declare const tokenEnvironmentVariableName = "HYPERTUNE_TOKEN"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.breakingSchemaChangesError = exports.defaultCacheSize = exports.defaultArmKey = exports.numHashBuckets = exports.fieldPathSeparator = exports.isQueryVariableKey = exports.isPartialObjectKey = exports.graphqlTypeNameKey = exports.tokenEnvironmentVariableName = exports.configFileName = exports.backendGraphqlEndpoint = exports.prodLogsBaseUrl = exports.prodEdgeWorkerBaseUrl = exports.prodEdgeCdnBaseUrl = exports.prodBackendBaseUrl = exports.stagingLogsBaseUrl = exports.stagingEdgeWorkerBaseUrl = exports.stagingEdgeCdnBaseUrl = exports.stagingBackendBaseUrl = exports.localLogsBaseUrl = exports.localEdgeWorkerBaseUrl = exports.localBackendBaseUrl = void 0; | ||
exports.breakingSchemaChangesError = exports.defaultCacheSize = exports.defaultArmKey = exports.numHashBuckets = exports.fieldPathSeparator = exports.isQueryVariableKey = exports.isPartialObjectKey = exports.graphqlTypeNameKey = exports.tokenEnvironmentVariableName = exports.configFileName = exports.logsCreationEndpoint = exports.backendGraphqlEndpoint = exports.prodLogsBaseUrl = exports.prodEdgeWorkerBaseUrl = exports.prodEdgeCdnBaseUrl = exports.prodBackendBaseUrl = exports.stagingLogsBaseUrl = exports.stagingEdgeWorkerBaseUrl = exports.stagingEdgeCdnBaseUrl = exports.stagingBackendBaseUrl = exports.localLogsBaseUrl = exports.localEdgeWorkerBaseUrl = exports.localBackendBaseUrl = void 0; | ||
exports.localBackendBaseUrl = "http://localhost:3001"; | ||
exports.localEdgeWorkerBaseUrl = "http://localhost:3002"; | ||
exports.localLogsBaseUrl = "http://localhost:3001"; | ||
exports.localLogsBaseUrl = "http://localhost:3003"; | ||
exports.stagingBackendBaseUrl = "https://backend.staging.hypertune.com"; | ||
@@ -16,2 +16,3 @@ exports.stagingEdgeCdnBaseUrl = "https://hypertune-edge-staging.b-cdn.net"; | ||
exports.backendGraphqlEndpoint = "/graphql"; | ||
exports.logsCreationEndpoint = "/logs"; | ||
exports.configFileName = "hypertune.json"; | ||
@@ -18,0 +19,0 @@ exports.tokenEnvironmentVariableName = "HYPERTUNE_TOKEN"; |
@@ -1,2 +0,2 @@ | ||
import { CreateLogsInput, LogType, LogLevel } from "../../generated/graphql"; | ||
import { CreateLogsInput, LogType, LogLevel } from ".."; | ||
import { CountMap } from "../types"; | ||
@@ -3,0 +3,0 @@ export default class BackendLogger { |
@@ -16,3 +16,3 @@ "use strict"; | ||
const p_retry_1 = __importDefault(require("p-retry")); | ||
const graphql_1 = require("../../generated/graphql"); | ||
const __1 = require(".."); | ||
const nullThrows_1 = __importDefault(require("./nullThrows")); | ||
@@ -42,3 +42,3 @@ const toDimensionTypeEnum_1 = __importDefault(require("./toDimensionTypeEnum")); | ||
if (queue.logs.length > 1000) { | ||
this.localLog(graphql_1.LogLevel.Debug, "Ignoring log, as more than 1000 in the queue already.", | ||
this.localLog(__1.LogLevel.Debug, "Ignoring log, as more than 1000 in the queue already.", | ||
/* metadata */ {}); | ||
@@ -133,3 +133,3 @@ return; | ||
yield (0, p_retry_1.default)((attemptNumber) => { | ||
localLog(graphql_1.LogLevel.Debug, `Attempt ${attemptNumber} to flush logs to backend...`, | ||
localLog(__1.LogLevel.Debug, `Attempt ${attemptNumber} to flush logs to backend...`, | ||
/* metadata */ { createLogsInput }); | ||
@@ -139,10 +139,10 @@ return createLogs(createLogsInput); | ||
onFailedAttempt: (error) => { | ||
localLog(graphql_1.LogLevel.Error, `Attempt ${error.attemptNumber} to flush logs failed. There are ${error.retriesLeft} retries left.`, Object.assign(Object.assign({}, (0, getMetadata_1.default)(error)), { createLogsInput })); | ||
localLog(__1.LogLevel.Error, `Attempt ${error.attemptNumber} to flush logs failed. There are ${error.retriesLeft} retries left.`, Object.assign(Object.assign({}, (0, getMetadata_1.default)(error)), { createLogsInput })); | ||
}, | ||
}); | ||
localLog(graphql_1.LogLevel.Debug, "Successfully flushed logs to backend.", | ||
localLog(__1.LogLevel.Debug, "Successfully flushed logs to backend.", | ||
/* metadata */ { createLogsInput }); | ||
} | ||
catch (error) { | ||
localLog(graphql_1.LogLevel.Error, "All attempts to flush logs failed. These logs will be lost.", Object.assign(Object.assign({}, (0, getMetadata_1.default)(error)), { createLogsInput })); | ||
localLog(__1.LogLevel.Error, "All attempts to flush logs failed. These logs will be lost.", Object.assign(Object.assign({}, (0, getMetadata_1.default)(error)), { createLogsInput })); | ||
} | ||
@@ -149,0 +149,0 @@ }); |
@@ -1,3 +0,3 @@ | ||
import { LogLevel } from "../../generated/graphql"; | ||
import { LogLevel } from ".."; | ||
export default function toConsoleFunction(logLevel: LogLevel): typeof console.log; | ||
//# sourceMappingURL=toConsoleFunction.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* eslint-disable no-console */ | ||
const graphql_1 = require("../../generated/graphql"); | ||
const __1 = require(".."); | ||
function toConsoleFunction(logLevel) { | ||
switch (logLevel) { | ||
case graphql_1.LogLevel.Error: | ||
case __1.LogLevel.Error: | ||
return console.error; | ||
case graphql_1.LogLevel.Warn: | ||
case __1.LogLevel.Warn: | ||
return console.warn; | ||
case graphql_1.LogLevel.Info: | ||
case __1.LogLevel.Info: | ||
return console.info; | ||
case graphql_1.LogLevel.Debug: | ||
case __1.LogLevel.Debug: | ||
return console.debug; | ||
@@ -15,0 +15,0 @@ default: { |
@@ -1,4 +0,3 @@ | ||
import { DimensionType } from "../../generated/graphql"; | ||
import { ContinuousDimensionType, DiscreteDimensionType } from "../types"; | ||
import { DimensionType, ContinuousDimensionType, DiscreteDimensionType } from "../types"; | ||
export default function toDimensionTypeEnum(type: typeof DiscreteDimensionType | typeof ContinuousDimensionType): DimensionType; | ||
//# sourceMappingURL=toDimensionTypeEnum.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const graphql_1 = require("../../generated/graphql"); | ||
const types_1 = require("../types"); | ||
function toDimensionTypeEnum(type) { | ||
switch (type) { | ||
case "discrete": | ||
return graphql_1.DimensionType.Discrete; | ||
return types_1.DimensionType.Discrete; | ||
case "continuous": | ||
return graphql_1.DimensionType.Continuous; | ||
return types_1.DimensionType.Continuous; | ||
default: { | ||
@@ -11,0 +11,0 @@ const neverType = type; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/** Replaced by the value in package.json on build */ | ||
exports.default = "1.12.0"; | ||
exports.default = "1.13.0"; | ||
//# sourceMappingURL=sdkVersion.js.map |
@@ -1,5 +0,3 @@ | ||
import { z } from "zod"; | ||
import { fetch } from "cross-fetch"; | ||
import { isQueryVariableKey } from "./constants"; | ||
import { LogLevel } from "../generated/graphql"; | ||
/** | ||
@@ -330,5 +328,4 @@ * It's difficult to remove fields from server-returned types (Expression, | ||
export type SplitType = "test" | "ml"; | ||
export type Split = { | ||
export type SplitBase = { | ||
id: string; | ||
type: SplitType; | ||
name: string; | ||
@@ -339,7 +336,15 @@ dimensions: { | ||
eventObjectTypeName: string | null; | ||
}; | ||
export type TestSplit = SplitBase & { | ||
type: "test"; | ||
}; | ||
export type MLSplit = SplitBase & { | ||
type: "ml"; | ||
rewardEvents: { | ||
eventObjectTypeName: string; | ||
unitIdPayloadPath: string[]; | ||
}[]; | ||
rewardSql: string; | ||
featureIds: { | ||
[featureId: string]: true; | ||
}; | ||
}; | ||
export type Split = TestSplit | MLSplit; | ||
export type SplitMap = { | ||
@@ -447,4 +452,2 @@ [splitId: string]: Split; | ||
}; | ||
export declare const valueSchema: z.ZodSchema<Value>; | ||
export declare const objectValueSchema: z.ZodSchema<ObjectValue>; | ||
export type QueryVariable = { | ||
@@ -477,87 +480,30 @@ [isQueryVariableKey]: true; | ||
export type RequestType = (typeof requestTypes)[number]; | ||
export declare const sdkType: z.ZodEnum<["js", "python", "rust"]>; | ||
export type SdkType = z.infer<typeof sdkType>; | ||
export declare const language: z.ZodEnum<["ts", "js", "python", "rust"]>; | ||
export type Language = z.infer<typeof language>; | ||
export declare const codegenRequestBody: z.ZodObject<{ | ||
query: z.ZodNullable<z.ZodString>; | ||
schemaVersion: z.ZodOptional<z.ZodString>; | ||
includeToken: z.ZodOptional<z.ZodBoolean>; | ||
includeFallback: z.ZodOptional<z.ZodBoolean>; | ||
sdkType: z.ZodEnum<["js", "python", "rust"]>; | ||
sdkVersion: z.ZodString; | ||
language: z.ZodEnum<["ts", "js", "python", "rust"]>; | ||
buildTimeFallback: z.ZodOptional<z.ZodBoolean>; | ||
}, "strip", z.ZodTypeAny, { | ||
export type SdkType = "js" | "python" | "rust"; | ||
export type Language = "ts" | "js" | "python" | "rust"; | ||
export type CodegenRequestBody = { | ||
query: string | null; | ||
sdkType: "js" | "python" | "rust"; | ||
schemaVersion?: string; | ||
includeToken?: boolean; | ||
includeFallback?: boolean; | ||
sdkType: SdkType; | ||
sdkVersion: string; | ||
language: "js" | "python" | "rust" | "ts"; | ||
schemaVersion?: string | undefined; | ||
includeToken?: boolean | undefined; | ||
includeFallback?: boolean | undefined; | ||
buildTimeFallback?: boolean | undefined; | ||
}, { | ||
query: string | null; | ||
sdkType: "js" | "python" | "rust"; | ||
sdkVersion: string; | ||
language: "js" | "python" | "rust" | "ts"; | ||
schemaVersion?: string | undefined; | ||
includeToken?: boolean | undefined; | ||
includeFallback?: boolean | undefined; | ||
buildTimeFallback?: boolean | undefined; | ||
}>; | ||
export type CodegenRequestBody = z.infer<typeof codegenRequestBody>; | ||
export declare const initRequestBody: z.ZodObject<{ | ||
query: z.ZodString; | ||
variables: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>; | ||
schemaVersion: z.ZodOptional<z.ZodString>; | ||
sdkType: z.ZodEnum<["js", "python", "rust"]>; | ||
sdkVersion: z.ZodString; | ||
}, "strip", z.ZodTypeAny, { | ||
language: Language; | ||
}; | ||
export type InitRequestBody = { | ||
query: string; | ||
sdkType: "js" | "python" | "rust"; | ||
variables: ObjectValue; | ||
schemaVersion?: string; | ||
sdkType: SdkType; | ||
sdkVersion: string; | ||
variables: {} & { | ||
[k: string]: unknown; | ||
}; | ||
schemaVersion?: string | undefined; | ||
}, { | ||
}; | ||
export type GraphqlRequestBody = { | ||
query: string; | ||
sdkType: "js" | "python" | "rust"; | ||
sdkVersion: string; | ||
variables: {} & { | ||
[k: string]: unknown; | ||
}; | ||
schemaVersion?: string | undefined; | ||
}>; | ||
export type InitRequestBody = z.infer<typeof initRequestBody>; | ||
export declare const graphqlRequestBody: z.ZodObject<{ | ||
query: z.ZodString; | ||
variables: z.ZodOptional<z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>>; | ||
schemaVersion: z.ZodOptional<z.ZodString>; | ||
}, "strip", z.ZodTypeAny, { | ||
query: string; | ||
variables?: z.objectOutputType<{}, z.ZodTypeAny, "passthrough"> | undefined; | ||
schemaVersion?: string | undefined; | ||
}, { | ||
query: string; | ||
variables?: z.objectInputType<{}, z.ZodTypeAny, "passthrough"> | undefined; | ||
schemaVersion?: string | undefined; | ||
}>; | ||
export type GraphqlRequestBody = z.infer<typeof graphqlRequestBody>; | ||
export declare const schemaRequestBody: z.ZodObject<{ | ||
schemaVersion: z.ZodOptional<z.ZodString>; | ||
optionalInputTypes: z.ZodOptional<z.ZodBoolean>; | ||
introspection: z.ZodOptional<z.ZodBoolean>; | ||
}, "strip", z.ZodTypeAny, { | ||
schemaVersion?: string | undefined; | ||
optionalInputTypes?: boolean | undefined; | ||
introspection?: boolean | undefined; | ||
}, { | ||
schemaVersion?: string | undefined; | ||
optionalInputTypes?: boolean | undefined; | ||
introspection?: boolean | undefined; | ||
}>; | ||
export type SchemaRequestBody = z.infer<typeof schemaRequestBody>; | ||
variables?: ObjectValue; | ||
schemaVersion?: string; | ||
}; | ||
export type SchemaRequestBody = { | ||
schemaVersion?: string; | ||
optionalInputTypes?: boolean; | ||
introspection?: boolean; | ||
}; | ||
export type CodegenResponseBody = { | ||
@@ -647,3 +593,75 @@ code: string; | ||
}; | ||
export type CreateLogsInput = { | ||
evaluations: EvaluationCountInput[]; | ||
events: EventInput[]; | ||
exposures: ExposureInput[]; | ||
idempotencyKey: string; | ||
logs: LogInput[]; | ||
token: string; | ||
}; | ||
export type LogInput = { | ||
commitId?: string | null; | ||
/** A JSON formatted Date string */ | ||
createdAt: string; | ||
level: LogLevel; | ||
message: string; | ||
/** JSON object containing metadata relating to the log */ | ||
metadataJson: string; | ||
type: LogType; | ||
}; | ||
export declare enum LogLevel { | ||
Debug = "Debug", | ||
Error = "Error", | ||
Info = "Info", | ||
Warn = "Warn" | ||
} | ||
export declare enum LogType { | ||
/** Codegen requests processed by the edge server */ | ||
Codegen = "Codegen", | ||
/** GraphQL requests processed by the edge server */ | ||
GraphQl = "GraphQL", | ||
/** Init requests processed by the edge server */ | ||
Init = "Init", | ||
/** JS requests processed by the edge server */ | ||
Js = "JS", | ||
/** Logs from the SDK, that doesn't relate to a specific node */ | ||
SdkMessage = "SDKMessage", | ||
/** Logs from the SDK, that relate to a specific node */ | ||
SdkNode = "SDKNode", | ||
/** Schema requests processed by the edge server */ | ||
Schema = "Schema" | ||
} | ||
export type EvaluationCountInput = { | ||
commitId: string; | ||
count: number; | ||
expressionId: string; | ||
}; | ||
export type EventInput = { | ||
commitId: string; | ||
createdAt: string; | ||
eventObjectTypeName?: string | null; | ||
eventPayloadJson?: string | null; | ||
eventTypeId?: string | null; | ||
unitId?: string | null; | ||
}; | ||
export type ExposureInput = { | ||
assignment: AssignmentInput[]; | ||
commitId: string; | ||
createdAt: string; | ||
eventObjectTypeName?: string | null; | ||
eventPayloadJson?: string | null; | ||
splitId: string; | ||
unitId: string; | ||
}; | ||
export type AssignmentInput = { | ||
continuousValue?: number | null; | ||
dimensionId: string; | ||
discreteArmId?: string | null; | ||
entryType: DimensionType; | ||
}; | ||
export declare enum DimensionType { | ||
Continuous = "Continuous", | ||
Discrete = "Discrete" | ||
} | ||
export {}; | ||
//# sourceMappingURL=types.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.schemaRequestBody = exports.graphqlRequestBody = exports.initRequestBody = exports.codegenRequestBody = exports.language = exports.sdkType = exports.requestTypes = exports.isQueryVariable = exports.objectValueSchema = exports.valueSchema = exports.ApplicationExpressionType = exports.VariableExpressionType = exports.FunctionExpressionType = exports.LogEventExpressionType = exports.ContinuousDimensionType = exports.DiscreteDimensionType = exports.SplitExpressionType = exports.GetUrlQueryParameterExpressionType = exports.StringConcatExpressionType = exports.StringifyNumberExpressionType = exports.RoundNumberExpressionType = exports.ArithmeticExpressionType = exports.arithmeticOperators = exports.ComparisonExpressionType = exports.EnumSwitchExpressionType = exports.SwitchExpressionType = exports.ListExpressionType = exports.UpdateObjectExpressionType = exports.GetFieldExpressionType = exports.ObjectExpressionType = exports.EnumExpressionType = exports.RegexExpressionType = exports.StringExpressionType = exports.FloatExpressionType = exports.IntExpressionType = exports.BooleanExpressionType = exports.NoOpExpressionType = void 0; | ||
/* eslint-disable capitalized-comments */ | ||
const zod_1 = require("zod"); | ||
exports.DimensionType = exports.LogType = exports.LogLevel = exports.requestTypes = exports.isQueryVariable = exports.ApplicationExpressionType = exports.VariableExpressionType = exports.FunctionExpressionType = exports.LogEventExpressionType = exports.ContinuousDimensionType = exports.DiscreteDimensionType = exports.SplitExpressionType = exports.GetUrlQueryParameterExpressionType = exports.StringConcatExpressionType = exports.StringifyNumberExpressionType = exports.RoundNumberExpressionType = exports.ArithmeticExpressionType = exports.arithmeticOperators = exports.ComparisonExpressionType = exports.EnumSwitchExpressionType = exports.SwitchExpressionType = exports.ListExpressionType = exports.UpdateObjectExpressionType = exports.GetFieldExpressionType = exports.ObjectExpressionType = exports.EnumExpressionType = exports.RegexExpressionType = exports.StringExpressionType = exports.FloatExpressionType = exports.IntExpressionType = exports.BooleanExpressionType = exports.NoOpExpressionType = void 0; | ||
const constants_1 = require("./constants"); | ||
@@ -54,10 +52,2 @@ exports.NoOpExpressionType = "NoOpExpression"; | ||
exports.ApplicationExpressionType = "ApplicationExpression"; | ||
exports.valueSchema = zod_1.z.union([ | ||
zod_1.z.boolean(), | ||
zod_1.z.number(), | ||
zod_1.z.string(), | ||
zod_1.z.lazy(() => exports.objectValueSchema), | ||
zod_1.z.lazy(() => exports.valueSchema.array()), | ||
]); | ||
exports.objectValueSchema = zod_1.z.record(zod_1.z.string(), exports.valueSchema); | ||
function isQueryVariable(value) { | ||
@@ -75,33 +65,35 @@ return (typeof value === "object" && value !== null && constants_1.isQueryVariableKey in value); | ||
]; | ||
// Edge Types | ||
exports.sdkType = zod_1.z.enum(["js", "python", "rust"]); | ||
exports.language = zod_1.z.enum(["ts", "js", "python", "rust"]); | ||
exports.codegenRequestBody = zod_1.z.object({ | ||
query: zod_1.z.nullable(zod_1.z.string()), | ||
schemaVersion: zod_1.z.optional(zod_1.z.string()), | ||
includeToken: zod_1.z.optional(zod_1.z.boolean()), | ||
includeFallback: zod_1.z.optional(zod_1.z.boolean()), | ||
sdkType: exports.sdkType, | ||
sdkVersion: zod_1.z.string(), | ||
language: exports.language, | ||
// Deprecated and ignored | ||
buildTimeFallback: zod_1.z.optional(zod_1.z.boolean()), | ||
}); | ||
exports.initRequestBody = zod_1.z.object({ | ||
query: zod_1.z.string(), | ||
variables: zod_1.z.object({}).passthrough(), | ||
schemaVersion: zod_1.z.optional(zod_1.z.string()), | ||
sdkType: exports.sdkType, | ||
sdkVersion: zod_1.z.string(), | ||
}); | ||
exports.graphqlRequestBody = zod_1.z.object({ | ||
query: zod_1.z.string(), | ||
variables: zod_1.z.object({}).passthrough().optional(), | ||
schemaVersion: zod_1.z.optional(zod_1.z.string()), | ||
}); | ||
exports.schemaRequestBody = zod_1.z.object({ | ||
schemaVersion: zod_1.z.optional(zod_1.z.string()), | ||
optionalInputTypes: zod_1.z.optional(zod_1.z.boolean()), | ||
introspection: zod_1.z.optional(zod_1.z.boolean()), | ||
}); | ||
// eslint-disable-next-line no-shadow | ||
var LogLevel; | ||
(function (LogLevel) { | ||
LogLevel["Debug"] = "Debug"; | ||
LogLevel["Error"] = "Error"; | ||
LogLevel["Info"] = "Info"; | ||
LogLevel["Warn"] = "Warn"; | ||
})(LogLevel = exports.LogLevel || (exports.LogLevel = {})); | ||
// eslint-disable-next-line no-shadow | ||
var LogType; | ||
(function (LogType) { | ||
/** Codegen requests processed by the edge server */ | ||
LogType["Codegen"] = "Codegen"; | ||
/** GraphQL requests processed by the edge server */ | ||
LogType["GraphQl"] = "GraphQL"; | ||
/** Init requests processed by the edge server */ | ||
LogType["Init"] = "Init"; | ||
/** JS requests processed by the edge server */ | ||
LogType["Js"] = "JS"; | ||
/** Logs from the SDK, that doesn't relate to a specific node */ | ||
LogType["SdkMessage"] = "SDKMessage"; | ||
/** Logs from the SDK, that relate to a specific node */ | ||
LogType["SdkNode"] = "SDKNode"; | ||
/** Schema requests processed by the edge server */ | ||
// eslint-disable-next-line no-shadow | ||
LogType["Schema"] = "Schema"; | ||
})(LogType = exports.LogType || (exports.LogType = {})); | ||
// eslint-disable-next-line no-shadow | ||
var DimensionType; | ||
(function (DimensionType) { | ||
DimensionType["Continuous"] = "Continuous"; | ||
DimensionType["Discrete"] = "Discrete"; | ||
})(DimensionType = exports.DimensionType || (exports.DimensionType = {})); | ||
//# sourceMappingURL=types.js.map |
{ | ||
"name": "hypertune", | ||
"version": "1.12.0", | ||
"version": "1.13.0", | ||
"private": false, | ||
@@ -23,4 +23,3 @@ "main": "./dist/index.js", | ||
"p-retry": "^4.6.2", | ||
"regenerator-runtime": "^0.13.11", | ||
"zod": "^3.20.2" | ||
"regenerator-runtime": "^0.13.11" | ||
}, | ||
@@ -27,0 +26,0 @@ "devDependencies": { |
@@ -6,13 +6,41 @@ import { CAC } from "cac"; | ||
import { codegenRequest } from "../../lib/edge"; | ||
import { CodegenArguments } from "../../shared"; | ||
import { CodegenArguments, Endpoints, EndpointsPreset } from "../../shared"; | ||
import getEndpoints from "../../shared/getEndpoints"; | ||
import { | ||
registerDocs, | ||
Schema, | ||
throwIfOptionIsUndefined, | ||
withOtherOptionSources, | ||
withValidation, | ||
} from "../helpers"; | ||
import { generateOptionsSchema } from "./options"; | ||
export type GenerateOptions = { | ||
token?: string; | ||
queryFilePath?: string; | ||
outputFilePath?: string; | ||
includeToken?: boolean; | ||
includeFallback?: boolean; | ||
language?: "ts" | "js"; | ||
endpoints?: EndpointsPreset | Endpoints; | ||
}; | ||
export const defaultOptions = { | ||
queryFilePath: "hypertune.graphql", | ||
outputFilePath: "generated/generated.ts", | ||
includeToken: false, | ||
includeFallback: false, | ||
language: "ts", | ||
}; | ||
export const generateOptionsSchema: Schema = { | ||
token: "string", | ||
queryFilePath: "string", | ||
outputFilePath: "string", | ||
includeToken: "boolean", | ||
includeFallback: "boolean", | ||
language: "string", | ||
endpoints: "any", | ||
}; | ||
export function registerGenerateCommand(cli: CAC): void { | ||
const command = cli | ||
cli | ||
// TODO: make generate a sub command, after https://github.com/cacjs/cac/pull/152 | ||
@@ -26,5 +54,25 @@ // .command("generate", "Generate Hypertune files for your project", { allowUnknownOptions: true }) | ||
.example((bin) => `${bin} --token U2FsdGVkX1abcdef`) | ||
.action(withOtherOptionSources(generate, generateOptionsSchema)); | ||
registerDocs(command, generateOptionsSchema); | ||
.action(withOtherOptionSources(generate, generateOptionsSchema)) | ||
.option("--token [value]", "Project token") | ||
.option( | ||
"--queryFilePath [value]", | ||
"File path to the GraphQL initialization query", | ||
{ default: defaultOptions.queryFilePath } | ||
) | ||
.option("--outputFilePath [value]", "Where to write the generated file", { | ||
default: defaultOptions.outputFilePath, | ||
}) | ||
.option("--includeToken", "Include your token in the generated code", { | ||
default: defaultOptions.includeToken, | ||
}) | ||
.option( | ||
"--includeFallback", | ||
"Embed a snapshot of your configuration logic in the generated code so the SDK can reliably, locally initialize first, before fetching the latest logic from the server, and can work even if the server is unreachable", | ||
{ | ||
default: defaultOptions.includeFallback, | ||
} | ||
) | ||
.option("--language [value]", "Target language (ts or js).", { | ||
default: defaultOptions.language, | ||
}); | ||
} | ||
@@ -34,12 +82,15 @@ | ||
generateOptionsSchema, | ||
async (options) => { | ||
async (options: GenerateOptions) => { | ||
console.log("Starting Hypertune code generation..."); | ||
try { | ||
await generateCode({ | ||
token: options.token, | ||
queryCode: getQueryCode(options.queryFilePath), | ||
outputFilePath: options.outputFilePath, | ||
includeToken: options.includeToken, | ||
includeFallback: options.includeFallback, | ||
language: options.language, | ||
token: throwIfOptionIsUndefined("token", options.token), | ||
queryCode: getQueryCode( | ||
options.queryFilePath ?? defaultOptions.queryFilePath | ||
), | ||
outputFilePath: options.outputFilePath ?? defaultOptions.outputFilePath, | ||
includeToken: options.includeToken ?? defaultOptions.includeToken, | ||
includeFallback: | ||
options.includeFallback ?? defaultOptions.includeFallback, | ||
language: options.language ?? (defaultOptions.language as "ts" | "js"), | ||
endpoints: getEndpoints(options.endpoints), | ||
@@ -46,0 +97,0 @@ }); |
import { test } from "vitest"; | ||
import { z } from "zod"; | ||
import { parseOptionValueWithSchema } from "./helpers"; | ||
import { Schema, parseOptionValueWithSchema } from "./helpers"; | ||
const testSchema = z.object({ | ||
stringNoDefault: z.string(), | ||
stringWithDefault: z.string().default("default"), | ||
booleanNoDefault: z.boolean(), | ||
booleanWithDefault: z.boolean().default(false), | ||
unionNoDefault: z.union([z.literal("val1"), z.literal("val2")]), | ||
unionWithDefault: z | ||
.union([z.literal("val1"), z.literal("val2")]) | ||
.default("val1"), | ||
}); | ||
const testSchema: Schema = { | ||
stringValue: "string", | ||
booleanValue: "boolean", | ||
numberValue: "number", | ||
}; | ||
test("parseOptionValueWithSchema doesn't throw an exception", () => { | ||
Object.entries(testSchema.shape).forEach(([, optionSchema]) => { | ||
parseOptionValueWithSchema(optionSchema, "1"); | ||
Object.entries(testSchema).forEach(([, valueType]) => { | ||
parseOptionValueWithSchema(valueType, "1"); | ||
}); | ||
}); |
/* eslint-disable no-underscore-dangle */ | ||
import { Command } from "cac"; | ||
import JoyCon from "joycon"; | ||
import { ZodFirstPartyTypeKind, ZodTypeAny, z } from "zod"; | ||
export type Schema< | ||
Options extends object = object, | ||
OptionsInput extends object = object, | ||
> = z.ZodObject<z.ZodRawShape, "strip", z.ZodTypeAny, Options, OptionsInput>; | ||
type ValueType = "string" | "boolean" | "number" | "any"; | ||
/** | ||
* Add docs to CAC command based on Zod Schema (the ones that appear when you run --help) | ||
*/ | ||
export function registerDocs(command: Command, optionsSchema: Schema): void { | ||
Object.entries(optionsSchema.shape).forEach(([name, optionSchema]) => { | ||
if ( | ||
optionSchema.description && | ||
!optionSchema.description.startsWith("[hidden") | ||
) { | ||
const defaultValue = | ||
optionSchema instanceof z.ZodDefault | ||
? optionSchema.parse(undefined) | ||
: undefined; | ||
export type Schema = { | ||
[optionName: string]: ValueType; | ||
}; | ||
const typeName = | ||
optionSchema instanceof z.ZodDefault | ||
? optionSchema._def.innerType._def.typeName | ||
: optionSchema._def.typeName; | ||
const isBoolean = typeName === "ZodBoolean"; | ||
command.option( | ||
`${optionNameToCliFlag(name)}${isBoolean ? "" : " [value]"}`, | ||
optionSchema.description, | ||
{ | ||
default: defaultValue, | ||
} | ||
); | ||
} | ||
}); | ||
} | ||
/** | ||
@@ -52,9 +20,5 @@ * Handler for a CLI command. Given `Options`, it asynchronously returns a `Result` | ||
*/ | ||
export type Wrapper = < | ||
Options extends object, | ||
OptionsInput extends object, | ||
Result, | ||
>( | ||
export type Wrapper = <Options extends object, Result>( | ||
handler: Handler<Options, Result>, | ||
schema?: Schema<Options, OptionsInput> | ||
schema?: Schema | ||
) => Handler<Options, Result>; | ||
@@ -137,7 +101,4 @@ | ||
k, | ||
schema && schema.shape[k as string] | ||
? parseOptionValueWithSchema( | ||
schema.shape[k as string], | ||
v as string | ||
) | ||
schema && schema[k as string] | ||
? parseOptionValueWithSchema(schema[k as string], v as string) | ||
: v, | ||
@@ -178,58 +139,70 @@ ]) | ||
*/ | ||
export function withValidation< | ||
Options extends object, | ||
OptionsInput extends object, | ||
HandlerResult, | ||
>( | ||
schema: Schema<Options, OptionsInput>, | ||
export function withValidation<Options extends object, HandlerResult>( | ||
schema: Schema, | ||
handler: Handler<Options, HandlerResult> | ||
): Handler<OptionsInput, HandlerResult> { | ||
): Handler<Options, HandlerResult> { | ||
return (options) => { | ||
const parseResult = schema.safeParse(options); | ||
Object.entries(options).forEach(([option, value]) => { | ||
if (!(option in schema)) { | ||
console.warn(`Warning: Ignoring unrecognized option ${option}`); | ||
return; | ||
} | ||
switch (schema[option as string]) { | ||
case "any": | ||
return; | ||
case "string": | ||
if (typeof value !== "string") { | ||
throw new Error( | ||
`Option "${option}" must be a string, but ${typeof value} was provided` | ||
); | ||
} | ||
return; | ||
case "boolean": | ||
if (typeof value !== "boolean") { | ||
throw new Error( | ||
`Option "${option}" must be a boolean, but ${typeof value} was provided` | ||
); | ||
} | ||
return; | ||
case "number": | ||
if (typeof value !== "number") { | ||
throw new Error( | ||
`Option "${option}" must be a number, but ${typeof value} was provided` | ||
); | ||
} | ||
return; | ||
if (!parseResult.success) { | ||
throw new Error( | ||
`Invalid options:\n${parseResult.error.errors | ||
.map(formatZodIssue) | ||
.map((l) => ` ${l}`) | ||
.join("\n")}` | ||
); | ||
} | ||
default: | ||
throw new Error( | ||
`Unexpected option type "${schema[option as string]}" for option "${option}"` | ||
); | ||
} | ||
}); | ||
const strictParseResult = schema.strict().safeParse(options); | ||
if (!strictParseResult.success) { | ||
console.warn( | ||
`Warning: Ignoring unrecognized option(s) ${strictParseResult.error.errors | ||
.map((error) => (error as z.ZodUnrecognizedKeysIssue).keys.join(".")) | ||
.join(" and ")}` | ||
); | ||
} | ||
return handler(parseResult.data); | ||
return handler(options); | ||
}; | ||
} | ||
/** Custom formatting for issues raised by Zod when validating options */ | ||
function formatZodIssue(issue: z.ZodIssue): string { | ||
if (issue.message === "Required") { | ||
const optionName = issue.path.join("."); | ||
return `${optionName}: Missing required argument. Set it in your hypertune config (such as hypertune.json) as ${optionName}, use the ${optionNameToCliFlag( | ||
optionName | ||
)} argument, or the ${optionNameToEnvName( | ||
optionName | ||
)} environment variable.`; | ||
export function throwIfOptionIsUndefined<T>( | ||
optionName: string, | ||
value: T | undefined | ||
): T { | ||
if (value === undefined) { | ||
throw new Error( | ||
`${optionName}: Missing required argument. Set it in your hypertune config (such as hypertune.json) as ${optionName}, use the ${optionNameToCliFlag( | ||
optionName | ||
)} argument, or the ${optionNameToEnvName( | ||
optionName | ||
)} environment variable.` | ||
); | ||
} | ||
return `${String(issue.path)}: ${issue.message}`; | ||
return value; | ||
} | ||
export function parseOptionValueWithSchema( | ||
schema: ZodTypeAny, | ||
valueType: ValueType, | ||
value: string | ||
): number | boolean | string { | ||
const typeName = schema._def.innerType | ||
? schema._def.innerType._def.typeName | ||
: schema._def.typeName; | ||
switch (typeName) { | ||
case ZodFirstPartyTypeKind.ZodBoolean: { | ||
switch (valueType) { | ||
case "boolean": { | ||
switch (value.toLowerCase().trim()) { | ||
@@ -248,3 +221,3 @@ case "1": | ||
} | ||
case ZodFirstPartyTypeKind.ZodNumber: { | ||
case "number": { | ||
if (/^-?\d+\.?\d*$/.test(value)) { | ||
@@ -251,0 +224,0 @@ return Number(value); |
@@ -28,3 +28,3 @@ import "regenerator-runtime/runtime"; | ||
export { LogLevel } from "./generated/graphql"; | ||
export { LogLevel } from "./shared/types"; | ||
@@ -31,0 +31,0 @@ export { initialize, Node }; |
@@ -21,2 +21,3 @@ import pRetry from "p-retry"; | ||
stableStringify, | ||
LogLevel, | ||
} from "../shared"; | ||
@@ -29,3 +30,2 @@ import { hashRequest, initRequest } from "./edge"; | ||
import LRUCache from "../shared/helpers/LRUCache"; | ||
import { LogLevel } from "../generated/graphql"; | ||
import getMetadata from "../shared/helpers/getMetadata"; | ||
@@ -32,0 +32,0 @@ |
import { | ||
backendGraphqlEndpoint, | ||
ReductionLogs, | ||
@@ -10,4 +9,7 @@ Expression, | ||
Fetch, | ||
CreateLogsInput, | ||
LogLevel, | ||
LogType, | ||
logsCreationEndpoint, | ||
} from "../shared"; | ||
import { CreateLogsInput, LogLevel, LogType } from "../generated/graphql"; | ||
import { LoggingMode } from ".."; | ||
@@ -51,3 +53,3 @@ import BackendLogger from "../shared/helpers/BackendLogger"; | ||
createLogs: getCreateLogsFunction( | ||
`${endpoints.logs}${backendGraphqlEndpoint}`, | ||
`${endpoints.logs}${logsCreationEndpoint}`, | ||
fetchFunction | ||
@@ -181,14 +183,8 @@ ), | ||
function getCreateLogsFunction(graphqlUrl: string, fetchFunction: Fetch) { | ||
function getCreateLogsFunction(logsUrl: string, fetchFunction: Fetch) { | ||
return async (input: CreateLogsInput) => { | ||
const body = { | ||
query: | ||
"mutation CreateLogsMutation($input: CreateLogsInput!) {\n createLogs(input: $input)\n}", | ||
variables: { input }, | ||
operationName: "CreateLogsMutation", | ||
}; | ||
const bodyJson = JSON.stringify(body); | ||
const bodyJson = JSON.stringify(input); | ||
const bodyBlob = new Blob([bodyJson]); | ||
const response = await fetchFunction(graphqlUrl, { | ||
const response = await fetchFunction(logsUrl, { | ||
method: "POST", | ||
@@ -195,0 +191,0 @@ headers: { |
/* eslint-disable no-underscore-dangle */ | ||
import { LogLevel } from "../generated/graphql"; | ||
import { | ||
@@ -16,3 +15,2 @@ evaluate, | ||
Value, | ||
objectValueSchema, | ||
breakingSchemaChangesError, | ||
@@ -25,5 +23,6 @@ InitResponseBody, | ||
DeepPartial, | ||
LogLevel, | ||
} from "../shared"; | ||
import getDeepestZodIssue from "../shared/helpers/getDeepestZodIssue"; | ||
import getMetadata from "../shared/helpers/getMetadata"; | ||
import throwIfObjectValueIsInvalid from "../shared/helpers/throwIfObjectValueIsInvalid"; | ||
import Context from "./Context"; | ||
@@ -149,9 +148,6 @@ import Logger from "./Logger"; | ||
try { | ||
const parseResult = objectValueSchema.safeParse(fieldArguments); | ||
if (!parseResult.success) { | ||
const issue = getDeepestZodIssue(parseResult.error); | ||
throw new Error( | ||
`Invalid field arguments: "${issue.message}" at "${issue.path.join(".")}"` | ||
); | ||
} | ||
prefixError( | ||
() => throwIfObjectValueIsInvalid(fieldArguments), | ||
"Invalid field arguments: " | ||
); | ||
@@ -158,0 +154,0 @@ this.updateIfNeeded(); |
export const localBackendBaseUrl = "http://localhost:3001"; | ||
export const localEdgeWorkerBaseUrl = "http://localhost:3002"; | ||
export const localLogsBaseUrl = "http://localhost:3001"; | ||
export const localLogsBaseUrl = "http://localhost:3003"; | ||
@@ -18,2 +18,3 @@ export const stagingBackendBaseUrl = "https://backend.staging.hypertune.com"; | ||
export const backendGraphqlEndpoint = "/graphql"; | ||
export const logsCreationEndpoint = "/logs"; | ||
@@ -20,0 +21,0 @@ export const configFileName = "hypertune.json"; |
import pRetry from "p-retry"; | ||
import { CreateLogsInput, LogType, LogLevel } from "../../generated/graphql"; | ||
import { CreateLogsInput, LogType, LogLevel } from ".."; | ||
import { CountMap, Event, Exposure } from "../types"; | ||
@@ -4,0 +4,0 @@ import nullThrows from "./nullThrows"; |
/* eslint-disable no-console */ | ||
import { LogLevel } from "../../generated/graphql"; | ||
import { LogLevel } from ".."; | ||
@@ -4,0 +4,0 @@ export default function toConsoleFunction( |
@@ -1,3 +0,6 @@ | ||
import { DimensionType } from "../../generated/graphql"; | ||
import { ContinuousDimensionType, DiscreteDimensionType } from "../types"; | ||
import { | ||
DimensionType, | ||
ContinuousDimensionType, | ||
DiscreteDimensionType, | ||
} from "../types"; | ||
@@ -4,0 +7,0 @@ export default function toDimensionTypeEnum( |
/* eslint-disable capitalized-comments */ | ||
import { z } from "zod"; | ||
import { fetch } from "cross-fetch"; | ||
import { isQueryVariableKey } from "./constants"; | ||
import { LogLevel } from "../generated/graphql"; | ||
@@ -468,16 +466,26 @@ /** | ||
export type Split = { | ||
export type SplitBase = { | ||
id: string; // Nano ID | ||
type: SplitType; | ||
name: string; | ||
dimensions: { [dimensionId: string]: Dimension }; | ||
eventObjectTypeName: string | null; // refers to event object schema definition | ||
// E.g. COUNT(*) FILTER (WHERE event_type_id = 'iDBuF9nDQf_D6KLpTETHz') | ||
}; | ||
export type TestSplit = SplitBase & { | ||
type: "test"; | ||
}; | ||
export type MLSplit = SplitBase & { | ||
type: "ml"; | ||
rewardEvents: { | ||
eventObjectTypeName: string; // refers to event object schema definition | ||
unitIdPayloadPath: string[]; // path to unit id in the payload of the goal event type | ||
}[]; | ||
// E.g. LEAST(COUNT(*) FILTER (WHERE event_object_type_name = 'ClickEvent'), 1) | ||
// TODO: Develop abstraction over raw SQL | ||
rewardSql: string; | ||
// @deprecated - use payload instead | ||
featureIds: { [featureId: string]: true }; | ||
}; | ||
export type Split = TestSplit | MLSplit; | ||
export type SplitMap = { [splitId: string]: Split }; | ||
@@ -613,14 +621,2 @@ | ||
export const valueSchema: z.ZodSchema<Value> = z.union([ | ||
z.boolean(), | ||
z.number(), | ||
z.string(), | ||
z.lazy(() => objectValueSchema), | ||
z.lazy(() => valueSchema.array()), | ||
]); | ||
export const objectValueSchema: z.ZodSchema<ObjectValue> = z.record( | ||
z.string(), | ||
valueSchema | ||
); | ||
export type QueryVariable = { [isQueryVariableKey]: true; name: string }; | ||
@@ -674,43 +670,35 @@ | ||
export const sdkType = z.enum(["js", "python", "rust"]); | ||
export type SdkType = z.infer<typeof sdkType>; | ||
export type SdkType = "js" | "python" | "rust"; | ||
export const language = z.enum(["ts", "js", "python", "rust"]); | ||
export type Language = z.infer<typeof language>; | ||
export type Language = "ts" | "js" | "python" | "rust"; | ||
export const codegenRequestBody = z.object({ | ||
query: z.nullable(z.string()), | ||
schemaVersion: z.optional(z.string()), | ||
includeToken: z.optional(z.boolean()), | ||
includeFallback: z.optional(z.boolean()), | ||
sdkType, | ||
sdkVersion: z.string(), | ||
language, | ||
// Deprecated and ignored | ||
buildTimeFallback: z.optional(z.boolean()), | ||
}); | ||
export type CodegenRequestBody = z.infer<typeof codegenRequestBody>; | ||
export type CodegenRequestBody = { | ||
query: string | null; | ||
schemaVersion?: string; | ||
includeToken?: boolean; | ||
includeFallback?: boolean; | ||
sdkType: SdkType; | ||
sdkVersion: string; | ||
language: Language; | ||
}; | ||
export const initRequestBody = z.object({ | ||
query: z.string(), | ||
variables: z.object({}).passthrough(), | ||
schemaVersion: z.optional(z.string()), | ||
sdkType, | ||
sdkVersion: z.string(), | ||
}); | ||
export type InitRequestBody = z.infer<typeof initRequestBody>; | ||
export type InitRequestBody = { | ||
query: string; | ||
variables: ObjectValue; | ||
schemaVersion?: string; | ||
sdkType: SdkType; | ||
sdkVersion: string; | ||
}; | ||
export const graphqlRequestBody = z.object({ | ||
query: z.string(), | ||
variables: z.object({}).passthrough().optional(), | ||
schemaVersion: z.optional(z.string()), | ||
}); | ||
export type GraphqlRequestBody = z.infer<typeof graphqlRequestBody>; | ||
export type GraphqlRequestBody = { | ||
query: string; | ||
variables?: ObjectValue; | ||
schemaVersion?: string; | ||
}; | ||
export const schemaRequestBody = z.object({ | ||
schemaVersion: z.optional(z.string()), | ||
optionalInputTypes: z.optional(z.boolean()), | ||
introspection: z.optional(z.boolean()), | ||
}); | ||
export type SchemaRequestBody = z.infer<typeof schemaRequestBody>; | ||
export type SchemaRequestBody = { | ||
schemaVersion?: string; | ||
optionalInputTypes?: boolean; | ||
introspection?: boolean; | ||
}; | ||
@@ -831,1 +819,88 @@ export type CodegenResponseBody = { code: string }; | ||
}; | ||
// Logs endpoint schema | ||
export type CreateLogsInput = { | ||
evaluations: EvaluationCountInput[]; | ||
events: EventInput[]; | ||
exposures: ExposureInput[]; | ||
idempotencyKey: string; | ||
logs: LogInput[]; | ||
token: string; | ||
}; | ||
export type LogInput = { | ||
commitId?: string | null; | ||
/** A JSON formatted Date string */ | ||
createdAt: string; | ||
level: LogLevel; | ||
message: string; | ||
/** JSON object containing metadata relating to the log */ | ||
metadataJson: string; | ||
type: LogType; | ||
}; | ||
// eslint-disable-next-line no-shadow | ||
export enum LogLevel { | ||
Debug = "Debug", | ||
Error = "Error", | ||
Info = "Info", | ||
Warn = "Warn", | ||
} | ||
// eslint-disable-next-line no-shadow | ||
export enum LogType { | ||
/** Codegen requests processed by the edge server */ | ||
Codegen = "Codegen", | ||
/** GraphQL requests processed by the edge server */ | ||
GraphQl = "GraphQL", | ||
/** Init requests processed by the edge server */ | ||
Init = "Init", | ||
/** JS requests processed by the edge server */ | ||
Js = "JS", | ||
/** Logs from the SDK, that doesn't relate to a specific node */ | ||
SdkMessage = "SDKMessage", | ||
/** Logs from the SDK, that relate to a specific node */ | ||
SdkNode = "SDKNode", | ||
/** Schema requests processed by the edge server */ | ||
// eslint-disable-next-line no-shadow | ||
Schema = "Schema", | ||
} | ||
export type EvaluationCountInput = { | ||
commitId: string; | ||
count: number; | ||
expressionId: string; | ||
}; | ||
export type EventInput = { | ||
commitId: string; | ||
createdAt: string; | ||
eventObjectTypeName?: string | null; | ||
eventPayloadJson?: string | null; | ||
eventTypeId?: string | null; | ||
unitId?: string | null; | ||
}; | ||
export type ExposureInput = { | ||
assignment: AssignmentInput[]; | ||
commitId: string; | ||
createdAt: string; | ||
eventObjectTypeName?: string | null; | ||
eventPayloadJson?: string | null; | ||
splitId: string; | ||
unitId: string; | ||
}; | ||
export type AssignmentInput = { | ||
continuousValue?: number | null; | ||
dimensionId: string; | ||
discreteArmId?: string | null; | ||
entryType: DimensionType; | ||
}; | ||
// eslint-disable-next-line no-shadow | ||
export enum DimensionType { | ||
Continuous = "Continuous", | ||
Discrete = "Discrete", | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9
641410
293
11749
- Removedzod@^3.20.2
- Removedzod@3.24.1(transitive)