better-call 0.0.0-experimental.06264e12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +254 -0
- package/dist/error.cjs +67 -0
- package/dist/error.cjs.map +1 -0
- package/dist/error.d.cts +46 -0
- package/dist/error.d.mts +46 -0
- package/dist/error.mjs +64 -0
- package/dist/error.mjs.map +1 -0
- package/dist/fn.cjs +335 -0
- package/dist/fn.cjs.map +1 -0
- package/dist/fn.d.cts +312 -0
- package/dist/fn.d.mts +312 -0
- package/dist/fn.mjs +335 -0
- package/dist/fn.mjs.map +1 -0
- package/dist/index.cjs +37 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +46 -0
- package/dist/index.d.mts +46 -0
- package/dist/index.mjs +24 -0
- package/dist/index.mjs.map +1 -0
- package/dist/module.cjs +111 -0
- package/dist/module.cjs.map +1 -0
- package/dist/module.d.cts +249 -0
- package/dist/module.d.mts +249 -0
- package/dist/module.mjs +102 -0
- package/dist/module.mjs.map +1 -0
- package/dist/plugins/http.cjs +185 -0
- package/dist/plugins/http.cjs.map +1 -0
- package/dist/plugins/http.d.cts +1261 -0
- package/dist/plugins/http.d.mts +1261 -0
- package/dist/plugins/http.mjs +175 -0
- package/dist/plugins/http.mjs.map +1 -0
- package/dist/plugins/read-only.cjs +19 -0
- package/dist/plugins/read-only.cjs.map +1 -0
- package/dist/plugins/read-only.d.cts +17 -0
- package/dist/plugins/read-only.d.mts +17 -0
- package/dist/plugins/read-only.mjs +19 -0
- package/dist/plugins/read-only.mjs.map +1 -0
- package/dist/schema.cjs +167 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +225 -0
- package/dist/schema.d.mts +225 -0
- package/dist/schema.mjs +159 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scope.d.cts +18 -0
- package/dist/scope.d.mts +18 -0
- package/dist/storage.cjs +256 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.cts +195 -0
- package/dist/storage.d.mts +195 -0
- package/dist/storage.mjs +253 -0
- package/dist/storage.mjs.map +1 -0
- package/dist/types.d.cts +8 -0
- package/dist/types.d.mts +8 -0
- package/dist/var.cjs +162 -0
- package/dist/var.cjs.map +1 -0
- package/dist/var.d.cts +36 -0
- package/dist/var.d.mts +36 -0
- package/dist/var.mjs +154 -0
- package/dist/var.mjs.map +1 -0
- package/package.json +92 -0
package/dist/module.cjs
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const require_schema = require("./schema.cjs");
|
|
2
|
+
//#region src/module.ts
|
|
3
|
+
/**
|
|
4
|
+
* Does an `on` target hit fn key `key`? Exact match, `*` wildcards
|
|
5
|
+
* ("*", "sign_up.*", "/sign-up/*" - a `*` spans anything), a RegExp, or a
|
|
6
|
+
* list (any member hitting counts).
|
|
7
|
+
*/
|
|
8
|
+
const matchesTarget = (target, key) => {
|
|
9
|
+
if (Array.isArray(target)) return target.some((member) => matchesTarget(member, key));
|
|
10
|
+
if (target instanceof RegExp) return target.test(key);
|
|
11
|
+
const name = target;
|
|
12
|
+
if (name === key) return true;
|
|
13
|
+
if (!name.includes("*")) return false;
|
|
14
|
+
const pattern = name.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
15
|
+
return new RegExp(`^${pattern}$`).test(key);
|
|
16
|
+
};
|
|
17
|
+
const isFn = (value) => typeof value === "function" && value?.$fn === true;
|
|
18
|
+
/**
|
|
19
|
+
* Guard the module list: modules are plain records, and passing a bare
|
|
20
|
+
* fn/var/extension/`on` entry is almost always a mistake (a bare fn would
|
|
21
|
+
* otherwise look like a factory and get CALLED). Fail loudly instead.
|
|
22
|
+
*/
|
|
23
|
+
const resolveModules = (modules) => modules.map((mod) => {
|
|
24
|
+
if (isFn(mod) || require_schema.isVar(mod) || isVarExtension(mod) || isOn(mod)) {
|
|
25
|
+
const bare = mod;
|
|
26
|
+
const name = bare.key ?? bare.name ?? (bare.target !== void 0 ? String(bare.target) : "member");
|
|
27
|
+
throw new Error(`modules are objects - wrap the member: use([{ ${name} }]), not use([${name}])`);
|
|
28
|
+
}
|
|
29
|
+
return mod ?? {};
|
|
30
|
+
});
|
|
31
|
+
/** Collect every exported fn from a set of plugin modules. */
|
|
32
|
+
const collectFns = (modules) => {
|
|
33
|
+
const fns = {};
|
|
34
|
+
for (const mod of resolveModules(modules)) for (const [name, value] of Object.entries(mod)) if (isFn(value)) fns[name] = value;
|
|
35
|
+
return fns;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* A plain-record member that GROUPS other members - not itself branded
|
|
39
|
+
* (fn, var, on entry, var extension) but holding at least one such member,
|
|
40
|
+
* transitively. Storages and other `$`-surfaced objects fail the member
|
|
41
|
+
* test and stay opaque values.
|
|
42
|
+
*/
|
|
43
|
+
const isNamespace = (value) => {
|
|
44
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
45
|
+
const proto = Object.getPrototypeOf(value);
|
|
46
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
47
|
+
if (require_schema.isVar(value) || isOn(value) || isVarExtension(value)) return false;
|
|
48
|
+
return Object.values(value).some((m) => isFn(m) || require_schema.isVar(m) || isOn(m) || isVarExtension(m) || isNamespace(m));
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Every exported fn AND var, with plain-record GROUPS kept as nested
|
|
52
|
+
* namespaces: `use: [{ cookie: { setCookie } }]` lands the fn on
|
|
53
|
+
* `c.cookie.setCookie`. A var member becomes an ALIAS under its export
|
|
54
|
+
* name (`{ cookie: { options: cookieOptions } }` -> `c.cookie.options`
|
|
55
|
+
* reads/writes the var). Groups holding nothing (however deep) drop out.
|
|
56
|
+
*/
|
|
57
|
+
const collectUsable = (modules) => {
|
|
58
|
+
const walk = (mod) => {
|
|
59
|
+
const out = {};
|
|
60
|
+
for (const [name, value] of Object.entries(mod)) if (isFn(value) || require_schema.isVar(value)) out[name] = value;
|
|
61
|
+
else if (isNamespace(value)) {
|
|
62
|
+
const nested = walk(value);
|
|
63
|
+
if (Object.keys(nested).length > 0) out[name] = nested;
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
};
|
|
67
|
+
const fns = {};
|
|
68
|
+
for (const mod of resolveModules(modules)) Object.assign(fns, walk(mod));
|
|
69
|
+
return fns;
|
|
70
|
+
};
|
|
71
|
+
const isOn = (value) => value?.$on === true;
|
|
72
|
+
function on(target, extendOrHandler, maybeHandler) {
|
|
73
|
+
const resolveOne = (member) => isFn(member) ? member.key : member;
|
|
74
|
+
const resolved = Array.isArray(target) ? target.map(resolveOne) : resolveOne(target);
|
|
75
|
+
return typeof extendOrHandler === "function" ? {
|
|
76
|
+
$on: true,
|
|
77
|
+
target: resolved,
|
|
78
|
+
handler: extendOrHandler
|
|
79
|
+
} : {
|
|
80
|
+
$on: true,
|
|
81
|
+
target: resolved,
|
|
82
|
+
extend: extendOrHandler,
|
|
83
|
+
handler: maybeHandler
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const isVarExtension = (value) => value?.$varExtend === true;
|
|
87
|
+
function extendVar(target, schema) {
|
|
88
|
+
return typeof target === "string" ? {
|
|
89
|
+
$varExtend: true,
|
|
90
|
+
name: target,
|
|
91
|
+
schema
|
|
92
|
+
} : {
|
|
93
|
+
$varExtend: true,
|
|
94
|
+
name: target.name,
|
|
95
|
+
schema,
|
|
96
|
+
base: target
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
100
|
+
exports.collectFns = collectFns;
|
|
101
|
+
exports.collectUsable = collectUsable;
|
|
102
|
+
exports.extendVar = extendVar;
|
|
103
|
+
exports.isFn = isFn;
|
|
104
|
+
exports.isNamespace = isNamespace;
|
|
105
|
+
exports.isOn = isOn;
|
|
106
|
+
exports.isVarExtension = isVarExtension;
|
|
107
|
+
exports.matchesTarget = matchesTarget;
|
|
108
|
+
exports.on = on;
|
|
109
|
+
exports.resolveModules = resolveModules;
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=module.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.cjs","names":["isVar"],"sources":["../src/module.ts"],"sourcesContent":["import type { FnDefination } from \"./fn\";\nimport { type InferArgs, type InferInput, isVar, type vTypes } from \"./schema\";\nimport type {\n\tLiteralString,\n\tMembers,\n\tPrettify,\n\tUnionToIntersection,\n} from \"./types\";\nimport type { VarDefination } from \"./var\";\n\nexport type Interceptor = (c: any, next: () => Promise<any>) => any;\n\n/**\n * What a GLOBAL `v.on` handler sees. Without a builder there is no scope\n * to infer, but the context's shape is always the same - so the default\n * is structured with loose leaves, never `any`: `c.input.code` works,\n * `c.tpyo` does not. Mount the entry on a builder's `on` for real types.\n */\nexport type OnDefaultContext = {\n\tinput: Record<string, any>;\n\ttypes: typeof vTypes;\n\tfn: unknown;\n\t/** Vars and used fns live directly on `c` - untyped here; mount the\n\t * entry on a builder's `on` for real types. */\n\t[key: string]: any;\n};\n\nexport type OnEntry<N extends string, Ext = unknown> = {\n\t$on: true;\n\t/** Exact key, a `*` wildcard pattern, a RegExp, or a LIST of any of\n\t * those - one handler mounted on several events at once. */\n\ttarget: N | RegExp | readonly (string | RegExp)[];\n\t/** Extra input fields this mount adds to the target fn. */\n\textend?: { input?: Ext };\n\thandler: Interceptor;\n};\n\n/**\n * Does an `on` target hit fn key `key`? Exact match, `*` wildcards\n * (\"*\", \"sign_up.*\", \"/sign-up/*\" - a `*` spans anything), a RegExp, or a\n * list (any member hitting counts).\n */\nexport const matchesTarget = (\n\ttarget: string | RegExp | readonly (string | RegExp)[],\n\tkey: string,\n): boolean => {\n\tif (Array.isArray(target)) {\n\t\treturn (target as readonly (string | RegExp)[]).some((member) =>\n\t\t\tmatchesTarget(member, key),\n\t\t);\n\t}\n\tif (target instanceof RegExp) return target.test(key);\n\t// Array.isArray cannot narrow a readonly array out of the union.\n\tconst name = target as string;\n\tif (name === key) return true;\n\tif (!name.includes(\"*\")) return false;\n\tconst pattern = name\n\t\t.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\t\t.replace(/\\*/g, \".*\");\n\treturn new RegExp(`^${pattern}$`).test(key);\n};\n\n/**\n * The type-level half of `matchesTarget`, for input extensions: exact\n * keys and single-`*` patterns narrow call sites; RegExp targets (typed\n * `string`) intercept and validate at runtime but cannot narrow.\n */\nexport type TargetMatches<N, K extends string> = N extends \"*\"\n\t? true\n\t: N extends `${infer Pre}*${infer Suf}`\n\t\t? K extends `${Pre}${string}`\n\t\t\t? Suf extends \"\"\n\t\t\t\t? true\n\t\t\t\t: K extends `${string}${Suf}`\n\t\t\t\t\t? true\n\t\t\t\t\t: false\n\t\t\t: false\n\t\t: N extends K\n\t\t\t? true\n\t\t\t: false;\n\n/**\n * A module is the unit of composition: a RECORD of members - vars, fns,\n * `on` entries, var extensions - usually just what a file exports\n * (`import * as twoFactor`) or a curated bundle. \"Plugin\" is not a\n * concept, only a usage: mounting someone else's module. Always an\n * object, never a bare member: `use: [{ createUser }]`, not\n * `use: [createUser]`. The `never` marks reject bare members at the type\n * level; `resolveModules` rejects them at runtime.\n */\nexport type Module = Record<string, unknown> & {\n\t$var?: never;\n\t$varExtend?: never;\n\t/** Rejects a bare `on` ENTRY (`$on: true`) while letting a storage\n\t * through - its `$on` is the hook-mounting METHOD, not the brand. */\n\t$on?: (...args: never[]) => unknown;\n\t$fn?: never;\n};\n\n/** A module member that can NEST other members: a plain record that is\n * not itself branded. Fn defs are callable, so they never match. */\ntype GroupMember<V> = V extends\n\t| { $var: true }\n\t| { $on: true }\n\t| { $varExtend: true }\n\t| ((...args: any[]) => any)\n\t| readonly unknown[]\n\t? never\n\t: V extends Record<string, unknown>\n\t\t? V\n\t\t: never;\n\ntype VarEntryUnion<M> = {\n\t[K in keyof M]: M[K] extends VarDefination<infer N, infer T, any, any>\n\t\t? { [P in N]: T }\n\t\t: M[K] extends VarExtension<infer N, any, infer BT>\n\t\t\t? unknown extends BT\n\t\t\t\t? never\n\t\t\t\t: { [P in N]: BT }\n\t\t\t: [GroupMember<M[K]>] extends [never]\n\t\t\t\t? never\n\t\t\t\t: VarEntryUnion<GroupMember<M[K]>>;\n}[keyof M];\n\n/**\n * Vars a module exports, keyed by their DECLARED name, not export name.\n * Distributes over `M`: without that, a union of modules collapses to\n * `keyof A & keyof B` and every var is lost. The `[never]` guard matters\n * too - `UnionToIntersection<never>` is `unknown`, and one var-less module\n * in the union would absorb every other module's vars into `unknown`.\n */\nexport type VarsFrom<M> = M extends unknown\n\t? [VarEntryUnion<M>] extends [never]\n\t\t? never\n\t\t: UnionToIntersection<VarEntryUnion<M>>\n\t: never;\n\nexport type ModuleVars<PL> = UnionToIntersection<VarsFrom<Members<PL>>>;\n\n/** Depth budget for nested groups: unbranded records can be arbitrarily\n * deep (schema shapes, values), so the walk stops after a few levels\n * instead of blowing the instantiation limit. */\ntype GroupDepth = [never, 0, 1, 2];\n\n/** A nested GROUP's fns: only real groups holding at least one fn\n * (within the depth budget) survive - everything else drops from the\n * surface. */\ntype GroupFns<V, D extends number> = [D] extends [never]\n\t? never\n\t: [GroupMember<V>] extends [never]\n\t\t? never\n\t\t: [keyof FnEntries<GroupMember<V>, D>] extends [never]\n\t\t\t? never\n\t\t\t: FnEntries<GroupMember<V>, D>;\n\ntype FnEntries<M, D extends number = 3> = {\n\t[K in keyof M as M[K] extends FnDefination<any, any, any, any, any, any>\n\t\t? K\n\t\t: M[K] extends VarDefination<any, any, any, any>\n\t\t\t? K\n\t\t\t: [GroupFns<M[K], GroupDepth[D]>] extends [never]\n\t\t\t\t? never\n\t\t\t\t: K]: M[K] extends FnDefination<any, any, any, any, any, any>\n\t\t? M[K]\n\t\t: M[K] extends VarDefination<any, any, any, any>\n\t\t\t? M[K]\n\t\t\t: GroupFns<M[K], GroupDepth[D]>;\n};\n\n/** Fns and vars a module exports, keyed by EXPORT name. A plain-record\n * member is a GROUP: its members stay nested under the member's name\n * (`use: [{ cookie: { setCookie } }]` -> `c.cookie.setCookie`). A var\n * member is an ALIAS onto the var - `{ cookie: { options: cookieOptions } }`\n * reads and writes the `cookieOptions` var through `c.cookie.options`. */\nexport type FnsFrom<M> = M extends unknown ? FnEntries<M> : never;\n\nexport type ModuleFns<PL> = UnionToIntersection<FnsFrom<Members<PL>>>;\n\nexport const isFn = (value: any): value is FnDefination<any, any> =>\n\ttypeof value === \"function\" && value?.$fn === true;\n\n/**\n * Guard the module list: modules are plain records, and passing a bare\n * fn/var/extension/`on` entry is almost always a mistake (a bare fn would\n * otherwise look like a factory and get CALLED). Fail loudly instead.\n */\nexport const resolveModules = (\n\tmodules: readonly Module[],\n): Record<string, unknown>[] =>\n\tmodules.map((mod) => {\n\t\tif (isFn(mod) || isVar(mod) || isVarExtension(mod) || isOn(mod)) {\n\t\t\tconst bare = mod as { key?: string; name?: string; target?: unknown };\n\t\t\tconst name =\n\t\t\t\tbare.key ??\n\t\t\t\tbare.name ??\n\t\t\t\t(bare.target !== undefined ? String(bare.target) : \"member\");\n\t\t\tthrow new Error(\n\t\t\t\t`modules are objects - wrap the member: use([{ ${name} }]), not use([${name}])`,\n\t\t\t);\n\t\t}\n\t\treturn (mod ?? {}) as Record<string, unknown>;\n\t});\n\n/** Collect every exported fn from a set of plugin modules. */\nexport const collectFns = (\n\tmodules: readonly Module[],\n): Record<string, FnDefination<any, any>> => {\n\tconst fns: Record<string, FnDefination<any, any>> = {};\n\tfor (const mod of resolveModules(modules)) {\n\t\tfor (const [name, value] of Object.entries(mod)) {\n\t\t\tif (isFn(value)) fns[name] = value;\n\t\t}\n\t}\n\treturn fns;\n};\n\n/**\n * A plain-record member that GROUPS other members - not itself branded\n * (fn, var, on entry, var extension) but holding at least one such member,\n * transitively. Storages and other `$`-surfaced objects fail the member\n * test and stay opaque values.\n */\nexport const isNamespace = (\n\tvalue: unknown,\n): value is Record<string, unknown> => {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\treturn false;\n\t}\n\tconst proto = Object.getPrototypeOf(value);\n\tif (proto !== Object.prototype && proto !== null) return false;\n\tif (isVar(value) || isOn(value) || isVarExtension(value)) return false;\n\treturn Object.values(value).some(\n\t\t(m) =>\n\t\t\tisFn(m) || isVar(m) || isOn(m) || isVarExtension(m) || isNamespace(m),\n\t);\n};\n\n/**\n * Every exported fn AND var, with plain-record GROUPS kept as nested\n * namespaces: `use: [{ cookie: { setCookie } }]` lands the fn on\n * `c.cookie.setCookie`. A var member becomes an ALIAS under its export\n * name (`{ cookie: { options: cookieOptions } }` -> `c.cookie.options`\n * reads/writes the var). Groups holding nothing (however deep) drop out.\n */\nexport const collectUsable = (\n\tmodules: readonly Module[],\n): Record<string, unknown> => {\n\tconst walk = (mod: Record<string, unknown>): Record<string, unknown> => {\n\t\tconst out: Record<string, unknown> = {};\n\t\tfor (const [name, value] of Object.entries(mod)) {\n\t\t\tif (isFn(value) || isVar(value)) {\n\t\t\t\tout[name] = value;\n\t\t\t} else if (isNamespace(value)) {\n\t\t\t\tconst nested = walk(value);\n\t\t\t\tif (Object.keys(nested).length > 0) out[name] = nested;\n\t\t\t}\n\t\t}\n\t\treturn out;\n\t};\n\tconst fns: Record<string, unknown> = {};\n\tfor (const mod of resolveModules(modules)) Object.assign(fns, walk(mod));\n\treturn fns;\n};\n\nexport const isOn = (value: any): value is OnEntry<string> =>\n\tvalue?.$on === true;\n\n/**\n * Mount onto another fn by name. The handler replaces that fn's body and\n * receives `next` - call it to delegate, or don't.\n *\n * Sounder than a before/after hook: it has the target's signature, runs\n * INSIDE the target's contract checks (so skipping `next` still has to\n * satisfy `provides`), and composes as a stack.\n *\n * The middle argument extends the target's INPUT: those fields are\n * validated alongside the fn's own, land on `c.input`, and - through\n * `ApplyOns` - become required at every call site that mounts this entry.\n */\n/** The var name a `var.set.…`/`var.get.…` target addresses - literal when\n * exact. */\ntype VarNameOf<T extends string> = T extends `var.${\"set\" | \"get\"}.${infer N}`\n\t? N extends `${string}*${string}`\n\t\t? string\n\t\t: N\n\t: string;\n\n/** What a `var.set.…` handler receives. Writes are synchronous, so the\n * handler must be too - call `next()` to let the write land, skip it to\n * cancel, throw to abort. */\nexport type VarSetContext<N extends string = string> = {\n\t/** The var being written. */\n\tname: N;\n\t/** The incoming value. */\n\tvalue: unknown;\n\t/** The fn frame performing the write. */\n\tfn: string;\n\t/** Every other property reads the scope's vars directly. */\n\t[key: string]: unknown;\n};\n\n/** What a `var.get.…` handler receives. Reads are synchronous, so the\n * handler must be too - `next()` yields the stored value and whatever the\n * handler returns becomes the read result. */\nexport type VarGetContext<N extends string = string> = {\n\t/** The var being read. */\n\tname: N;\n\t/** The fn frame performing the read. */\n\tfn: string;\n\t/** Every other property reads the scope's vars (raw - no get hooks). */\n\t[key: string]: unknown;\n};\n\ntype FnInputOf<F> =\n\tF extends FnDefination<any, any, any, infer I, any, any>\n\t\t? InferInput<I>\n\t\t: never;\n\ntype FnResultOf<F> =\n\tF extends FnDefination<any, infer R, any, any, any, any> ? Awaited<R> : never;\n\nexport function on<T extends \"var.set.*\" | `var.set.${string}`>(\n\ttarget: T,\n\thandler: (c: VarSetContext<VarNameOf<T>>, next: () => void) => void,\n): OnEntry<T>;\nexport function on<T extends \"var.get.*\" | `var.get.${string}`>(\n\ttarget: T,\n\thandler: (c: VarGetContext<VarNameOf<T>>, next: () => unknown) => unknown,\n): OnEntry<T>;\nexport function on<F extends FnDefination<any, any, string, any, any, any>>(\n\ttarget: F,\n\thandler: (\n\t\tc: Omit<OnDefaultContext, \"input\"> & { input: FnInputOf<F> },\n\t\tnext: () => Promise<FnResultOf<F>>,\n\t) => any,\n): OnEntry<F[\"key\"]>;\nexport function on<\n\tF extends FnDefination<any, any, string, any, any, any>,\n\tconst Ext,\n>(\n\ttarget: F,\n\textend: { input: Ext },\n\thandler: (\n\t\tc: Omit<OnDefaultContext, \"input\"> & {\n\t\t\tinput: FnInputOf<F> & InferInput<Ext>;\n\t\t},\n\t\tnext: () => Promise<FnResultOf<F>>,\n\t) => any,\n): OnEntry<F[\"key\"], Ext>;\nexport function on(\n\ttargets: readonly (\n\t\t| LiteralString\n\t\t| RegExp\n\t\t| FnDefination<any, any, string, any, any, any>\n\t)[],\n\thandler: (c: OnDefaultContext, next: () => Promise<any>) => any,\n): OnEntry<string>;\nexport function on(\n\ttarget: RegExp,\n\thandler: (c: OnDefaultContext, next: () => Promise<any>) => any,\n): OnEntry<string>;\nexport function on<N extends \"*\" | LiteralString>(\n\ttarget: N,\n\thandler: (c: OnDefaultContext, next: () => Promise<any>) => any,\n): OnEntry<N>;\nexport function on<const Ext>(\n\ttarget: RegExp,\n\textend: { input: Ext },\n\thandler: (\n\t\tc: Omit<OnDefaultContext, \"input\"> & {\n\t\t\tinput: InferInput<Ext> & Record<string, any>;\n\t\t},\n\t\tnext: () => Promise<any>,\n\t) => any,\n): OnEntry<string, Ext>;\nexport function on<N extends LiteralString, const Ext>(\n\ttarget: N,\n\textend: { input: Ext },\n\thandler: (\n\t\tc: Omit<OnDefaultContext, \"input\"> & {\n\t\t\tinput: InferInput<Ext> & Record<string, any>;\n\t\t},\n\t\tnext: () => Promise<any>,\n\t) => any,\n): OnEntry<N, Ext>;\nexport function on(\n\ttarget:\n\t\t| string\n\t\t| RegExp\n\t\t| FnDefination<any, any, string, any, any, any>\n\t\t| readonly (\n\t\t\t\t| string\n\t\t\t\t| RegExp\n\t\t\t\t| FnDefination<any, any, string, any, any, any>\n\t\t )[],\n\textendOrHandler: any,\n\tmaybeHandler?: any,\n): OnEntry<string, any> {\n\t// A fn reference targets its own key - no string to typo. A LIST\n\t// resolves member-wise: one handler, several events.\n\tconst resolveOne = (member: unknown) =>\n\t\tisFn(member)\n\t\t\t? (member as { key: string }).key\n\t\t\t: (member as string | RegExp);\n\tconst resolved = (\n\t\tArray.isArray(target) ? target.map(resolveOne) : resolveOne(target)\n\t) as string | RegExp | readonly (string | RegExp)[];\n\treturn typeof extendOrHandler === \"function\"\n\t\t? { $on: true, target: resolved, handler: extendOrHandler }\n\t\t: {\n\t\t\t\t$on: true,\n\t\t\t\ttarget: resolved,\n\t\t\t\textend: extendOrHandler,\n\t\t\t\thandler: maybeHandler,\n\t\t\t};\n}\n\n/* ----------------------------- var extension ------------------------------ */\n\nexport type VarExtension<N extends string, S, BaseT = unknown> = {\n\t$varExtend: true;\n\tname: N;\n\tschema: S;\n\t/** The var being extended, when handed by reference. */\n\tbase?: VarDefination<N, BaseT, any, any>;\n};\n\nexport const isVarExtension = (\n\tvalue: any,\n): value is VarExtension<string, any> => value?.$varExtend === true;\n\n/**\n * Extend a var's shape from a module. Where `customize` mints a NEW var\n * definition to re-export, an extension is a mountable value: every scope\n * or fn that `use`s the module containing it sees the var widened, and\n * nothing that doesn't mount it is affected.\n *\n * Handed the var by REFERENCE, the extension carries it: mounting just\n * the extension brings the var itself, merged by its declared key - no\n * need to also mount the base var.\n */\nexport function extendVar<N extends LiteralString, S, BaseT>(\n\ttarget: VarDefination<N, BaseT, any, any>,\n\tschema: S,\n): VarExtension<N, S, BaseT>;\nexport function extendVar<N extends LiteralString, S>(\n\ttarget: N,\n\tschema: S,\n): VarExtension<N, S>;\nexport function extendVar(\n\ttarget: VarDefination<string, any, any, any> | string,\n\tschema: any,\n): VarExtension<string, any, any> {\n\treturn typeof target === \"string\"\n\t\t? { $varExtend: true, name: target, schema }\n\t\t: { $varExtend: true, name: target.name, schema, base: target };\n}\n\ntype VarExtEntry<M, K extends string> = M extends unknown\n\t? {\n\t\t\t[P in keyof M]: M[P] extends VarExtension<K, infer S>\n\t\t\t\t? InferInput<S>\n\t\t\t\t: never;\n\t\t}[keyof M]\n\t: never;\n\n/**\n * Shape additions modules in `PL` mount on var `K`. Resolves to `unknown`\n * when none - which intersects away harmlessly.\n */\nexport type VarExtensionsFor<PL, K extends string> = UnionToIntersection<\n\tVarExtEntry<Members<PL>, K>\n>;\n\ntype VarExtArgsEntry<M, K extends string> = M extends unknown\n\t? {\n\t\t\t[P in keyof M]: M[P] extends VarExtension<K, infer S>\n\t\t\t\t? InferArgs<S>\n\t\t\t\t: never;\n\t\t}[keyof M]\n\t: never;\n\n/** The ARGS side of the same extensions - what a caller must send. */\nexport type VarExtensionArgsFor<PL, K extends string> = UnionToIntersection<\n\tVarExtArgsEntry<Members<PL>, K>\n>;\n\n/** The args side of every VAR named `K` a module set declares - a\n * `customize`d re-export shadows by NAME, so mounting it counts as a\n * declaration about the same var. Schema-less vars contribute nothing. */\ntype VarShadowArgsEntry<M, K extends string> = M extends unknown\n\t? {\n\t\t\t[P in keyof M]: M[P] extends VarDefination<K, any, infer S, any>\n\t\t\t\t? unknown extends S\n\t\t\t\t\t? never\n\t\t\t\t\t: [NonNullable<S>] extends [never]\n\t\t\t\t\t\t? never\n\t\t\t\t\t\t: InferArgs<NonNullable<S>>\n\t\t\t\t: never;\n\t\t}[keyof M]\n\t: never;\n\n/**\n * Everything a module set says about var `K`'s ARGS side: extensions\n * mounted on it, intersected with every same-name var declaration. This is\n * what widens a fn schema whose input references `K` (see `WidenSchemaFns`)\n * - `unknown` when the scope adds nothing, which intersects away.\n */\nexport type VarArgsInScope<PL, K extends string> = VarExtensionArgsFor<PL, K> &\n\t([VarShadowArgsEntry<Members<PL>, K>] extends [never]\n\t\t? unknown\n\t\t: UnionToIntersection<VarShadowArgsEntry<Members<PL>, K>>);\n\ntype VarFieldKeys<I> = {\n\t[K in keyof I]: I[K] extends { $var: true } ? K : never;\n}[keyof I];\n\n/**\n * Extra args a fn must accept because its INPUT references vars that `PL`\n * extends. Whole-var input (`input: user`) merges at the top level; a var\n * used as a field widens that field. `unknown` when nothing applies.\n */\nexport type InputVarExtra<PL, I> = I extends {\n\t$var: true;\n\tname: infer N extends string;\n}\n\t? VarExtensionArgsFor<PL, N>\n\t: [VarFieldKeys<I>] extends [never]\n\t\t? unknown\n\t\t: {\n\t\t\t\t[K in keyof I as I[K] extends { $var: true }\n\t\t\t\t\t? K\n\t\t\t\t\t: never]: I[K] extends {\n\t\t\t\t\t$var: true;\n\t\t\t\t\tname: infer N extends string;\n\t\t\t\t}\n\t\t\t\t\t? VarExtensionArgsFor<PL, N>\n\t\t\t\t\t: unknown;\n\t\t\t};\n\n/* ------------------------------- extension -------------------------------- */\n\ntype ExtEntryArgs<M, K extends string> = M extends unknown\n\t? {\n\t\t\t[P in keyof M]: M[P] extends OnEntry<infer N, infer E>\n\t\t\t\t? unknown extends E\n\t\t\t\t\t? never\n\t\t\t\t\t: TargetMatches<N, K> extends true\n\t\t\t\t\t\t? InferArgs<E>\n\t\t\t\t\t\t: never\n\t\t\t\t: never;\n\t\t}[keyof M]\n\t: never;\n\n/** Args added onto fn `K` by every extending `on` entry in `PL`. */\nexport type ExtendedArgs<PL, K extends string> = UnionToIntersection<\n\tExtEntryArgs<Members<PL>, K>\n>;\n\n/**\n * Rewrite a fn's type with the input extensions the module set `PL`\n * mounts on it. This is how a plugin's extra field becomes REQUIRED at\n * the call site, even though the fn itself never declared it.\n */\nexport type ApplyOn<F, PL> =\n\tF extends FnDefination<\n\t\tinfer A,\n\t\tinfer R,\n\t\tinfer K,\n\t\tinfer I,\n\t\tinfer P,\n\t\tinfer Er,\n\t\tinfer W,\n\t\tinfer O\n\t>\n\t\t? unknown extends ExtendedArgs<PL, K & string> & InputVarExtra<PL, I>\n\t\t\t? F\n\t\t\t: FnDefination<\n\t\t\t\t\tPrettify<\n\t\t\t\t\t\t([A] extends [void] ? unknown : A) &\n\t\t\t\t\t\t\tExtendedArgs<PL, K & string> &\n\t\t\t\t\t\t\tInputVarExtra<PL, I>\n\t\t\t\t\t>,\n\t\t\t\t\tR,\n\t\t\t\t\tK & string,\n\t\t\t\t\tI,\n\t\t\t\t\tP,\n\t\t\t\t\tEr,\n\t\t\t\t\tW,\n\t\t\t\t\tO\n\t\t\t\t>\n\t\t: F;\n\nexport type ApplyOns<Fns, PL> = { [P in keyof Fns]: ApplyOn<Fns[P], PL> };\n\n/* -------------------------------- derived --------------------------------- */\n\ntype DerivedNamesEntry<Mod, Q> = Mod extends unknown\n\t? {\n\t\t\t[K in keyof Mod]: Mod[K] extends VarDefination<\n\t\t\t\tinfer N,\n\t\t\t\tany,\n\t\t\t\tany,\n\t\t\t\tinfer Src\n\t\t\t>\n\t\t\t\t? [Src] extends [never]\n\t\t\t\t\t? never\n\t\t\t\t\t: Src extends Q\n\t\t\t\t\t\t? N\n\t\t\t\t\t\t: never\n\t\t\t\t: never;\n\t\t}[keyof Mod]\n\t: never;\n\n/**\n * Expand a `requires` set with every var DERIVED from a required source:\n * requiring `request` makes `method`, `path`, ... non-null too, because\n * they are computed from it.\n */\nexport type WithDerived<PL, BasePL, Q> =\n\t| Q\n\t| DerivedNamesEntry<Members<PL> | Members<BasePL>, Q>;\n"],"mappings":";;;;;;;AA0CA,MAAa,iBACZ,QACA,QACa;CACb,IAAI,MAAM,QAAQ,MAAM,GACvB,OAAQ,OAAwC,MAAM,WACrD,cAAc,QAAQ,GAAG,CAC1B;CAED,IAAI,kBAAkB,QAAQ,OAAO,OAAO,KAAK,GAAG;CAEpD,MAAM,OAAO;CACb,IAAI,SAAS,KAAK,OAAO;CACzB,IAAI,CAAC,KAAK,SAAS,GAAG,GAAG,OAAO;CAChC,MAAM,UAAU,KACd,QAAQ,sBAAsB,MAAM,CAAC,CACrC,QAAQ,OAAO,IAAI;CACrB,OAAO,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC,KAAK,GAAG;AAC3C;AAsHA,MAAa,QAAQ,UACpB,OAAO,UAAU,cAAc,OAAO,QAAQ;;;;;;AAO/C,MAAa,kBACZ,YAEA,QAAQ,KAAK,QAAQ;CACpB,IAAI,KAAK,GAAG,KAAKA,eAAAA,MAAM,GAAG,KAAK,eAAe,GAAG,KAAK,KAAK,GAAG,GAAG;EAChE,MAAM,OAAO;EACb,MAAM,OACL,KAAK,OACL,KAAK,SACJ,KAAK,WAAW,KAAA,IAAY,OAAO,KAAK,MAAM,IAAI;EACpD,MAAM,IAAI,MACT,iDAAiD,KAAK,iBAAiB,KAAK,GAC7E;CACD;CACA,OAAQ,OAAO,CAAC;AACjB,CAAC;;AAGF,MAAa,cACZ,YAC4C;CAC5C,MAAM,MAA8C,CAAC;CACrD,KAAK,MAAM,OAAO,eAAe,OAAO,GACvC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,GAAG,GAC7C,IAAI,KAAK,KAAK,GAAG,IAAI,QAAQ;CAG/B,OAAO;AACR;;;;;;;AAQA,MAAa,eACZ,UACsC;CACtC,IAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GACrE,OAAO;CAER,MAAM,QAAQ,OAAO,eAAe,KAAK;CACzC,IAAI,UAAU,OAAO,aAAa,UAAU,MAAM,OAAO;CACzD,IAAIA,eAAAA,MAAM,KAAK,KAAK,KAAK,KAAK,KAAK,eAAe,KAAK,GAAG,OAAO;CACjE,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,MAC1B,MACA,KAAK,CAAC,KAAKA,eAAAA,MAAM,CAAC,KAAK,KAAK,CAAC,KAAK,eAAe,CAAC,KAAK,YAAY,CAAC,CACtE;AACD;;;;;;;;AASA,MAAa,iBACZ,YAC6B;CAC7B,MAAM,QAAQ,QAA0D;EACvE,MAAM,MAA+B,CAAC;EACtC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,GAAG,GAC7C,IAAI,KAAK,KAAK,KAAKA,eAAAA,MAAM,KAAK,GAC7B,IAAI,QAAQ;OACN,IAAI,YAAY,KAAK,GAAG;GAC9B,MAAM,SAAS,KAAK,KAAK;GACzB,IAAI,OAAO,KAAK,MAAM,CAAC,CAAC,SAAS,GAAG,IAAI,QAAQ;EACjD;EAED,OAAO;CACR;CACA,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,OAAO,eAAe,OAAO,GAAG,OAAO,OAAO,KAAK,KAAK,GAAG,CAAC;CACvE,OAAO;AACR;AAEA,MAAa,QAAQ,UACpB,OAAO,QAAQ;AAwHhB,SAAgB,GACf,QASA,iBACA,cACuB;CAGvB,MAAM,cAAc,WACnB,KAAK,MAAM,IACP,OAA2B,MAC3B;CACL,MAAM,WACL,MAAM,QAAQ,MAAM,IAAI,OAAO,IAAI,UAAU,IAAI,WAAW,MAAM;CAEnE,OAAO,OAAO,oBAAoB,aAC/B;EAAE,KAAK;EAAM,QAAQ;EAAU,SAAS;CAAgB,IACxD;EACA,KAAK;EACL,QAAQ;EACR,QAAQ;EACR,SAAS;CACV;AACH;AAYA,MAAa,kBACZ,UACwC,OAAO,eAAe;AAoB/D,SAAgB,UACf,QACA,QACiC;CACjC,OAAO,OAAO,WAAW,WACtB;EAAE,YAAY;EAAM,MAAM;EAAQ;CAAO,IACzC;EAAE,YAAY;EAAM,MAAM,OAAO;EAAM;EAAQ,MAAM;CAAO;AAChE"}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { LiteralString, Members, Prettify, UnionToIntersection } from "./types.cjs";
|
|
2
|
+
import { InferArgs, InferInput, vTypes } from "./schema.cjs";
|
|
3
|
+
import { VarDefination } from "./var.cjs";
|
|
4
|
+
import { FnDefination } from "./fn.cjs";
|
|
5
|
+
//#region src/module.d.ts
|
|
6
|
+
type Interceptor = (c: any, next: () => Promise<any>) => any;
|
|
7
|
+
/**
|
|
8
|
+
* What a GLOBAL `v.on` handler sees. Without a builder there is no scope
|
|
9
|
+
* to infer, but the context's shape is always the same - so the default
|
|
10
|
+
* is structured with loose leaves, never `any`: `c.input.code` works,
|
|
11
|
+
* `c.tpyo` does not. Mount the entry on a builder's `on` for real types.
|
|
12
|
+
*/
|
|
13
|
+
type OnDefaultContext = {
|
|
14
|
+
input: Record<string, any>;
|
|
15
|
+
types: typeof vTypes;
|
|
16
|
+
fn: unknown;
|
|
17
|
+
/** Vars and used fns live directly on `c` - untyped here; mount the
|
|
18
|
+
* entry on a builder's `on` for real types. */
|
|
19
|
+
[key: string]: any;
|
|
20
|
+
};
|
|
21
|
+
type OnEntry<N extends string, Ext = unknown> = {
|
|
22
|
+
$on: true;
|
|
23
|
+
/** Exact key, a `*` wildcard pattern, a RegExp, or a LIST of any of
|
|
24
|
+
* those - one handler mounted on several events at once. */
|
|
25
|
+
target: N | RegExp | readonly (string | RegExp)[];
|
|
26
|
+
/** Extra input fields this mount adds to the target fn. */
|
|
27
|
+
extend?: {
|
|
28
|
+
input?: Ext;
|
|
29
|
+
};
|
|
30
|
+
handler: Interceptor;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The type-level half of `matchesTarget`, for input extensions: exact
|
|
34
|
+
* keys and single-`*` patterns narrow call sites; RegExp targets (typed
|
|
35
|
+
* `string`) intercept and validate at runtime but cannot narrow.
|
|
36
|
+
*/
|
|
37
|
+
type TargetMatches<N, K extends string> = N extends "*" ? true : N extends `${infer Pre}*${infer Suf}` ? K extends `${Pre}${string}` ? Suf extends "" ? true : K extends `${string}${Suf}` ? true : false : false : N extends K ? true : false;
|
|
38
|
+
/**
|
|
39
|
+
* A module is the unit of composition: a RECORD of members - vars, fns,
|
|
40
|
+
* `on` entries, var extensions - usually just what a file exports
|
|
41
|
+
* (`import * as twoFactor`) or a curated bundle. "Plugin" is not a
|
|
42
|
+
* concept, only a usage: mounting someone else's module. Always an
|
|
43
|
+
* object, never a bare member: `use: [{ createUser }]`, not
|
|
44
|
+
* `use: [createUser]`. The `never` marks reject bare members at the type
|
|
45
|
+
* level; `resolveModules` rejects them at runtime.
|
|
46
|
+
*/
|
|
47
|
+
type Module = Record<string, unknown> & {
|
|
48
|
+
$var?: never;
|
|
49
|
+
$varExtend?: never;
|
|
50
|
+
/** Rejects a bare `on` ENTRY (`$on: true`) while letting a storage
|
|
51
|
+
* through - its `$on` is the hook-mounting METHOD, not the brand. */
|
|
52
|
+
$on?: (...args: never[]) => unknown;
|
|
53
|
+
$fn?: never;
|
|
54
|
+
};
|
|
55
|
+
/** A module member that can NEST other members: a plain record that is
|
|
56
|
+
* not itself branded. Fn defs are callable, so they never match. */
|
|
57
|
+
type GroupMember<V> = V extends {
|
|
58
|
+
$var: true;
|
|
59
|
+
} | {
|
|
60
|
+
$on: true;
|
|
61
|
+
} | {
|
|
62
|
+
$varExtend: true;
|
|
63
|
+
} | ((...args: any[]) => any) | readonly unknown[] ? never : V extends Record<string, unknown> ? V : never;
|
|
64
|
+
type VarEntryUnion<M> = { [K in keyof M]: M[K] extends VarDefination<infer N, infer T, any, any> ? { [P in N]: T; } : M[K] extends VarExtension<infer N, any, infer BT> ? unknown extends BT ? never : { [P in N]: BT; } : [GroupMember<M[K]>] extends [never] ? never : VarEntryUnion<GroupMember<M[K]>>; }[keyof M];
|
|
65
|
+
/**
|
|
66
|
+
* Vars a module exports, keyed by their DECLARED name, not export name.
|
|
67
|
+
* Distributes over `M`: without that, a union of modules collapses to
|
|
68
|
+
* `keyof A & keyof B` and every var is lost. The `[never]` guard matters
|
|
69
|
+
* too - `UnionToIntersection<never>` is `unknown`, and one var-less module
|
|
70
|
+
* in the union would absorb every other module's vars into `unknown`.
|
|
71
|
+
*/
|
|
72
|
+
type VarsFrom<M> = M extends unknown ? [VarEntryUnion<M>] extends [never] ? never : UnionToIntersection<VarEntryUnion<M>> : never;
|
|
73
|
+
type ModuleVars<PL> = UnionToIntersection<VarsFrom<Members<PL>>>;
|
|
74
|
+
/** Depth budget for nested groups: unbranded records can be arbitrarily
|
|
75
|
+
* deep (schema shapes, values), so the walk stops after a few levels
|
|
76
|
+
* instead of blowing the instantiation limit. */
|
|
77
|
+
type GroupDepth = [never, 0, 1, 2];
|
|
78
|
+
/** A nested GROUP's fns: only real groups holding at least one fn
|
|
79
|
+
* (within the depth budget) survive - everything else drops from the
|
|
80
|
+
* surface. */
|
|
81
|
+
type GroupFns<V, D extends number> = [D] extends [never] ? never : [GroupMember<V>] extends [never] ? never : [keyof FnEntries<GroupMember<V>, D>] extends [never] ? never : FnEntries<GroupMember<V>, D>;
|
|
82
|
+
type FnEntries<M, D extends number = 3> = { [K in keyof M as M[K] extends FnDefination<any, any, any, any, any, any> ? K : M[K] extends VarDefination<any, any, any, any> ? K : [GroupFns<M[K], GroupDepth[D]>] extends [never] ? never : K]: M[K] extends FnDefination<any, any, any, any, any, any> ? M[K] : M[K] extends VarDefination<any, any, any, any> ? M[K] : GroupFns<M[K], GroupDepth[D]>; };
|
|
83
|
+
/** Fns and vars a module exports, keyed by EXPORT name. A plain-record
|
|
84
|
+
* member is a GROUP: its members stay nested under the member's name
|
|
85
|
+
* (`use: [{ cookie: { setCookie } }]` -> `c.cookie.setCookie`). A var
|
|
86
|
+
* member is an ALIAS onto the var - `{ cookie: { options: cookieOptions } }`
|
|
87
|
+
* reads and writes the `cookieOptions` var through `c.cookie.options`. */
|
|
88
|
+
type FnsFrom<M> = M extends unknown ? FnEntries<M> : never;
|
|
89
|
+
type ModuleFns<PL> = UnionToIntersection<FnsFrom<Members<PL>>>;
|
|
90
|
+
declare const isFn: (value: any) => value is FnDefination<any, any>;
|
|
91
|
+
/** Collect every exported fn from a set of plugin modules. */
|
|
92
|
+
declare const collectFns: (modules: readonly Module[]) => Record<string, FnDefination<any, any>>;
|
|
93
|
+
/**
|
|
94
|
+
* A plain-record member that GROUPS other members - not itself branded
|
|
95
|
+
* (fn, var, on entry, var extension) but holding at least one such member,
|
|
96
|
+
* transitively. Storages and other `$`-surfaced objects fail the member
|
|
97
|
+
* test and stay opaque values.
|
|
98
|
+
*/
|
|
99
|
+
declare const isNamespace: (value: unknown) => value is Record<string, unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Every exported fn AND var, with plain-record GROUPS kept as nested
|
|
102
|
+
* namespaces: `use: [{ cookie: { setCookie } }]` lands the fn on
|
|
103
|
+
* `c.cookie.setCookie`. A var member becomes an ALIAS under its export
|
|
104
|
+
* name (`{ cookie: { options: cookieOptions } }` -> `c.cookie.options`
|
|
105
|
+
* reads/writes the var). Groups holding nothing (however deep) drop out.
|
|
106
|
+
*/
|
|
107
|
+
declare const collectUsable: (modules: readonly Module[]) => Record<string, unknown>;
|
|
108
|
+
declare const isOn: (value: any) => value is OnEntry<string>;
|
|
109
|
+
/**
|
|
110
|
+
* Mount onto another fn by name. The handler replaces that fn's body and
|
|
111
|
+
* receives `next` - call it to delegate, or don't.
|
|
112
|
+
*
|
|
113
|
+
* Sounder than a before/after hook: it has the target's signature, runs
|
|
114
|
+
* INSIDE the target's contract checks (so skipping `next` still has to
|
|
115
|
+
* satisfy `provides`), and composes as a stack.
|
|
116
|
+
*
|
|
117
|
+
* The middle argument extends the target's INPUT: those fields are
|
|
118
|
+
* validated alongside the fn's own, land on `c.input`, and - through
|
|
119
|
+
* `ApplyOns` - become required at every call site that mounts this entry.
|
|
120
|
+
*/
|
|
121
|
+
/** The var name a `var.set.…`/`var.get.…` target addresses - literal when
|
|
122
|
+
* exact. */
|
|
123
|
+
type VarNameOf<T extends string> = T extends `var.${"set" | "get"}.${infer N}` ? N extends `${string}*${string}` ? string : N : string;
|
|
124
|
+
/** What a `var.set.…` handler receives. Writes are synchronous, so the
|
|
125
|
+
* handler must be too - call `next()` to let the write land, skip it to
|
|
126
|
+
* cancel, throw to abort. */
|
|
127
|
+
type VarSetContext<N extends string = string> = {
|
|
128
|
+
/** The var being written. */
|
|
129
|
+
name: N;
|
|
130
|
+
/** The incoming value. */
|
|
131
|
+
value: unknown;
|
|
132
|
+
/** The fn frame performing the write. */
|
|
133
|
+
fn: string;
|
|
134
|
+
/** Every other property reads the scope's vars directly. */
|
|
135
|
+
[key: string]: unknown;
|
|
136
|
+
};
|
|
137
|
+
/** What a `var.get.…` handler receives. Reads are synchronous, so the
|
|
138
|
+
* handler must be too - `next()` yields the stored value and whatever the
|
|
139
|
+
* handler returns becomes the read result. */
|
|
140
|
+
type VarGetContext<N extends string = string> = {
|
|
141
|
+
/** The var being read. */
|
|
142
|
+
name: N;
|
|
143
|
+
/** The fn frame performing the read. */
|
|
144
|
+
fn: string;
|
|
145
|
+
/** Every other property reads the scope's vars (raw - no get hooks). */
|
|
146
|
+
[key: string]: unknown;
|
|
147
|
+
};
|
|
148
|
+
type FnInputOf<F> = F extends FnDefination<any, any, any, infer I, any, any> ? InferInput<I> : never;
|
|
149
|
+
type FnResultOf<F> = F extends FnDefination<any, infer R, any, any, any, any> ? Awaited<R> : never;
|
|
150
|
+
declare function on<T extends "var.set.*" | `var.set.${string}`>(target: T, handler: (c: VarSetContext<VarNameOf<T>>, next: () => void) => void): OnEntry<T>;
|
|
151
|
+
declare function on<T extends "var.get.*" | `var.get.${string}`>(target: T, handler: (c: VarGetContext<VarNameOf<T>>, next: () => unknown) => unknown): OnEntry<T>;
|
|
152
|
+
declare function on<F extends FnDefination<any, any, string, any, any, any>>(target: F, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
153
|
+
input: FnInputOf<F>;
|
|
154
|
+
}, next: () => Promise<FnResultOf<F>>) => any): OnEntry<F["key"]>;
|
|
155
|
+
declare function on<F extends FnDefination<any, any, string, any, any, any>, const Ext>(target: F, extend: {
|
|
156
|
+
input: Ext;
|
|
157
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
158
|
+
input: FnInputOf<F> & InferInput<Ext>;
|
|
159
|
+
}, next: () => Promise<FnResultOf<F>>) => any): OnEntry<F["key"], Ext>;
|
|
160
|
+
declare function on(targets: readonly (LiteralString | RegExp | FnDefination<any, any, string, any, any, any>)[], handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<string>;
|
|
161
|
+
declare function on(target: RegExp, handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<string>;
|
|
162
|
+
declare function on<N extends "*" | LiteralString>(target: N, handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<N>;
|
|
163
|
+
declare function on<const Ext>(target: RegExp, extend: {
|
|
164
|
+
input: Ext;
|
|
165
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
166
|
+
input: InferInput<Ext> & Record<string, any>;
|
|
167
|
+
}, next: () => Promise<any>) => any): OnEntry<string, Ext>;
|
|
168
|
+
declare function on<N extends LiteralString, const Ext>(target: N, extend: {
|
|
169
|
+
input: Ext;
|
|
170
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
171
|
+
input: InferInput<Ext> & Record<string, any>;
|
|
172
|
+
}, next: () => Promise<any>) => any): OnEntry<N, Ext>;
|
|
173
|
+
type VarExtension<N extends string, S, BaseT = unknown> = {
|
|
174
|
+
$varExtend: true;
|
|
175
|
+
name: N;
|
|
176
|
+
schema: S;
|
|
177
|
+
/** The var being extended, when handed by reference. */
|
|
178
|
+
base?: VarDefination<N, BaseT, any, any>;
|
|
179
|
+
};
|
|
180
|
+
declare const isVarExtension: (value: any) => value is VarExtension<string, any>;
|
|
181
|
+
/**
|
|
182
|
+
* Extend a var's shape from a module. Where `customize` mints a NEW var
|
|
183
|
+
* definition to re-export, an extension is a mountable value: every scope
|
|
184
|
+
* or fn that `use`s the module containing it sees the var widened, and
|
|
185
|
+
* nothing that doesn't mount it is affected.
|
|
186
|
+
*
|
|
187
|
+
* Handed the var by REFERENCE, the extension carries it: mounting just
|
|
188
|
+
* the extension brings the var itself, merged by its declared key - no
|
|
189
|
+
* need to also mount the base var.
|
|
190
|
+
*/
|
|
191
|
+
declare function extendVar<N extends LiteralString, S, BaseT>(target: VarDefination<N, BaseT, any, any>, schema: S): VarExtension<N, S, BaseT>;
|
|
192
|
+
declare function extendVar<N extends LiteralString, S>(target: N, schema: S): VarExtension<N, S>;
|
|
193
|
+
type VarExtEntry<M, K extends string> = M extends unknown ? { [P in keyof M]: M[P] extends VarExtension<K, infer S> ? InferInput<S> : never; }[keyof M] : never;
|
|
194
|
+
/**
|
|
195
|
+
* Shape additions modules in `PL` mount on var `K`. Resolves to `unknown`
|
|
196
|
+
* when none - which intersects away harmlessly.
|
|
197
|
+
*/
|
|
198
|
+
type VarExtensionsFor<PL, K extends string> = UnionToIntersection<VarExtEntry<Members<PL>, K>>;
|
|
199
|
+
type VarExtArgsEntry<M, K extends string> = M extends unknown ? { [P in keyof M]: M[P] extends VarExtension<K, infer S> ? InferArgs<S> : never; }[keyof M] : never;
|
|
200
|
+
/** The ARGS side of the same extensions - what a caller must send. */
|
|
201
|
+
type VarExtensionArgsFor<PL, K extends string> = UnionToIntersection<VarExtArgsEntry<Members<PL>, K>>;
|
|
202
|
+
/** The args side of every VAR named `K` a module set declares - a
|
|
203
|
+
* `customize`d re-export shadows by NAME, so mounting it counts as a
|
|
204
|
+
* declaration about the same var. Schema-less vars contribute nothing. */
|
|
205
|
+
type VarShadowArgsEntry<M, K extends string> = M extends unknown ? { [P in keyof M]: M[P] extends VarDefination<K, any, infer S, any> ? unknown extends S ? never : [NonNullable<S>] extends [never] ? never : InferArgs<NonNullable<S>> : never; }[keyof M] : never;
|
|
206
|
+
/**
|
|
207
|
+
* Everything a module set says about var `K`'s ARGS side: extensions
|
|
208
|
+
* mounted on it, intersected with every same-name var declaration. This is
|
|
209
|
+
* what widens a fn schema whose input references `K` (see `WidenSchemaFns`)
|
|
210
|
+
* - `unknown` when the scope adds nothing, which intersects away.
|
|
211
|
+
*/
|
|
212
|
+
type VarArgsInScope<PL, K extends string> = VarExtensionArgsFor<PL, K> & ([VarShadowArgsEntry<Members<PL>, K>] extends [never] ? unknown : UnionToIntersection<VarShadowArgsEntry<Members<PL>, K>>);
|
|
213
|
+
type VarFieldKeys<I> = { [K in keyof I]: I[K] extends {
|
|
214
|
+
$var: true;
|
|
215
|
+
} ? K : never; }[keyof I];
|
|
216
|
+
/**
|
|
217
|
+
* Extra args a fn must accept because its INPUT references vars that `PL`
|
|
218
|
+
* extends. Whole-var input (`input: user`) merges at the top level; a var
|
|
219
|
+
* used as a field widens that field. `unknown` when nothing applies.
|
|
220
|
+
*/
|
|
221
|
+
type InputVarExtra<PL, I> = I extends {
|
|
222
|
+
$var: true;
|
|
223
|
+
name: infer N extends string;
|
|
224
|
+
} ? VarExtensionArgsFor<PL, N> : [VarFieldKeys<I>] extends [never] ? unknown : { [K in keyof I as I[K] extends {
|
|
225
|
+
$var: true;
|
|
226
|
+
} ? K : never]: I[K] extends {
|
|
227
|
+
$var: true;
|
|
228
|
+
name: infer N extends string;
|
|
229
|
+
} ? VarExtensionArgsFor<PL, N> : unknown; };
|
|
230
|
+
type ExtEntryArgs<M, K extends string> = M extends unknown ? { [P in keyof M]: M[P] extends OnEntry<infer N, infer E> ? unknown extends E ? never : TargetMatches<N, K> extends true ? InferArgs<E> : never : never; }[keyof M] : never;
|
|
231
|
+
/** Args added onto fn `K` by every extending `on` entry in `PL`. */
|
|
232
|
+
type ExtendedArgs<PL, K extends string> = UnionToIntersection<ExtEntryArgs<Members<PL>, K>>;
|
|
233
|
+
/**
|
|
234
|
+
* Rewrite a fn's type with the input extensions the module set `PL`
|
|
235
|
+
* mounts on it. This is how a plugin's extra field becomes REQUIRED at
|
|
236
|
+
* the call site, even though the fn itself never declared it.
|
|
237
|
+
*/
|
|
238
|
+
type ApplyOn<F, PL> = F extends FnDefination<infer A, infer R, infer K, infer I, infer P, infer Er, infer W, infer O> ? unknown extends ExtendedArgs<PL, K & string> & InputVarExtra<PL, I> ? F : FnDefination<Prettify<([A] extends [void] ? unknown : A) & ExtendedArgs<PL, K & string> & InputVarExtra<PL, I>>, R, K & string, I, P, Er, W, O> : F;
|
|
239
|
+
type ApplyOns<Fns, PL> = { [P in keyof Fns]: ApplyOn<Fns[P], PL>; };
|
|
240
|
+
type DerivedNamesEntry<Mod, Q> = Mod extends unknown ? { [K in keyof Mod]: Mod[K] extends VarDefination<infer N, any, any, infer Src> ? [Src] extends [never] ? never : Src extends Q ? N : never : never; }[keyof Mod] : never;
|
|
241
|
+
/**
|
|
242
|
+
* Expand a `requires` set with every var DERIVED from a required source:
|
|
243
|
+
* requiring `request` makes `method`, `path`, ... non-null too, because
|
|
244
|
+
* they are computed from it.
|
|
245
|
+
*/
|
|
246
|
+
type WithDerived<PL, BasePL, Q> = Q | DerivedNamesEntry<Members<PL> | Members<BasePL>, Q>;
|
|
247
|
+
//#endregion
|
|
248
|
+
export { ApplyOn, ApplyOns, ExtendedArgs, FnsFrom, InputVarExtra, Interceptor, Module, ModuleFns, ModuleVars, OnDefaultContext, OnEntry, TargetMatches, VarArgsInScope, VarExtension, VarExtensionArgsFor, VarExtensionsFor, VarGetContext, VarSetContext, VarsFrom, WithDerived, collectFns, collectUsable, extendVar, isFn, isNamespace, isOn, isVarExtension, on };
|
|
249
|
+
//# sourceMappingURL=module.d.cts.map
|