WIP: bootstrap and partial real Solana watcher implementation
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export type MakeRequired<Base, Key extends keyof Base> = Omit<Base, Key> & Required<Record<Key, NonNullable<Base[Key]>>>;
|
||||
export type ValueOf<T> = T[keyof T];
|
||||
@@ -0,0 +1,666 @@
|
||||
import type * as checks from "./checks.js";
|
||||
import type * as JSONSchema from "./json-schema.js";
|
||||
import type { $ZodRegistry } from "./registries.js";
|
||||
import type * as schemas from "./schemas.js";
|
||||
import {
|
||||
type Processor,
|
||||
type RegistryToJSONSchemaParams,
|
||||
type ToJSONSchemaParams,
|
||||
type ZodStandardJSONSchemaPayload,
|
||||
extractDefs,
|
||||
finalize,
|
||||
initializeContext,
|
||||
process,
|
||||
} from "./to-json-schema.js";
|
||||
import { getEnumValues } from "./util.js";
|
||||
|
||||
const formatMap: Partial<Record<checks.$ZodStringFormats, string | undefined>> = {
|
||||
guid: "uuid",
|
||||
url: "uri",
|
||||
datetime: "date-time",
|
||||
json_string: "json-string",
|
||||
regex: "", // do not set
|
||||
};
|
||||
|
||||
// ==================== SIMPLE TYPE PROCESSORS ====================
|
||||
|
||||
export const stringProcessor: Processor<schemas.$ZodString> = (schema, ctx, _json, _params) => {
|
||||
const json = _json as JSONSchema.StringSchema;
|
||||
json.type = "string";
|
||||
const { minimum, maximum, format, patterns, contentEncoding } = schema._zod
|
||||
.bag as schemas.$ZodStringInternals<unknown>["bag"];
|
||||
if (typeof minimum === "number") json.minLength = minimum;
|
||||
if (typeof maximum === "number") json.maxLength = maximum;
|
||||
// custom pattern overrides format
|
||||
if (format) {
|
||||
json.format = formatMap[format as checks.$ZodStringFormats] ?? format;
|
||||
if (json.format === "") delete json.format; // empty format is not valid
|
||||
|
||||
// JSON Schema format: "time" requires a full time with offset or Z
|
||||
// z.iso.time() does not include timezone information, so format: "time" should never be used
|
||||
if (format === "time") {
|
||||
delete json.format;
|
||||
}
|
||||
}
|
||||
if (contentEncoding) json.contentEncoding = contentEncoding;
|
||||
if (patterns && patterns.size > 0) {
|
||||
const regexes = [...patterns];
|
||||
if (regexes.length === 1) json.pattern = regexes[0]!.source;
|
||||
else if (regexes.length > 1) {
|
||||
json.allOf = [
|
||||
...regexes.map((regex) => ({
|
||||
...(ctx.target === "draft-07" || ctx.target === "draft-04" || ctx.target === "openapi-3.0"
|
||||
? ({ type: "string" } as const)
|
||||
: {}),
|
||||
pattern: regex.source,
|
||||
})),
|
||||
];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const numberProcessor: Processor<schemas.$ZodNumber> = (schema, ctx, _json, _params) => {
|
||||
const json = _json as JSONSchema.NumberSchema | JSONSchema.IntegerSchema;
|
||||
const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
|
||||
if (typeof format === "string" && format.includes("int")) json.type = "integer";
|
||||
else json.type = "number";
|
||||
|
||||
// when both minimum and exclusiveMinimum exist, pick the more restrictive one
|
||||
const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
|
||||
const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
|
||||
const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
|
||||
|
||||
if (exMin) {
|
||||
if (legacy) {
|
||||
json.minimum = exclusiveMinimum;
|
||||
json.exclusiveMinimum = true;
|
||||
} else {
|
||||
json.exclusiveMinimum = exclusiveMinimum;
|
||||
}
|
||||
} else if (typeof minimum === "number") {
|
||||
json.minimum = minimum;
|
||||
}
|
||||
|
||||
if (exMax) {
|
||||
if (legacy) {
|
||||
json.maximum = exclusiveMaximum;
|
||||
json.exclusiveMaximum = true;
|
||||
} else {
|
||||
json.exclusiveMaximum = exclusiveMaximum;
|
||||
}
|
||||
} else if (typeof maximum === "number") {
|
||||
json.maximum = maximum;
|
||||
}
|
||||
|
||||
if (typeof multipleOf === "number") json.multipleOf = multipleOf;
|
||||
};
|
||||
|
||||
export const booleanProcessor: Processor<schemas.$ZodBoolean> = (_schema, _ctx, json, _params) => {
|
||||
(json as JSONSchema.BooleanSchema).type = "boolean";
|
||||
};
|
||||
|
||||
export const bigintProcessor: Processor<schemas.$ZodBigInt> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("BigInt cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const symbolProcessor: Processor<schemas.$ZodSymbol> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Symbols cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const nullProcessor: Processor<schemas.$ZodNull> = (_schema, ctx, json, _params) => {
|
||||
if (ctx.target === "openapi-3.0") {
|
||||
json.type = "string";
|
||||
json.nullable = true;
|
||||
json.enum = [null];
|
||||
} else {
|
||||
json.type = "null";
|
||||
}
|
||||
};
|
||||
|
||||
export const undefinedProcessor: Processor<schemas.$ZodUndefined> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Undefined cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const voidProcessor: Processor<schemas.$ZodVoid> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Void cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const neverProcessor: Processor<schemas.$ZodNever> = (_schema, _ctx, json, _params) => {
|
||||
json.not = {};
|
||||
};
|
||||
|
||||
export const anyProcessor: Processor<schemas.$ZodAny> = (_schema, _ctx, _json, _params) => {
|
||||
// empty schema accepts anything
|
||||
};
|
||||
|
||||
export const unknownProcessor: Processor<schemas.$ZodUnknown> = (_schema, _ctx, _json, _params) => {
|
||||
// empty schema accepts anything
|
||||
};
|
||||
|
||||
export const dateProcessor: Processor<schemas.$ZodDate> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Date cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const enumProcessor: Processor<schemas.$ZodEnum> = (schema, _ctx, json, _params) => {
|
||||
const def = schema._zod.def as schemas.$ZodEnumDef;
|
||||
const values = getEnumValues(def.entries);
|
||||
// Number enums can have both string and number values
|
||||
if (values.every((v) => typeof v === "number")) json.type = "number";
|
||||
if (values.every((v) => typeof v === "string")) json.type = "string";
|
||||
json.enum = values;
|
||||
};
|
||||
|
||||
export const literalProcessor: Processor<schemas.$ZodLiteral> = (schema, ctx, json, _params) => {
|
||||
const def = schema._zod.def as schemas.$ZodLiteralDef<any>;
|
||||
const vals: (string | number | boolean | null)[] = [];
|
||||
for (const val of def.values) {
|
||||
if (val === undefined) {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Literal `undefined` cannot be represented in JSON Schema");
|
||||
} else {
|
||||
// do not add to vals
|
||||
}
|
||||
} else if (typeof val === "bigint") {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("BigInt literals cannot be represented in JSON Schema");
|
||||
} else {
|
||||
vals.push(Number(val));
|
||||
}
|
||||
} else {
|
||||
vals.push(val);
|
||||
}
|
||||
}
|
||||
if (vals.length === 0) {
|
||||
// do nothing (an undefined literal was stripped)
|
||||
} else if (vals.length === 1) {
|
||||
const val = vals[0]!;
|
||||
json.type = val === null ? ("null" as const) : (typeof val as any);
|
||||
if (ctx.target === "draft-04" || ctx.target === "openapi-3.0") {
|
||||
json.enum = [val];
|
||||
} else {
|
||||
json.const = val;
|
||||
}
|
||||
} else {
|
||||
if (vals.every((v) => typeof v === "number")) json.type = "number";
|
||||
if (vals.every((v) => typeof v === "string")) json.type = "string";
|
||||
if (vals.every((v) => typeof v === "boolean")) json.type = "boolean";
|
||||
if (vals.every((v) => v === null)) json.type = "null";
|
||||
json.enum = vals;
|
||||
}
|
||||
};
|
||||
|
||||
export const nanProcessor: Processor<schemas.$ZodNaN> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("NaN cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const templateLiteralProcessor: Processor<schemas.$ZodTemplateLiteral> = (schema, _ctx, json, _params) => {
|
||||
const _json = json as JSONSchema.StringSchema;
|
||||
const pattern = schema._zod.pattern;
|
||||
if (!pattern) throw new Error("Pattern not found in template literal");
|
||||
_json.type = "string";
|
||||
_json.pattern = pattern.source;
|
||||
};
|
||||
|
||||
export const fileProcessor: Processor<schemas.$ZodFile> = (schema, _ctx, json, _params) => {
|
||||
const _json = json as JSONSchema.StringSchema;
|
||||
const file: JSONSchema.StringSchema = {
|
||||
type: "string",
|
||||
format: "binary",
|
||||
contentEncoding: "binary",
|
||||
};
|
||||
|
||||
const { minimum, maximum, mime } = schema._zod.bag as schemas.$ZodFileInternals["bag"];
|
||||
if (minimum !== undefined) file.minLength = minimum;
|
||||
if (maximum !== undefined) file.maxLength = maximum;
|
||||
if (mime) {
|
||||
if (mime.length === 1) {
|
||||
file.contentMediaType = mime[0]!;
|
||||
Object.assign(_json, file);
|
||||
} else {
|
||||
Object.assign(_json, file); // shared props at root
|
||||
_json.anyOf = mime.map((m) => ({ contentMediaType: m })); // only contentMediaType differs
|
||||
}
|
||||
} else {
|
||||
Object.assign(_json, file);
|
||||
}
|
||||
};
|
||||
|
||||
export const successProcessor: Processor<schemas.$ZodSuccess> = (_schema, _ctx, json, _params) => {
|
||||
(json as JSONSchema.BooleanSchema).type = "boolean";
|
||||
};
|
||||
|
||||
export const customProcessor: Processor<schemas.$ZodCustom> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Custom types cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const functionProcessor: Processor<schemas.$ZodFunction> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Function types cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const transformProcessor: Processor<schemas.$ZodTransform> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Transforms cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const mapProcessor: Processor<schemas.$ZodMap> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Map cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
export const setProcessor: Processor<schemas.$ZodSet> = (_schema, ctx, _json, _params) => {
|
||||
if (ctx.unrepresentable === "throw") {
|
||||
throw new Error("Set cannot be represented in JSON Schema");
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== COMPOSITE TYPE PROCESSORS ====================
|
||||
|
||||
export const arrayProcessor: Processor<schemas.$ZodArray> = (schema, ctx, _json, params) => {
|
||||
const json = _json as JSONSchema.ArraySchema;
|
||||
const def = schema._zod.def as schemas.$ZodArrayDef;
|
||||
const { minimum, maximum } = schema._zod.bag;
|
||||
if (typeof minimum === "number") json.minItems = minimum;
|
||||
if (typeof maximum === "number") json.maxItems = maximum;
|
||||
|
||||
json.type = "array";
|
||||
json.items = process(def.element, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "items"],
|
||||
});
|
||||
};
|
||||
|
||||
export const objectProcessor: Processor<schemas.$ZodObject> = (schema, ctx, _json, params) => {
|
||||
const json = _json as JSONSchema.ObjectSchema;
|
||||
const def = schema._zod.def as schemas.$ZodObjectDef;
|
||||
json.type = "object";
|
||||
json.properties = {};
|
||||
const shape = def.shape;
|
||||
|
||||
for (const key in shape) {
|
||||
json.properties[key] = process(shape[key]!, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "properties", key],
|
||||
});
|
||||
}
|
||||
|
||||
// required keys
|
||||
const allKeys = new Set(Object.keys(shape));
|
||||
const requiredKeys = new Set(
|
||||
[...allKeys].filter((key) => {
|
||||
const v = def.shape[key]!._zod;
|
||||
if (ctx.io === "input") {
|
||||
return v.optin === undefined;
|
||||
} else {
|
||||
return v.optout === undefined;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
if (requiredKeys.size > 0) {
|
||||
json.required = Array.from(requiredKeys);
|
||||
}
|
||||
|
||||
// catchall
|
||||
if (def.catchall?._zod.def.type === "never") {
|
||||
// strict
|
||||
json.additionalProperties = false;
|
||||
} else if (!def.catchall) {
|
||||
// regular
|
||||
if (ctx.io === "output") json.additionalProperties = false;
|
||||
} else if (def.catchall) {
|
||||
json.additionalProperties = process(def.catchall, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "additionalProperties"],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const unionProcessor: Processor<schemas.$ZodUnion> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodUnionDef;
|
||||
// Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches)
|
||||
// This includes both z.xor() and discriminated unions
|
||||
const isExclusive = def.inclusive === false;
|
||||
const options = def.options.map((x, i) =>
|
||||
process(x, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i],
|
||||
})
|
||||
);
|
||||
if (isExclusive) {
|
||||
json.oneOf = options;
|
||||
} else {
|
||||
json.anyOf = options;
|
||||
}
|
||||
};
|
||||
|
||||
export const intersectionProcessor: Processor<schemas.$ZodIntersection> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodIntersectionDef;
|
||||
const a = process(def.left, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "allOf", 0],
|
||||
});
|
||||
const b = process(def.right, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "allOf", 1],
|
||||
});
|
||||
|
||||
const isSimpleIntersection = (val: any) => "allOf" in val && Object.keys(val).length === 1;
|
||||
const allOf = [
|
||||
...(isSimpleIntersection(a) ? (a.allOf as any[]) : [a]),
|
||||
...(isSimpleIntersection(b) ? (b.allOf as any[]) : [b]),
|
||||
];
|
||||
json.allOf = allOf;
|
||||
};
|
||||
|
||||
export const tupleProcessor: Processor<schemas.$ZodTuple> = (schema, ctx, _json, params) => {
|
||||
const json = _json as JSONSchema.ArraySchema;
|
||||
const def = schema._zod.def as schemas.$ZodTupleDef;
|
||||
json.type = "array";
|
||||
|
||||
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
||||
const restPath =
|
||||
ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
||||
|
||||
const prefixItems = def.items.map((x, i) =>
|
||||
process(x, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, prefixPath, i],
|
||||
})
|
||||
);
|
||||
const rest = def.rest
|
||||
? process(def.rest, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, restPath, ...(ctx.target === "openapi-3.0" ? [def.items.length] : [])],
|
||||
})
|
||||
: null;
|
||||
|
||||
if (ctx.target === "draft-2020-12") {
|
||||
json.prefixItems = prefixItems;
|
||||
if (rest) {
|
||||
json.items = rest;
|
||||
}
|
||||
} else if (ctx.target === "openapi-3.0") {
|
||||
json.items = {
|
||||
anyOf: prefixItems,
|
||||
};
|
||||
|
||||
if (rest) {
|
||||
json.items.anyOf!.push(rest);
|
||||
}
|
||||
json.minItems = prefixItems.length;
|
||||
if (!rest) {
|
||||
json.maxItems = prefixItems.length;
|
||||
}
|
||||
} else {
|
||||
json.items = prefixItems;
|
||||
if (rest) {
|
||||
json.additionalItems = rest;
|
||||
}
|
||||
}
|
||||
|
||||
// length
|
||||
const { minimum, maximum } = schema._zod.bag as {
|
||||
minimum?: number;
|
||||
maximum?: number;
|
||||
};
|
||||
if (typeof minimum === "number") json.minItems = minimum;
|
||||
if (typeof maximum === "number") json.maxItems = maximum;
|
||||
};
|
||||
|
||||
export const recordProcessor: Processor<schemas.$ZodRecord> = (schema, ctx, _json, params) => {
|
||||
const json = _json as JSONSchema.ObjectSchema;
|
||||
const def = schema._zod.def as schemas.$ZodRecordDef;
|
||||
json.type = "object";
|
||||
|
||||
// For looseRecord with regex patterns, use patternProperties
|
||||
// This correctly represents "only validate keys matching the pattern" semantics
|
||||
// and composes well with allOf (intersections)
|
||||
const keyType = def.keyType as schemas.$ZodTypes;
|
||||
const keyBag = keyType._zod.bag as schemas.$ZodStringInternals<unknown>["bag"] | undefined;
|
||||
const patterns = keyBag?.patterns;
|
||||
|
||||
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
||||
// Use patternProperties for looseRecord with regex patterns
|
||||
const valueSchema = process(def.valueType, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "patternProperties", "*"],
|
||||
});
|
||||
json.patternProperties = {};
|
||||
for (const pattern of patterns) {
|
||||
json.patternProperties[pattern.source] = valueSchema;
|
||||
}
|
||||
} else {
|
||||
// Default behavior: use propertyNames + additionalProperties
|
||||
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
||||
json.propertyNames = process(def.keyType, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "propertyNames"],
|
||||
});
|
||||
}
|
||||
json.additionalProperties = process(def.valueType, ctx as any, {
|
||||
...params,
|
||||
path: [...params.path, "additionalProperties"],
|
||||
});
|
||||
}
|
||||
|
||||
// Add required for keys with discrete values (enum, literal, etc.)
|
||||
const keyValues = keyType._zod.values;
|
||||
if (keyValues) {
|
||||
const validKeyValues = [...keyValues].filter(
|
||||
(v): v is string | number => typeof v === "string" || typeof v === "number"
|
||||
);
|
||||
|
||||
if (validKeyValues.length > 0) {
|
||||
json.required = validKeyValues as string[];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const nullableProcessor: Processor<schemas.$ZodNullable> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodNullableDef;
|
||||
const inner = process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
if (ctx.target === "openapi-3.0") {
|
||||
seen.ref = def.innerType;
|
||||
json.nullable = true;
|
||||
} else {
|
||||
json.anyOf = [inner, { type: "null" }];
|
||||
}
|
||||
};
|
||||
|
||||
export const nonoptionalProcessor: Processor<schemas.$ZodNonOptional> = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodNonOptionalDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
|
||||
export const defaultProcessor: Processor<schemas.$ZodDefault> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodDefaultDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
json.default = JSON.parse(JSON.stringify(def.defaultValue));
|
||||
};
|
||||
|
||||
export const prefaultProcessor: Processor<schemas.$ZodPrefault> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodPrefaultDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
if (ctx.io === "input") json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
|
||||
};
|
||||
|
||||
export const catchProcessor: Processor<schemas.$ZodCatch> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodCatchDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
let catchValue: any;
|
||||
try {
|
||||
catchValue = def.catchValue(undefined as any);
|
||||
} catch {
|
||||
throw new Error("Dynamic catch values are not supported in JSON Schema");
|
||||
}
|
||||
json.default = catchValue;
|
||||
};
|
||||
|
||||
export const pipeProcessor: Processor<schemas.$ZodPipe> = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodPipeDef;
|
||||
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
||||
const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
|
||||
process(innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = innerType;
|
||||
};
|
||||
|
||||
export const readonlyProcessor: Processor<schemas.$ZodReadonly> = (schema, ctx, json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodReadonlyDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
json.readOnly = true;
|
||||
};
|
||||
|
||||
export const promiseProcessor: Processor<schemas.$ZodPromise> = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodPromiseDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
|
||||
export const optionalProcessor: Processor<schemas.$ZodOptional> = (schema, ctx, _json, params) => {
|
||||
const def = schema._zod.def as schemas.$ZodOptionalDef;
|
||||
process(def.innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = def.innerType;
|
||||
};
|
||||
|
||||
export const lazyProcessor: Processor<schemas.$ZodLazy> = (schema, ctx, _json, params) => {
|
||||
const innerType = (schema as schemas.$ZodLazy)._zod.innerType;
|
||||
process(innerType, ctx as any, params);
|
||||
const seen = ctx.seen.get(schema)!;
|
||||
seen.ref = innerType;
|
||||
};
|
||||
|
||||
// ==================== ALL PROCESSORS ====================
|
||||
|
||||
export const allProcessors: Record<string, Processor<any>> = {
|
||||
string: stringProcessor,
|
||||
number: numberProcessor,
|
||||
boolean: booleanProcessor,
|
||||
bigint: bigintProcessor,
|
||||
symbol: symbolProcessor,
|
||||
null: nullProcessor,
|
||||
undefined: undefinedProcessor,
|
||||
void: voidProcessor,
|
||||
never: neverProcessor,
|
||||
any: anyProcessor,
|
||||
unknown: unknownProcessor,
|
||||
date: dateProcessor,
|
||||
enum: enumProcessor,
|
||||
literal: literalProcessor,
|
||||
nan: nanProcessor,
|
||||
template_literal: templateLiteralProcessor,
|
||||
file: fileProcessor,
|
||||
success: successProcessor,
|
||||
custom: customProcessor,
|
||||
function: functionProcessor,
|
||||
transform: transformProcessor,
|
||||
map: mapProcessor,
|
||||
set: setProcessor,
|
||||
array: arrayProcessor,
|
||||
object: objectProcessor,
|
||||
union: unionProcessor,
|
||||
intersection: intersectionProcessor,
|
||||
tuple: tupleProcessor,
|
||||
record: recordProcessor,
|
||||
nullable: nullableProcessor,
|
||||
nonoptional: nonoptionalProcessor,
|
||||
default: defaultProcessor,
|
||||
prefault: prefaultProcessor,
|
||||
catch: catchProcessor,
|
||||
pipe: pipeProcessor,
|
||||
readonly: readonlyProcessor,
|
||||
promise: promiseProcessor,
|
||||
optional: optionalProcessor,
|
||||
lazy: lazyProcessor,
|
||||
};
|
||||
|
||||
// ==================== TOP-LEVEL toJSONSchema ====================
|
||||
|
||||
export function toJSONSchema<T extends schemas.$ZodType>(
|
||||
schema: T,
|
||||
params?: ToJSONSchemaParams
|
||||
): ZodStandardJSONSchemaPayload<T>;
|
||||
export function toJSONSchema(
|
||||
registry: $ZodRegistry<{ id?: string | undefined }>,
|
||||
params?: RegistryToJSONSchemaParams
|
||||
): { schemas: Record<string, ZodStandardJSONSchemaPayload<schemas.$ZodType>> };
|
||||
export function toJSONSchema(
|
||||
input: schemas.$ZodType | $ZodRegistry<{ id?: string | undefined }>,
|
||||
params?: ToJSONSchemaParams | RegistryToJSONSchemaParams
|
||||
): any {
|
||||
if ("_idmap" in input) {
|
||||
// Registry case
|
||||
const registry = input as $ZodRegistry<{ id?: string | undefined }>;
|
||||
const ctx = initializeContext({ ...params, processors: allProcessors });
|
||||
const defs: any = {};
|
||||
|
||||
// First pass: process all schemas to build the seen map
|
||||
for (const entry of registry._idmap.entries()) {
|
||||
const [_, schema] = entry;
|
||||
process(schema, ctx as any);
|
||||
}
|
||||
|
||||
const schemas: Record<string, JSONSchema.BaseSchema> = {};
|
||||
const external = {
|
||||
registry,
|
||||
uri: (params as RegistryToJSONSchemaParams)?.uri,
|
||||
defs,
|
||||
};
|
||||
|
||||
// Update the context with external configuration
|
||||
ctx.external = external;
|
||||
|
||||
// Second pass: emit each schema
|
||||
for (const entry of registry._idmap.entries()) {
|
||||
const [key, schema] = entry;
|
||||
extractDefs(ctx as any, schema);
|
||||
schemas[key] = finalize(ctx as any, schema);
|
||||
}
|
||||
|
||||
if (Object.keys(defs).length > 0) {
|
||||
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
||||
schemas.__shared = {
|
||||
[defsSegment]: defs,
|
||||
};
|
||||
}
|
||||
|
||||
return { schemas };
|
||||
}
|
||||
|
||||
// Single schema case
|
||||
const ctx = initializeContext({ ...params, processors: allProcessors });
|
||||
process(input, ctx as any);
|
||||
extractDefs(ctx as any, input);
|
||||
return finalize(ctx as any, input);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# base-x
|
||||
|
||||
[](https://www.npmjs.org/package/base-x)
|
||||
[](https://travis-ci.org/cryptocoinjs/base-x)
|
||||
|
||||
[](https://github.com/feross/standard)
|
||||
|
||||
Fast base encoding / decoding of any given alphabet using bitcoin style leading
|
||||
zero compression.
|
||||
|
||||
**WARNING:** This module is **NOT RFC3548** compliant, it cannot be used for base16 (hex), base32, or base64 encoding in a standards compliant manner.
|
||||
|
||||
## Example
|
||||
|
||||
Base58
|
||||
|
||||
``` javascript
|
||||
var BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
var bs58 = require('base-x')(BASE58)
|
||||
|
||||
var decoded = bs58.decode('5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr')
|
||||
|
||||
console.log(decoded)
|
||||
// => <Buffer 80 ed db dc 11 68 f1 da ea db d3 e4 4c 1e 3f 8f 5a 28 4c 20 29 f7 8a d2 6a f9 85 83 a4 99 de 5b 19>
|
||||
|
||||
console.log(bs58.encode(decoded))
|
||||
// => 5Kd3NBUAdUnhyzenEwVLy9pBKxSwXvE9FMPyR4UKZvpe6E3AgLr
|
||||
```
|
||||
|
||||
### Alphabets
|
||||
|
||||
See below for a list of commonly recognized alphabets, and their respective base.
|
||||
|
||||
Base | Alphabet
|
||||
------------- | -------------
|
||||
2 | `01`
|
||||
8 | `01234567`
|
||||
11 | `0123456789a`
|
||||
16 | `0123456789abcdef`
|
||||
32 | `0123456789ABCDEFGHJKMNPQRSTVWXYZ`
|
||||
32 | `ybndrfg8ejkmcpqxot1uwisza345h769` (z-base-32)
|
||||
36 | `0123456789abcdefghijklmnopqrstuvwxyz`
|
||||
58 | `123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz`
|
||||
62 | `0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
|
||||
64 | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`
|
||||
67 | `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~`
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
It encodes octet arrays by doing long divisions on all significant digits in the
|
||||
array, creating a representation of that number in the new base. Then for every
|
||||
leading zero in the input (not significant as a number) it will encode as a
|
||||
single leader character. This is the first in the alphabet and will decode as 8
|
||||
bits. The other characters depend upon the base. For example, a base58 alphabet
|
||||
packs roughly 5.858 bits per character.
|
||||
|
||||
This means the encoded string 000f (using a base16, 0-f alphabet) will actually decode
|
||||
to 4 bytes unlike a canonical hex encoding which uniformly packs 4 bits into each
|
||||
character.
|
||||
|
||||
While unusual, this does mean that no padding is required and it works for bases
|
||||
like 43.
|
||||
|
||||
|
||||
## LICENSE [MIT](LICENSE)
|
||||
A direct derivation of the base58 implementation from [`bitcoin/bitcoin`](https://github.com/bitcoin/bitcoin/blob/f1e2f2a85962c1664e4e55471061af0eaa798d40/src/base58.cpp), generalized for variable length alphabets.
|
||||
@@ -0,0 +1,108 @@
|
||||
import * as util from "../core/util.js";
|
||||
const error = () => {
|
||||
const Sizable = {
|
||||
string: { unit: "ký tự", verb: "có" },
|
||||
file: { unit: "byte", verb: "có" },
|
||||
array: { unit: "phần tử", verb: "có" },
|
||||
set: { unit: "phần tử", verb: "có" },
|
||||
};
|
||||
function getSizing(origin) {
|
||||
return Sizable[origin] ?? null;
|
||||
}
|
||||
const FormatDictionary = {
|
||||
regex: "đầu vào",
|
||||
email: "địa chỉ email",
|
||||
url: "URL",
|
||||
emoji: "emoji",
|
||||
uuid: "UUID",
|
||||
uuidv4: "UUIDv4",
|
||||
uuidv6: "UUIDv6",
|
||||
nanoid: "nanoid",
|
||||
guid: "GUID",
|
||||
cuid: "cuid",
|
||||
cuid2: "cuid2",
|
||||
ulid: "ULID",
|
||||
xid: "XID",
|
||||
ksuid: "KSUID",
|
||||
datetime: "ngày giờ ISO",
|
||||
date: "ngày ISO",
|
||||
time: "giờ ISO",
|
||||
duration: "khoảng thời gian ISO",
|
||||
ipv4: "địa chỉ IPv4",
|
||||
ipv6: "địa chỉ IPv6",
|
||||
cidrv4: "dải IPv4",
|
||||
cidrv6: "dải IPv6",
|
||||
base64: "chuỗi mã hóa base64",
|
||||
base64url: "chuỗi mã hóa base64url",
|
||||
json_string: "chuỗi JSON",
|
||||
e164: "số E.164",
|
||||
jwt: "JWT",
|
||||
template_literal: "đầu vào",
|
||||
};
|
||||
const TypeDictionary = {
|
||||
nan: "NaN",
|
||||
number: "số",
|
||||
array: "mảng",
|
||||
};
|
||||
return (issue) => {
|
||||
switch (issue.code) {
|
||||
case "invalid_type": {
|
||||
const expected = TypeDictionary[issue.expected] ?? issue.expected;
|
||||
const receivedType = util.parsedType(issue.input);
|
||||
const received = TypeDictionary[receivedType] ?? receivedType;
|
||||
if (/^[A-Z]/.test(issue.expected)) {
|
||||
return `Đầu vào không hợp lệ: mong đợi instanceof ${issue.expected}, nhận được ${received}`;
|
||||
}
|
||||
return `Đầu vào không hợp lệ: mong đợi ${expected}, nhận được ${received}`;
|
||||
}
|
||||
case "invalid_value":
|
||||
if (issue.values.length === 1)
|
||||
return `Đầu vào không hợp lệ: mong đợi ${util.stringifyPrimitive(issue.values[0])}`;
|
||||
return `Tùy chọn không hợp lệ: mong đợi một trong các giá trị ${util.joinValues(issue.values, "|")}`;
|
||||
case "too_big": {
|
||||
const adj = issue.inclusive ? "<=" : "<";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing)
|
||||
return `Quá lớn: mong đợi ${issue.origin ?? "giá trị"} ${sizing.verb} ${adj}${issue.maximum.toString()} ${sizing.unit ?? "phần tử"}`;
|
||||
return `Quá lớn: mong đợi ${issue.origin ?? "giá trị"} ${adj}${issue.maximum.toString()}`;
|
||||
}
|
||||
case "too_small": {
|
||||
const adj = issue.inclusive ? ">=" : ">";
|
||||
const sizing = getSizing(issue.origin);
|
||||
if (sizing) {
|
||||
return `Quá nhỏ: mong đợi ${issue.origin} ${sizing.verb} ${adj}${issue.minimum.toString()} ${sizing.unit}`;
|
||||
}
|
||||
return `Quá nhỏ: mong đợi ${issue.origin} ${adj}${issue.minimum.toString()}`;
|
||||
}
|
||||
case "invalid_format": {
|
||||
const _issue = issue;
|
||||
if (_issue.format === "starts_with")
|
||||
return `Chuỗi không hợp lệ: phải bắt đầu bằng "${_issue.prefix}"`;
|
||||
if (_issue.format === "ends_with")
|
||||
return `Chuỗi không hợp lệ: phải kết thúc bằng "${_issue.suffix}"`;
|
||||
if (_issue.format === "includes")
|
||||
return `Chuỗi không hợp lệ: phải bao gồm "${_issue.includes}"`;
|
||||
if (_issue.format === "regex")
|
||||
return `Chuỗi không hợp lệ: phải khớp với mẫu ${_issue.pattern}`;
|
||||
return `${FormatDictionary[_issue.format] ?? issue.format} không hợp lệ`;
|
||||
}
|
||||
case "not_multiple_of":
|
||||
return `Số không hợp lệ: phải là bội số của ${issue.divisor}`;
|
||||
case "unrecognized_keys":
|
||||
return `Khóa không được nhận dạng: ${util.joinValues(issue.keys, ", ")}`;
|
||||
case "invalid_key":
|
||||
return `Khóa không hợp lệ trong ${issue.origin}`;
|
||||
case "invalid_union":
|
||||
return "Đầu vào không hợp lệ";
|
||||
case "invalid_element":
|
||||
return `Giá trị không hợp lệ trong ${issue.origin}`;
|
||||
default:
|
||||
return `Đầu vào không hợp lệ`;
|
||||
}
|
||||
};
|
||||
};
|
||||
export default function () {
|
||||
return {
|
||||
localeError: error(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/**
|
||||
* SHA3 (keccak) hash function, based on a new "Sponge function" design.
|
||||
* Different from older hashes, the internal state is bigger than output size.
|
||||
*
|
||||
* Check out [FIPS-202](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf),
|
||||
* [Website](https://keccak.team/keccak.html),
|
||||
* [the differences between SHA-3 and Keccak](https://crypto.stackexchange.com/questions/15727/what-are-the-key-differences-between-the-draft-sha-3-standard-and-the-keccak-sub).
|
||||
*
|
||||
* Check out `sha3-addons` module for cSHAKE, k12, and others.
|
||||
* @module
|
||||
*/
|
||||
import { rotlBH, rotlBL, rotlSH, rotlSL, split } from './_u64.ts';
|
||||
// prettier-ignore
|
||||
import {
|
||||
abytes, aexists, anumber, aoutput,
|
||||
clean, createHasher, createXOFer, Hash,
|
||||
swap32IfBE,
|
||||
toBytes, u32,
|
||||
type CHash, type CHashXO, type HashXOF, type Input
|
||||
} from './utils.ts';
|
||||
|
||||
// No __PURE__ annotations in sha3 header:
|
||||
// EVERYTHING is in fact used on every export.
|
||||
// Various per round constants calculations
|
||||
const _0n = BigInt(0);
|
||||
const _1n = BigInt(1);
|
||||
const _2n = BigInt(2);
|
||||
const _7n = BigInt(7);
|
||||
const _256n = BigInt(256);
|
||||
const _0x71n = BigInt(0x71);
|
||||
const SHA3_PI: number[] = [];
|
||||
const SHA3_ROTL: number[] = [];
|
||||
const _SHA3_IOTA: bigint[] = [];
|
||||
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
||||
// Pi
|
||||
[x, y] = [y, (2 * x + 3 * y) % 5];
|
||||
SHA3_PI.push(2 * (5 * y + x));
|
||||
// Rotational
|
||||
SHA3_ROTL.push((((round + 1) * (round + 2)) / 2) % 64);
|
||||
// Iota
|
||||
let t = _0n;
|
||||
for (let j = 0; j < 7; j++) {
|
||||
R = ((R << _1n) ^ ((R >> _7n) * _0x71n)) % _256n;
|
||||
if (R & _2n) t ^= _1n << ((_1n << /* @__PURE__ */ BigInt(j)) - _1n);
|
||||
}
|
||||
_SHA3_IOTA.push(t);
|
||||
}
|
||||
const IOTAS = split(_SHA3_IOTA, true);
|
||||
const SHA3_IOTA_H = IOTAS[0];
|
||||
const SHA3_IOTA_L = IOTAS[1];
|
||||
|
||||
// Left rotation (without 0, 32, 64)
|
||||
const rotlH = (h: number, l: number, s: number) => (s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s));
|
||||
const rotlL = (h: number, l: number, s: number) => (s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s));
|
||||
|
||||
/** `keccakf1600` internal function, additionally allows to adjust round count. */
|
||||
export function keccakP(s: Uint32Array, rounds: number = 24): void {
|
||||
const B = new Uint32Array(5 * 2);
|
||||
// NOTE: all indices are x2 since we store state as u32 instead of u64 (bigints to slow in js)
|
||||
for (let round = 24 - rounds; round < 24; round++) {
|
||||
// Theta θ
|
||||
for (let x = 0; x < 10; x++) B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
||||
for (let x = 0; x < 10; x += 2) {
|
||||
const idx1 = (x + 8) % 10;
|
||||
const idx0 = (x + 2) % 10;
|
||||
const B0 = B[idx0];
|
||||
const B1 = B[idx0 + 1];
|
||||
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
||||
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
s[x + y] ^= Th;
|
||||
s[x + y + 1] ^= Tl;
|
||||
}
|
||||
}
|
||||
// Rho (ρ) and Pi (π)
|
||||
let curH = s[2];
|
||||
let curL = s[3];
|
||||
for (let t = 0; t < 24; t++) {
|
||||
const shift = SHA3_ROTL[t];
|
||||
const Th = rotlH(curH, curL, shift);
|
||||
const Tl = rotlL(curH, curL, shift);
|
||||
const PI = SHA3_PI[t];
|
||||
curH = s[PI];
|
||||
curL = s[PI + 1];
|
||||
s[PI] = Th;
|
||||
s[PI + 1] = Tl;
|
||||
}
|
||||
// Chi (χ)
|
||||
for (let y = 0; y < 50; y += 10) {
|
||||
for (let x = 0; x < 10; x++) B[x] = s[y + x];
|
||||
for (let x = 0; x < 10; x++) s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
||||
}
|
||||
// Iota (ι)
|
||||
s[0] ^= SHA3_IOTA_H[round];
|
||||
s[1] ^= SHA3_IOTA_L[round];
|
||||
}
|
||||
clean(B);
|
||||
}
|
||||
|
||||
/** Keccak sponge function. */
|
||||
export class Keccak extends Hash<Keccak> implements HashXOF<Keccak> {
|
||||
protected state: Uint8Array;
|
||||
protected pos = 0;
|
||||
protected posOut = 0;
|
||||
protected finished = false;
|
||||
protected state32: Uint32Array;
|
||||
protected destroyed = false;
|
||||
|
||||
public blockLen: number;
|
||||
public suffix: number;
|
||||
public outputLen: number;
|
||||
protected enableXOF = false;
|
||||
protected rounds: number;
|
||||
|
||||
// NOTE: we accept arguments in bytes instead of bits here.
|
||||
constructor(
|
||||
blockLen: number,
|
||||
suffix: number,
|
||||
outputLen: number,
|
||||
enableXOF = false,
|
||||
rounds: number = 24
|
||||
) {
|
||||
super();
|
||||
this.blockLen = blockLen;
|
||||
this.suffix = suffix;
|
||||
this.outputLen = outputLen;
|
||||
this.enableXOF = enableXOF;
|
||||
this.rounds = rounds;
|
||||
// Can be passed from user as dkLen
|
||||
anumber(outputLen);
|
||||
// 1600 = 5x5 matrix of 64bit. 1600 bits === 200 bytes
|
||||
// 0 < blockLen < 200
|
||||
if (!(0 < blockLen && blockLen < 200))
|
||||
throw new Error('only keccak-f1600 function is supported');
|
||||
this.state = new Uint8Array(200);
|
||||
this.state32 = u32(this.state);
|
||||
}
|
||||
clone(): Keccak {
|
||||
return this._cloneInto();
|
||||
}
|
||||
protected keccak(): void {
|
||||
swap32IfBE(this.state32);
|
||||
keccakP(this.state32, this.rounds);
|
||||
swap32IfBE(this.state32);
|
||||
this.posOut = 0;
|
||||
this.pos = 0;
|
||||
}
|
||||
update(data: Input): this {
|
||||
aexists(this);
|
||||
data = toBytes(data);
|
||||
abytes(data);
|
||||
const { blockLen, state } = this;
|
||||
const len = data.length;
|
||||
for (let pos = 0; pos < len; ) {
|
||||
const take = Math.min(blockLen - this.pos, len - pos);
|
||||
for (let i = 0; i < take; i++) state[this.pos++] ^= data[pos++];
|
||||
if (this.pos === blockLen) this.keccak();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
protected finish(): void {
|
||||
if (this.finished) return;
|
||||
this.finished = true;
|
||||
const { state, suffix, pos, blockLen } = this;
|
||||
// Do the padding
|
||||
state[pos] ^= suffix;
|
||||
if ((suffix & 0x80) !== 0 && pos === blockLen - 1) this.keccak();
|
||||
state[blockLen - 1] ^= 0x80;
|
||||
this.keccak();
|
||||
}
|
||||
protected writeInto(out: Uint8Array): Uint8Array {
|
||||
aexists(this, false);
|
||||
abytes(out);
|
||||
this.finish();
|
||||
const bufferOut = this.state;
|
||||
const { blockLen } = this;
|
||||
for (let pos = 0, len = out.length; pos < len; ) {
|
||||
if (this.posOut >= blockLen) this.keccak();
|
||||
const take = Math.min(blockLen - this.posOut, len - pos);
|
||||
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
||||
this.posOut += take;
|
||||
pos += take;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
xofInto(out: Uint8Array): Uint8Array {
|
||||
// Sha3/Keccak usage with XOF is probably mistake, only SHAKE instances can do XOF
|
||||
if (!this.enableXOF) throw new Error('XOF is not possible for this instance');
|
||||
return this.writeInto(out);
|
||||
}
|
||||
xof(bytes: number): Uint8Array {
|
||||
anumber(bytes);
|
||||
return this.xofInto(new Uint8Array(bytes));
|
||||
}
|
||||
digestInto(out: Uint8Array): Uint8Array {
|
||||
aoutput(out, this);
|
||||
if (this.finished) throw new Error('digest() was already called');
|
||||
this.writeInto(out);
|
||||
this.destroy();
|
||||
return out;
|
||||
}
|
||||
digest(): Uint8Array {
|
||||
return this.digestInto(new Uint8Array(this.outputLen));
|
||||
}
|
||||
destroy(): void {
|
||||
this.destroyed = true;
|
||||
clean(this.state);
|
||||
}
|
||||
_cloneInto(to?: Keccak): Keccak {
|
||||
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
||||
to ||= new Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
|
||||
to.state32.set(this.state32);
|
||||
to.pos = this.pos;
|
||||
to.posOut = this.posOut;
|
||||
to.finished = this.finished;
|
||||
to.rounds = rounds;
|
||||
// Suffix can change in cSHAKE
|
||||
to.suffix = suffix;
|
||||
to.outputLen = outputLen;
|
||||
to.enableXOF = enableXOF;
|
||||
to.destroyed = this.destroyed;
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
const gen = (suffix: number, blockLen: number, outputLen: number) =>
|
||||
createHasher(() => new Keccak(blockLen, suffix, outputLen));
|
||||
|
||||
/** SHA3-224 hash function. */
|
||||
export const sha3_224: CHash = /* @__PURE__ */ (() => gen(0x06, 144, 224 / 8))();
|
||||
/** SHA3-256 hash function. Different from keccak-256. */
|
||||
export const sha3_256: CHash = /* @__PURE__ */ (() => gen(0x06, 136, 256 / 8))();
|
||||
/** SHA3-384 hash function. */
|
||||
export const sha3_384: CHash = /* @__PURE__ */ (() => gen(0x06, 104, 384 / 8))();
|
||||
/** SHA3-512 hash function. */
|
||||
export const sha3_512: CHash = /* @__PURE__ */ (() => gen(0x06, 72, 512 / 8))();
|
||||
|
||||
/** keccak-224 hash function. */
|
||||
export const keccak_224: CHash = /* @__PURE__ */ (() => gen(0x01, 144, 224 / 8))();
|
||||
/** keccak-256 hash function. Different from SHA3-256. */
|
||||
export const keccak_256: CHash = /* @__PURE__ */ (() => gen(0x01, 136, 256 / 8))();
|
||||
/** keccak-384 hash function. */
|
||||
export const keccak_384: CHash = /* @__PURE__ */ (() => gen(0x01, 104, 384 / 8))();
|
||||
/** keccak-512 hash function. */
|
||||
export const keccak_512: CHash = /* @__PURE__ */ (() => gen(0x01, 72, 512 / 8))();
|
||||
|
||||
export type ShakeOpts = { dkLen?: number };
|
||||
|
||||
const genShake = (suffix: number, blockLen: number, outputLen: number) =>
|
||||
createXOFer<HashXOF<Keccak>, ShakeOpts>(
|
||||
(opts: ShakeOpts = {}) =>
|
||||
new Keccak(blockLen, suffix, opts.dkLen === undefined ? outputLen : opts.dkLen, true)
|
||||
);
|
||||
|
||||
/** SHAKE128 XOF with 128-bit security. */
|
||||
export const shake128: CHashXO = /* @__PURE__ */ (() => genShake(0x1f, 168, 128 / 8))();
|
||||
/** SHAKE256 XOF with 256-bit security. */
|
||||
export const shake256: CHashXO = /* @__PURE__ */ (() => genShake(0x1f, 136, 256 / 8))();
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "pino-abstract-transport",
|
||||
"version": "3.0.0",
|
||||
"description": "Write Pino transports easily",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"prepare": "husky install",
|
||||
"test": "standard | snazzy && borp --check-coverage 'test/*.test.js' && tsd",
|
||||
"test-ci": "npm test"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pinojs/pino-abstract-transport.git"
|
||||
},
|
||||
"keywords": [
|
||||
"pino",
|
||||
"transport"
|
||||
],
|
||||
"author": "Matteo Collina <hello@matteocollina.com>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pinojs/pino-abstract-transport/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pinojs/pino-abstract-transport#readme",
|
||||
"dependencies": {
|
||||
"split2": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@matteo.collina/tspl": "^0.2.0",
|
||||
"@types/node": "^20.1.0",
|
||||
"borp": "^0.20.2",
|
||||
"husky": "^9.0.6",
|
||||
"snazzy": "^9.0.0",
|
||||
"standard": "^17.0.0",
|
||||
"thread-stream": "^3.1.0",
|
||||
"tsd": "^0.31.0"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "./test/types"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const utils_1 = require("@typescript-eslint/utils");
|
||||
const ts = __importStar(require("typescript"));
|
||||
const util_1 = require("../util");
|
||||
exports.default = (0, util_1.createRule)({
|
||||
name: 'return-await',
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: 'Enforce consistent awaiting of returned promises',
|
||||
recommended: {
|
||||
strict: ['error-handling-correctness-only'],
|
||||
},
|
||||
requiresTypeChecking: true,
|
||||
},
|
||||
fixable: 'code',
|
||||
// eslint-disable-next-line eslint-plugin/require-meta-has-suggestions -- suggestions are exposed through a helper.
|
||||
hasSuggestions: true,
|
||||
messages: {
|
||||
disallowedPromiseAwait: 'Returning an awaited promise is not allowed in this context.',
|
||||
disallowedPromiseAwaitSuggestion: 'Remove `await` before the expression. Use caution as this may impact control flow.',
|
||||
nonPromiseAwait: 'Returning an awaited value that is not a promise is not allowed.',
|
||||
requiredPromiseAwait: 'Returning an awaited promise is required in this context.',
|
||||
requiredPromiseAwaitSuggestion: 'Add `await` before the expression. Use caution as this may impact control flow.',
|
||||
},
|
||||
schema: [
|
||||
{
|
||||
type: 'string',
|
||||
oneOf: [
|
||||
{
|
||||
type: 'string',
|
||||
description: 'Requires that all returned promises be awaited.',
|
||||
enum: ['always'],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: 'In error-handling contexts, the rule enforces that returned promises must be awaited. In ordinary contexts, the rule does not enforce any particular behavior around whether returned promises are awaited.',
|
||||
enum: ['error-handling-correctness-only'],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: 'In error-handling contexts, the rule enforces that returned promises must be awaited. In ordinary contexts, the rule enforces that returned promises _must not_ be awaited.',
|
||||
enum: ['in-try-catch'],
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: 'Disallows awaiting any returned promises.',
|
||||
enum: ['never'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
defaultOptions: ['in-try-catch'],
|
||||
create(context, [option]) {
|
||||
const services = (0, util_1.getParserServices)(context);
|
||||
const checker = services.program.getTypeChecker();
|
||||
const scopeInfoStack = [];
|
||||
function enterFunction(node) {
|
||||
scopeInfoStack.push({
|
||||
hasAsync: node.async,
|
||||
owningFunc: node,
|
||||
});
|
||||
}
|
||||
function exitFunction() {
|
||||
scopeInfoStack.pop();
|
||||
}
|
||||
function affectsExplicitResourceManagement(node) {
|
||||
// just need to determine if there is a `using` declaration in scope.
|
||||
let scope = context.sourceCode.getScope(node);
|
||||
const functionScope = scope.variableScope;
|
||||
while (true) {
|
||||
for (const variable of scope.variables) {
|
||||
if (variable.defs.length !== 1) {
|
||||
// This can't be the case for `using` or `await using` since it's
|
||||
// an error to redeclare those more than once in the same scope,
|
||||
// unlike, say, `var` declarations.
|
||||
continue;
|
||||
}
|
||||
const declaration = variable.defs[0];
|
||||
const declaratorNode = declaration.node;
|
||||
const declarationNode = declaratorNode.parent;
|
||||
// if it's a using/await using declaration, and it comes _before_ the
|
||||
// node we're checking, it affects control flow for that node.
|
||||
if (['await using', 'using'].includes(declarationNode.kind) &&
|
||||
declaratorNode.range[1] < node.range[0]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (scope === functionScope) {
|
||||
// We've checked all the relevant scopes
|
||||
break;
|
||||
}
|
||||
// This should always exist, since the rule should only be checking
|
||||
// contexts in which `return` statements are legal, which should always
|
||||
// be inside a function.
|
||||
scope = (0, util_1.nullThrows)(scope.upper, 'Expected parent scope to exist. return-await should only operate on return statements within functions');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Tests whether a node is inside of an explicit error handling context
|
||||
* (try/catch/finally) in a way that throwing an exception will have an
|
||||
* impact on the program's control flow.
|
||||
*/
|
||||
function affectsExplicitErrorHandling(node) {
|
||||
// If an error-handling block is followed by another error-handling block,
|
||||
// control flow is affected by whether promises in it are awaited or not.
|
||||
// Otherwise, we need to check recursively for nested try statements until
|
||||
// we get to the top level of a function or the program. If by then,
|
||||
// there's no offending error-handling blocks, it doesn't affect control
|
||||
// flow.
|
||||
const tryAncestorResult = findContainingTryStatement(node);
|
||||
if (tryAncestorResult == null) {
|
||||
return false;
|
||||
}
|
||||
const { block, tryStatement } = tryAncestorResult;
|
||||
switch (block) {
|
||||
case 'catch':
|
||||
// Exceptions thrown in catch blocks followed by a finally block affect
|
||||
// control flow.
|
||||
if (tryStatement.finallyBlock != null) {
|
||||
return true;
|
||||
}
|
||||
// Otherwise recurse.
|
||||
return affectsExplicitErrorHandling(tryStatement);
|
||||
case 'finally':
|
||||
return affectsExplicitErrorHandling(tryStatement);
|
||||
case 'try':
|
||||
// Try blocks are always followed by either a catch or finally,
|
||||
// so exceptions thrown here always affect control flow.
|
||||
return true;
|
||||
default: {
|
||||
const __never = block;
|
||||
throw new Error(`Unexpected block type: ${String(__never)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A try _statement_ is the whole thing that encompasses try block,
|
||||
* catch clause, and finally block. This function finds the nearest
|
||||
* enclosing try statement (if present) for a given node, and reports which
|
||||
* part of the try statement the node is in.
|
||||
*/
|
||||
function findContainingTryStatement(node) {
|
||||
let child = node;
|
||||
let ancestor = node.parent;
|
||||
while (ancestor && !ts.isFunctionLike(ancestor)) {
|
||||
if (ts.isTryStatement(ancestor)) {
|
||||
let block;
|
||||
if (child === ancestor.tryBlock) {
|
||||
block = 'try';
|
||||
}
|
||||
else if (child === ancestor.catchClause) {
|
||||
block = 'catch';
|
||||
}
|
||||
else if (child === ancestor.finallyBlock) {
|
||||
block = 'finally';
|
||||
}
|
||||
return {
|
||||
block: (0, util_1.nullThrows)(block, 'Child of a try statement must be a try block, catch clause, or finally block'),
|
||||
tryStatement: ancestor,
|
||||
};
|
||||
}
|
||||
child = ancestor;
|
||||
ancestor = ancestor.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function removeAwait(fixer, node) {
|
||||
// Should always be an await node; but let's be safe.
|
||||
/* istanbul ignore if */ if (!(0, util_1.isAwaitExpression)(node)) {
|
||||
return null;
|
||||
}
|
||||
const awaitToken = context.sourceCode.getFirstToken(node, util_1.isAwaitKeyword);
|
||||
// Should always be the case; but let's be safe.
|
||||
/* istanbul ignore if */ if (!awaitToken) {
|
||||
return null;
|
||||
}
|
||||
const startAt = awaitToken.range[0];
|
||||
let endAt = awaitToken.range[1];
|
||||
// Also remove any extraneous whitespace after `await`, if there is any.
|
||||
const nextToken = context.sourceCode.getTokenAfter(awaitToken, {
|
||||
includeComments: true,
|
||||
});
|
||||
if (nextToken) {
|
||||
endAt = nextToken.range[0];
|
||||
}
|
||||
return fixer.removeRange([startAt, endAt]);
|
||||
}
|
||||
function insertAwait(fixer, node, isHighPrecedence) {
|
||||
if (isHighPrecedence) {
|
||||
return fixer.insertTextBefore(node, 'await ');
|
||||
}
|
||||
return [
|
||||
fixer.insertTextBefore(node, 'await ('),
|
||||
fixer.insertTextAfter(node, ')'),
|
||||
];
|
||||
}
|
||||
function test(node, expression) {
|
||||
let child;
|
||||
const isAwait = ts.isAwaitExpression(expression);
|
||||
if (isAwait) {
|
||||
child = expression.getChildAt(1);
|
||||
}
|
||||
else {
|
||||
child = expression;
|
||||
}
|
||||
const type = checker.getTypeAtLocation(child);
|
||||
const certainty = (0, util_1.needsToBeAwaited)(checker, expression, type);
|
||||
// handle awaited _non_thenables
|
||||
if (certainty !== util_1.Awaitable.Always) {
|
||||
if (isAwait) {
|
||||
if (certainty === util_1.Awaitable.May) {
|
||||
return;
|
||||
}
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'nonPromiseAwait',
|
||||
fix: fixer => removeAwait(fixer, node),
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
// At this point it's definitely a thenable.
|
||||
const affectsErrorHandling = affectsExplicitErrorHandling(expression) ||
|
||||
affectsExplicitResourceManagement(node);
|
||||
const useAutoFix = !affectsErrorHandling;
|
||||
const ruleConfiguration = getConfiguration(option);
|
||||
const shouldAwaitInCurrentContext = affectsErrorHandling
|
||||
? ruleConfiguration.errorHandlingContext
|
||||
: ruleConfiguration.ordinaryContext;
|
||||
switch (shouldAwaitInCurrentContext) {
|
||||
case 'await':
|
||||
if (!isAwait) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'requiredPromiseAwait',
|
||||
...(0, util_1.getFixOrSuggest)({
|
||||
fixOrSuggest: useAutoFix ? 'fix' : 'suggest',
|
||||
suggestion: {
|
||||
messageId: 'requiredPromiseAwaitSuggestion',
|
||||
fix: fixer => insertAwait(fixer, node, (0, util_1.isHigherPrecedenceThanAwait)(expression)),
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
break;
|
||||
case "don't-care":
|
||||
break;
|
||||
case 'no-await':
|
||||
if (isAwait) {
|
||||
context.report({
|
||||
node,
|
||||
messageId: 'disallowedPromiseAwait',
|
||||
...(0, util_1.getFixOrSuggest)({
|
||||
fixOrSuggest: useAutoFix ? 'fix' : 'suggest',
|
||||
suggestion: {
|
||||
messageId: 'disallowedPromiseAwaitSuggestion',
|
||||
fix: fixer => removeAwait(fixer, node),
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
function findPossiblyReturnedNodes(node) {
|
||||
if (node.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
|
||||
return [
|
||||
...findPossiblyReturnedNodes(node.alternate),
|
||||
...findPossiblyReturnedNodes(node.consequent),
|
||||
];
|
||||
}
|
||||
return [node];
|
||||
}
|
||||
return {
|
||||
ArrowFunctionExpression: enterFunction,
|
||||
'ArrowFunctionExpression:exit': exitFunction,
|
||||
FunctionDeclaration: enterFunction,
|
||||
'FunctionDeclaration:exit': exitFunction,
|
||||
FunctionExpression: enterFunction,
|
||||
'FunctionExpression:exit': exitFunction,
|
||||
// executes after less specific handler, so exitFunction is called
|
||||
'ArrowFunctionExpression[async = true]:exit'(node) {
|
||||
if (node.body.type !== utils_1.AST_NODE_TYPES.BlockStatement) {
|
||||
findPossiblyReturnedNodes(node.body).forEach(node => {
|
||||
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
|
||||
test(node, tsNode);
|
||||
});
|
||||
}
|
||||
},
|
||||
ReturnStatement(node) {
|
||||
const scopeInfo = scopeInfoStack.at(-1);
|
||||
if (!scopeInfo?.hasAsync || !node.argument) {
|
||||
return;
|
||||
}
|
||||
findPossiblyReturnedNodes(node.argument).forEach(node => {
|
||||
const tsNode = services.esTreeNodeToTSNodeMap.get(node);
|
||||
test(node, tsNode);
|
||||
});
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
function getConfiguration(option) {
|
||||
switch (option) {
|
||||
case 'always':
|
||||
return {
|
||||
errorHandlingContext: 'await',
|
||||
ordinaryContext: 'await',
|
||||
};
|
||||
case 'error-handling-correctness-only':
|
||||
return {
|
||||
errorHandlingContext: 'await',
|
||||
ordinaryContext: "don't-care",
|
||||
};
|
||||
case 'in-try-catch':
|
||||
return {
|
||||
errorHandlingContext: 'await',
|
||||
ordinaryContext: 'no-await',
|
||||
};
|
||||
case 'never':
|
||||
return {
|
||||
errorHandlingContext: 'no-await',
|
||||
ordinaryContext: 'no-await',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
// @ts-ignore TS6133
|
||||
import { test } from "vitest";
|
||||
|
||||
import * as z from "zod/v3";
|
||||
|
||||
test("test", () => {
|
||||
z;
|
||||
});
|
||||
|
||||
// const fish = z.object({
|
||||
// name: z.string(),
|
||||
// props: z.object({
|
||||
// color: z.string(),
|
||||
// numScales: z.number(),
|
||||
// }),
|
||||
// });
|
||||
|
||||
// const nonStrict = z
|
||||
// .object({
|
||||
// name: z.string(),
|
||||
// color: z.string(),
|
||||
// })
|
||||
// .nonstrict();
|
||||
|
||||
// test('object pick type', () => {
|
||||
// const modNonStrictFish = nonStrict.omit({ name: true });
|
||||
// modNonStrictFish.parse({ color: 'asdf' });
|
||||
|
||||
// const bad1 = () => fish.pick({ props: { unknown: true } } as any);
|
||||
// const bad2 = () => fish.omit({ name: true, props: { unknown: true } } as any);
|
||||
|
||||
// expect(bad1).toThrow();
|
||||
// expect(bad2).toThrow();
|
||||
// });
|
||||
|
||||
// test('f1', () => {
|
||||
// const f1 = fish.pick(true);
|
||||
// f1.parse({ name: 'a', props: { color: 'b', numScales: 3 } });
|
||||
// });
|
||||
// test('f2', () => {
|
||||
// const f2 = fish.pick({ props: true });
|
||||
// f2.parse({ props: { color: 'asdf', numScales: 1 } });
|
||||
// const badcheck2 = () => f2.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
|
||||
// expect(badcheck2).toThrow();
|
||||
// });
|
||||
// test('f3', () => {
|
||||
// const f3 = fish.pick({ props: { color: true } });
|
||||
// f3.parse({ props: { color: 'b' } });
|
||||
// const badcheck3 = () => f3.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
|
||||
// expect(badcheck3).toThrow();
|
||||
// });
|
||||
// test('f4', () => {
|
||||
// const badcheck4 = () => fish.pick({ props: { color: true, unknown: true } });
|
||||
// expect(badcheck4).toThrow();
|
||||
// });
|
||||
// test('f6', () => {
|
||||
// const f6 = fish.omit({ props: true });
|
||||
// const badcheck6 = () => f6.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
|
||||
// f6.parse({ name: 'adsf' });
|
||||
// expect(badcheck6).toThrow();
|
||||
// });
|
||||
// test('f7', () => {
|
||||
// const f7 = fish.omit({ props: { color: true } });
|
||||
// f7.parse({ name: 'a', props: { numScales: 3 } });
|
||||
// const badcheck7 = () => f7.parse({ name: 'a', props: { color: 'b', numScales: 3 } } as any);
|
||||
// expect(badcheck7).toThrow();
|
||||
// });
|
||||
// test('f8', () => {
|
||||
// const badcheck8 = () => fish.omit({ props: { color: true, unknown: true } });
|
||||
// expect(badcheck8).toThrow();
|
||||
// });
|
||||
// test('f9', () => {
|
||||
// const f9 = nonStrict.pick(true);
|
||||
// f9.parse({ name: 'a', color: 'asdf' });
|
||||
// });
|
||||
// test('f10', () => {
|
||||
// const f10 = nonStrict.pick({ name: true });
|
||||
// f10.parse({ name: 'a' });
|
||||
// const val = f10.parse({ name: 'a', color: 'b' });
|
||||
// expect(val).toEqual({ name: 'a' });
|
||||
// });
|
||||
// test('f12', () => {
|
||||
// const badfcheck12 = () => nonStrict.omit({ color: true, asdf: true });
|
||||
// expect(badfcheck12).toThrow();
|
||||
// });
|
||||
|
||||
// test('array masking', () => {
|
||||
// const fishArray = z.array(fish);
|
||||
// const modFishArray = fishArray.pick({
|
||||
// name: true,
|
||||
// props: {
|
||||
// numScales: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
// modFishArray.parse([{ name: 'fish', props: { numScales: 12 } }]);
|
||||
// const bad1 = () => modFishArray.parse([{ name: 'fish', props: { numScales: 12, color: 'asdf' } }] as any);
|
||||
// expect(bad1).toThrow();
|
||||
// });
|
||||
|
||||
// test('array masking', () => {
|
||||
// const fishArray = z.array(fish);
|
||||
// const fail = () =>
|
||||
// fishArray.pick({
|
||||
// name: true,
|
||||
// props: {
|
||||
// whatever: true,
|
||||
// },
|
||||
// } as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('array masking', () => {
|
||||
// const fishArray = z.array(fish);
|
||||
// const fail = () =>
|
||||
// fishArray.omit({
|
||||
// whateve: true,
|
||||
// } as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('array masking', () => {
|
||||
// const fishArray = z.array(fish);
|
||||
// const modFishList = fishArray.omit({
|
||||
// name: true,
|
||||
// props: {
|
||||
// color: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
// modFishList.parse([{ props: { numScales: 12 } }]);
|
||||
// const fail = () => modFishList.parse([{ name: 'hello', props: { numScales: 12 } }] as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('primitive array masking', () => {
|
||||
// const fishArray = z.array(z.number());
|
||||
// const fail = () => fishArray.pick({} as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('other array masking', () => {
|
||||
// const fishArray = z.array(z.array(z.number()));
|
||||
// const fail = () => fishArray.pick({} as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #1', () => {
|
||||
// const fail = () => fish.pick(1 as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #2', () => {
|
||||
// const fail = () => fish.pick([] as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #3', () => {
|
||||
// const fail = () => fish.pick(false as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #4', () => {
|
||||
// const fail = () => fish.pick('asdf' as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #5', () => {
|
||||
// const fail = () => fish.omit(1 as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #6', () => {
|
||||
// const fail = () => fish.omit([] as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #7', () => {
|
||||
// const fail = () => fish.omit(false as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
|
||||
// test('invalid mask #8', () => {
|
||||
// const fail = () => fish.omit('asdf' as any);
|
||||
// expect(fail).toThrow();
|
||||
// });
|
||||
@@ -0,0 +1 @@
|
||||
self.Flatted=function(n){"use strict";function t(n){return t="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(n){return typeof n}:function(n){return n&&"function"==typeof Symbol&&n.constructor===Symbol&&n!==Symbol.prototype?"symbol":typeof n},t(n)}var r=JSON.parse,e=JSON.stringify,o=Object.keys,u=String,f="string",i={},c="object",a=function(n,t){return t},l=function(n){return n instanceof u?u(n):n},s=function(n,r){return t(r)===f?new u(r):r},y=function(n,t,r){var e=u(t.push(r)-1);return n.set(r,e),e},p=function(n,e){var f=r(n,s).map(l),y=e||a,p=f[0];if(t(p)===c&&p){var v=[],S=function(n,r,e,f){return function(a){for(var l=o(a),s=l.length,y=0;y<s;y++){var p=l[y],v=a[p];if(v instanceof u){var S=n[+v];t(S)!==c||e.has(S)?a[p]=f.call(a,p,S):(e.add(S),a[p]=i,r.push({o:a,k:p,r:S}))}else a[p]!==i&&(a[p]=f.call(a,p,v))}return a}}(f,v,new Set,y);p=S(p);for(var b=0;b<v.length;){var m=v[b++],g=m.o,h=m.k,O=m.r;g[h]=y.call(g,h,S(O))}}return y.call({"":p},"",p)},v=function(n,r,o){for(var u=r&&t(r)===c?function(n,t){return""===n||-1<r.indexOf(n)?t:void 0}:r||a,i=new Map,l=[],s=[],p=+y(i,l,u.call({"":n},"",n)),v=!p;p<l.length;)v=!0,s[p]=e(l[p++],S,o);return"["+s.join(",")+"]";function S(n,r){if(v)return v=!v,r;var e=u.call(this,n,r);switch(t(e)){case c:if(null===e)return e;case f:return i.get(e)||y(i,l,e)}return e}};return n.fromJSON=function(n){return p(e(n))},n.parse=p,n.stringify=v,n.toJSON=function(n){return r(v(n))},n}({});
|
||||
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const promisify = require('es6-promisify');
|
||||
const jayson = require('../../../');
|
||||
const promiseUtils = require('../utils');
|
||||
|
||||
/**
|
||||
* Constructor for a Jayson Promise Client Http
|
||||
* @see Client
|
||||
* @class PromiseClientHttp
|
||||
* @extends ClientHttp
|
||||
* @return {PromiseClientHttp}
|
||||
*/
|
||||
const PromiseClientHttp = function(options) {
|
||||
if(!(this instanceof PromiseClientHttp)) {
|
||||
return new PromiseClientHttp(options);
|
||||
}
|
||||
jayson.Client.http.apply(this, arguments);
|
||||
this.request = promiseUtils.wrapClientRequestMethod(this.request.bind(this));
|
||||
};
|
||||
require('util').inherits(PromiseClientHttp, jayson.Client.http);
|
||||
|
||||
module.exports = PromiseClientHttp;
|
||||
@@ -0,0 +1,84 @@
|
||||
var assert = require('assert');
|
||||
var stringify = require('json-stable-stringify');
|
||||
|
||||
var objectTest = {};
|
||||
for (var i = 35; i < 91; i++) {
|
||||
objectTest[String.fromCharCode(i)] = i;
|
||||
}
|
||||
var objectExpected = stringify(objectTest);
|
||||
|
||||
var names = [];
|
||||
var values = [];
|
||||
|
||||
var objKeys = Object.keys || function(obj) {
|
||||
var keys = [];
|
||||
for (var name in obj) {
|
||||
if (obj[name] !== undefined) {
|
||||
keys.push(name);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
suite('iter', function() {
|
||||
|
||||
var minSamples = 120;
|
||||
|
||||
benchmark('keys-while', function() {
|
||||
// only object is left
|
||||
var val = objectTest;
|
||||
var key;
|
||||
var keys = objKeys(val).sort();
|
||||
var max = keys.length;
|
||||
var str = "";
|
||||
var i = 0;
|
||||
while (i < max) {
|
||||
key = keys[i++];
|
||||
if (val[key] !== undefined) {
|
||||
if (str) {
|
||||
str += ',';
|
||||
}
|
||||
str += '"' + key + '":' + val[key];
|
||||
}
|
||||
}
|
||||
assert.equal('{' + str + '}', objectExpected);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark('keys-for', function() {
|
||||
// only object is left
|
||||
var val = objectTest;
|
||||
var key;
|
||||
var keys = objKeys(val).sort();
|
||||
var max = keys.length;
|
||||
var str = "";
|
||||
var i = 0;
|
||||
for (; i < max; i++) {
|
||||
key = keys[i];
|
||||
if (val[key] !== undefined) {
|
||||
if (str) {
|
||||
str += ',';
|
||||
}
|
||||
str += '"' + key + '":' + val[key];
|
||||
}
|
||||
}
|
||||
assert.equal('{' + str + '}', objectExpected);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
benchmark('incr-for', function() {
|
||||
names.length = 0;
|
||||
values.length = 0;
|
||||
var val = objectTest;
|
||||
var name;
|
||||
var i;
|
||||
var max = -1;
|
||||
for (name in val) {
|
||||
i = max;
|
||||
while (names[i] > name) i--;
|
||||
names.splice(i + 1, 0, name);
|
||||
values.splice(i + 1, 0, '"' + name + '":' + JSON.stringify(val[name]));
|
||||
max++;
|
||||
}
|
||||
assert.equal('{' + values.join(',') + '}', objectExpected);
|
||||
}, { minSamples: minSamples });
|
||||
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@humanfs/core",
|
||||
"version": "0.19.2",
|
||||
"description": "The core of the humanfs library.",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./src/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"src"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepare": "npm run build",
|
||||
"pretest": "npm run build",
|
||||
"test": "c8 mocha tests"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/humanwhocodes/humanfs.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"keywords": [
|
||||
"filesystem",
|
||||
"fs",
|
||||
"hfs",
|
||||
"files"
|
||||
],
|
||||
"author": "Nicholas C. Zakas",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/humanwhocodes/humanfs/issues"
|
||||
},
|
||||
"homepage": "https://github.com/humanwhocodes/humanfs#readme",
|
||||
"engines": {
|
||||
"node": ">=18.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"c8": "^9.0.0",
|
||||
"mocha": "^10.2.0",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@humanfs/types": "^0.15.0"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
import type { LibDefinition } from '../variable';
|
||||
export declare const dom_asynciterable: LibDefinition;
|
||||
@@ -0,0 +1,49 @@
|
||||
/*! *****************************************************************************
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
this file except in compliance with the License. You may obtain a copy of the
|
||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
||||
|
||||
See the Apache Version 2.0 License for specific language governing permissions
|
||||
and limitations under the License.
|
||||
***************************************************************************** */
|
||||
|
||||
|
||||
/// <reference no-default-lib="true"/>
|
||||
|
||||
interface ObjectConstructor {
|
||||
/**
|
||||
* Returns an array of values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
values<T>(o: { [s: string]: T; } | ArrayLike<T>): T[];
|
||||
|
||||
/**
|
||||
* Returns an array of values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
values(o: {}): any[];
|
||||
|
||||
/**
|
||||
* Returns an array of key/values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries<T>(o: { [s: string]: T; } | ArrayLike<T>): [string, T][];
|
||||
|
||||
/**
|
||||
* Returns an array of key/values of the enumerable properties of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
entries(o: {}): [string, any][];
|
||||
|
||||
/**
|
||||
* Returns an object containing all own property descriptors of an object
|
||||
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
|
||||
*/
|
||||
getOwnPropertyDescriptors<T>(o: T): { [P in keyof T]: TypedPropertyDescriptor<T[P]>; } & { [x: string]: PropertyDescriptor; };
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export { decodeOriginalScopes, encodeOriginalScopes, decodeGeneratedRanges, encodeGeneratedRanges, } from './scopes.mts';
|
||||
export type { OriginalScope, GeneratedRange, CallSite, BindingExpressionRange } from './scopes.mts';
|
||||
export type SourceMapSegment = [number] | [number, number, number, number] | [number, number, number, number, number];
|
||||
export type SourceMapLine = SourceMapSegment[];
|
||||
export type SourceMapMappings = SourceMapLine[];
|
||||
export declare function decode(mappings: string): SourceMapMappings;
|
||||
export declare function encode(decoded: SourceMapMappings): string;
|
||||
export declare function encode(decoded: Readonly<SourceMapMappings>): string;
|
||||
//# sourceMappingURL=sourcemap-codec.d.ts.map
|
||||
@@ -0,0 +1,71 @@
|
||||
import {IncomingMessage, ServerResponse} from "http";
|
||||
import {
|
||||
err,
|
||||
errWithCause,
|
||||
req,
|
||||
res,
|
||||
SerializedError,
|
||||
SerializedRequest,
|
||||
wrapErrorSerializer,
|
||||
wrapRequestSerializer,
|
||||
wrapResponseSerializer,
|
||||
SerializedResponse
|
||||
} from '../../';
|
||||
|
||||
const customErrorSerializer = (error: SerializedError) => {
|
||||
return {
|
||||
myOwnError: {
|
||||
data: `${error.type}-${error.message}\n\n${error.stack}`,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const customRequestSerializer = (req: SerializedRequest) => {
|
||||
const {
|
||||
headers,
|
||||
id,
|
||||
method,
|
||||
raw,
|
||||
remoteAddress,
|
||||
remotePort,
|
||||
url,
|
||||
query,
|
||||
params,
|
||||
} = req;
|
||||
return {
|
||||
myOwnRequest: {
|
||||
data: `${method}-${id}-${remoteAddress}-${remotePort}-${url}`,
|
||||
headers,
|
||||
raw,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const customResponseSerializer = (res: SerializedResponse) => {
|
||||
const {headers, raw, statusCode} = res;
|
||||
return {
|
||||
myOwnResponse: {
|
||||
data: statusCode,
|
||||
headers,
|
||||
raw,
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const fakeError = new Error('A fake error for testing');
|
||||
const serializedError: SerializedError = err(fakeError);
|
||||
const mySerializer = wrapErrorSerializer(customErrorSerializer);
|
||||
|
||||
const fakeErrorWithCause = new Error('A fake error for testing with cause', { cause: new Error('An inner fake error') });
|
||||
const serializedErrorWithCause: SerializedError = errWithCause(fakeError);
|
||||
|
||||
const request: IncomingMessage = {} as IncomingMessage
|
||||
const serializedRequest: SerializedRequest = req(request);
|
||||
const myReqSerializer = wrapRequestSerializer(customRequestSerializer);
|
||||
|
||||
const response: ServerResponse = {} as ServerResponse
|
||||
const myResSerializer = wrapResponseSerializer(customResponseSerializer);
|
||||
const serializedResponse = res(response);
|
||||
|
||||
myResSerializer(response)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# shebang-regex [](https://travis-ci.org/sindresorhus/shebang-regex)
|
||||
|
||||
> Regular expression for matching a [shebang](https://en.wikipedia.org/wiki/Shebang_(Unix)) line
|
||||
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install shebang-regex
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const shebangRegex = require('shebang-regex');
|
||||
|
||||
const string = '#!/usr/bin/env node\nconsole.log("unicorns");';
|
||||
|
||||
shebangRegex.test(string);
|
||||
//=> true
|
||||
|
||||
shebangRegex.exec(string)[0];
|
||||
//=> '#!/usr/bin/env node'
|
||||
|
||||
shebangRegex.exec(string)[1];
|
||||
//=> '/usr/bin/env node'
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Sindre Sorhus](https://sindresorhus.com)
|
||||
Reference in New Issue
Block a user