better-call 0.0.0-experimental.01ff4b77
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 +265 -0
- package/dist/capability.cjs +417 -0
- package/dist/capability.cjs.map +1 -0
- package/dist/capability.d.cts +189 -0
- package/dist/capability.d.mts +189 -0
- package/dist/capability.mjs +401 -0
- package/dist/capability.mjs.map +1 -0
- package/dist/error.cjs +76 -0
- package/dist/error.cjs.map +1 -0
- package/dist/error.d.cts +51 -0
- package/dist/error.d.mts +51 -0
- package/dist/error.mjs +72 -0
- package/dist/error.mjs.map +1 -0
- package/dist/fn.cjs +350 -0
- package/dist/fn.cjs.map +1 -0
- package/dist/fn.d.cts +378 -0
- package/dist/fn.d.mts +378 -0
- package/dist/fn.mjs +350 -0
- package/dist/fn.mjs.map +1 -0
- package/dist/index.cjs +43 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +50 -0
- package/dist/index.d.mts +50 -0
- package/dist/index.mjs +24 -0
- package/dist/index.mjs.map +1 -0
- package/dist/module.cjs +176 -0
- package/dist/module.cjs.map +1 -0
- package/dist/module.d.cts +283 -0
- package/dist/module.d.mts +283 -0
- package/dist/module.mjs +166 -0
- package/dist/module.mjs.map +1 -0
- package/dist/plugins/db.cjs +24 -0
- package/dist/plugins/db.cjs.map +1 -0
- package/dist/plugins/db.d.cts +18 -0
- package/dist/plugins/db.d.mts +18 -0
- package/dist/plugins/db.mjs +19 -0
- package/dist/plugins/db.mjs.map +1 -0
- package/dist/plugins/http/attrs.cjs +91 -0
- package/dist/plugins/http/attrs.cjs.map +1 -0
- package/dist/plugins/http/attrs.d.cts +26 -0
- package/dist/plugins/http/attrs.d.mts +26 -0
- package/dist/plugins/http/attrs.mjs +87 -0
- package/dist/plugins/http/attrs.mjs.map +1 -0
- package/dist/plugins/http/cookie.cjs +117 -0
- package/dist/plugins/http/cookie.cjs.map +1 -0
- package/dist/plugins/http/cookie.d.cts +637 -0
- package/dist/plugins/http/cookie.d.mts +637 -0
- package/dist/plugins/http/cookie.mjs +113 -0
- package/dist/plugins/http/cookie.mjs.map +1 -0
- package/dist/plugins/http/error.cjs +39 -0
- package/dist/plugins/http/error.cjs.map +1 -0
- package/dist/plugins/http/error.d.cts +38 -0
- package/dist/plugins/http/error.d.mts +38 -0
- package/dist/plugins/http/error.mjs +35 -0
- package/dist/plugins/http/error.mjs.map +1 -0
- package/dist/plugins/http/handle.cjs +73 -0
- package/dist/plugins/http/handle.cjs.map +1 -0
- package/dist/plugins/http/handle.d.cts +955 -0
- package/dist/plugins/http/handle.d.mts +955 -0
- package/dist/plugins/http/handle.mjs +72 -0
- package/dist/plugins/http/handle.mjs.map +1 -0
- package/dist/plugins/http/redirect.cjs +82 -0
- package/dist/plugins/http/redirect.cjs.map +1 -0
- package/dist/plugins/http/redirect.d.cts +67 -0
- package/dist/plugins/http/redirect.d.mts +67 -0
- package/dist/plugins/http/redirect.mjs +79 -0
- package/dist/plugins/http/redirect.mjs.map +1 -0
- package/dist/plugins/http/request.cjs +63 -0
- package/dist/plugins/http/request.cjs.map +1 -0
- package/dist/plugins/http/request.d.cts +142 -0
- package/dist/plugins/http/request.d.mts +142 -0
- package/dist/plugins/http/request.mjs +61 -0
- package/dist/plugins/http/request.mjs.map +1 -0
- package/dist/plugins/http/response.cjs +13 -0
- package/dist/plugins/http/response.cjs.map +1 -0
- package/dist/plugins/http/response.d.cts +18 -0
- package/dist/plugins/http/response.d.mts +18 -0
- package/dist/plugins/http/response.mjs +13 -0
- package/dist/plugins/http/response.mjs.map +1 -0
- package/dist/plugins/http.cjs +64 -0
- package/dist/plugins/http.cjs.map +1 -0
- package/dist/plugins/http.d.cts +945 -0
- package/dist/plugins/http.d.mts +945 -0
- package/dist/plugins/http.mjs +37 -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 +245 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +411 -0
- package/dist/schema.d.mts +411 -0
- package/dist/schema.mjs +235 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scope.d.cts +31 -0
- package/dist/scope.d.mts +31 -0
- package/dist/storage.cjs +308 -0
- package/dist/storage.cjs.map +1 -0
- package/dist/storage.d.cts +220 -0
- package/dist/storage.d.mts +220 -0
- package/dist/storage.mjs +302 -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 +186 -0
- package/dist/var.cjs.map +1 -0
- package/dist/var.d.cts +46 -0
- package/dist/var.d.mts +46 -0
- package/dist/var.mjs +177 -0
- package/dist/var.mjs.map +1 -0
- package/package.json +114 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ControlFlow, FnError, Issue, UnexpectedError, ValidationError } from "./error.mjs";
|
|
2
|
+
import { LiteralString, Prettify } from "./types.mjs";
|
|
3
|
+
import { AttrBag, InferArgs, InferInput, InferOutput, InferType, TypeDefination, attrsOf, vTypes, withAttrs } from "./schema.mjs";
|
|
4
|
+
import { Collection, Condition, FieldMeta, FindManyOptions, ModelConfig, Storage, StorageAdapter, StorageApi, StorageHook, StorageHookContext, StorageModels, StorageOp, StorageTarget, Where, WhereOp, WhereOps, conditionsOf, fieldsFromSchema, isStorage, makeStorage, matchesWhere, memoryAdapter, resolveModelFields } from "./storage.mjs";
|
|
5
|
+
import { ResolvedVars, RowInScope, ScopeOf, VarName, VarScope } from "./scope.mjs";
|
|
6
|
+
import { ApplyOn, ApplyOns, ExtendedArgs, InputVarExtra, InputVarExtraOut, Interceptor, Module, ModuleFns, ModuleVars, OnDefaultContext, OnEntry, VarExtension, VarExtensionsFor, VarGetContext, VarSetContext, VarsFrom, collectFns, collectUsable, extendVar, isFn, isNamespace, isOn, isVarExtension, on } from "./module.mjs";
|
|
7
|
+
import { Context, Fn, FnDefination, FnErrors, Instance, OptionType, ParentContext, UseApi, WidenedArgs, WithContext, WithSeed } from "./fn.mjs";
|
|
8
|
+
import { NameOfVar, ValueOfVar, VarCustomizer, VarDefination, VarMap } from "./var.mjs";
|
|
9
|
+
//#region src/index.d.ts
|
|
10
|
+
interface V {
|
|
11
|
+
fn: Fn;
|
|
12
|
+
var: <N extends LiteralString, S = undefined, D = undefined, const M extends boolean | undefined = undefined>(name: N, options?: {
|
|
13
|
+
default?: D;
|
|
14
|
+
schema?: S;
|
|
15
|
+
merge?: M;
|
|
16
|
+
}) => VarDefination<N, [S] extends [undefined] ? D : InferInput<S> | ([D] extends [InferInput<S>] ? never : D), S> & (M extends true ? {
|
|
17
|
+
$merge: true;
|
|
18
|
+
} : unknown);
|
|
19
|
+
/** A var you accumulate into: `set()` merges instead of replacing. */
|
|
20
|
+
record: <N extends LiteralString, S = undefined>(name: N, options?: {
|
|
21
|
+
schema?: S;
|
|
22
|
+
}) => VarDefination<N, [S] extends [undefined] ? Record<string, unknown> : Partial<InferInput<S>>, S>;
|
|
23
|
+
/**
|
|
24
|
+
* A var computed from another. Reads run the getter against the current
|
|
25
|
+
* source; `requires` on the SOURCE makes derived vars non-null too.
|
|
26
|
+
*/
|
|
27
|
+
derive: <N extends LiteralString, SV extends VarDefination<any, any, any, any>, R>(name: N, source: SV, get: (value: NonNullable<ValueOfVar<SV>>) => R) => VarDefination<N, R | Extract<ValueOfVar<SV>, null | undefined>, unknown, NameOfVar<SV>>;
|
|
28
|
+
/**
|
|
29
|
+
* MANY instances of a var, queryable: each named var becomes a
|
|
30
|
+
* COLLECTION of rows shaped like its value - `db.user.create(row)`,
|
|
31
|
+
* `db.user.findOne({ email })`, findMany/update/delete/count. The
|
|
32
|
+
* adapter is the translation seam a real database implements;
|
|
33
|
+
* `memoryAdapter()` is the built-in dummy.
|
|
34
|
+
*/
|
|
35
|
+
storage: typeof makeStorage;
|
|
36
|
+
on: typeof on;
|
|
37
|
+
extend: typeof extendVar;
|
|
38
|
+
string: (typeof vTypes)["string"];
|
|
39
|
+
number: (typeof vTypes)["number"];
|
|
40
|
+
boolean: (typeof vTypes)["boolean"];
|
|
41
|
+
date: (typeof vTypes)["date"];
|
|
42
|
+
object: (typeof vTypes)["object"];
|
|
43
|
+
array: (typeof vTypes)["array"];
|
|
44
|
+
union: (typeof vTypes)["union"];
|
|
45
|
+
any: (typeof vTypes)["any"];
|
|
46
|
+
}
|
|
47
|
+
declare const v: V;
|
|
48
|
+
//#endregion
|
|
49
|
+
export { type ApplyOn, type ApplyOns, type AttrBag, type Collection, type Condition, type Context, ControlFlow, type ExtendedArgs, type FieldMeta, type FindManyOptions, type Fn, type FnDefination, FnError, type FnErrors, type InferArgs, type InferInput, type InferOutput, type InferType, type InputVarExtra, type InputVarExtraOut, type Instance, type Interceptor, type Issue, type LiteralString, type ModelConfig, type Module, type ModuleFns, type ModuleVars, type OnDefaultContext, type OnEntry, type OptionType, type ParentContext, type Prettify, type ResolvedVars, type RowInScope, type ScopeOf, type Storage, type StorageAdapter, type StorageApi, type StorageHook, type StorageHookContext, type StorageModels, type StorageOp, type StorageTarget, type TypeDefination, UnexpectedError, type UseApi, ValidationError, type VarCustomizer, type VarDefination, type VarExtension, type VarExtensionsFor, type VarGetContext, type VarMap, type VarName, type VarScope, type VarSetContext, type VarsFrom, type Where, type WhereOp, type WhereOps, type WidenedArgs, type WithContext, type WithSeed, attrsOf, collectFns, collectUsable, conditionsOf, fieldsFromSchema, isFn, isNamespace, isOn, isStorage, isVarExtension, matchesWhere, memoryAdapter, resolveModelFields, v, withAttrs };
|
|
50
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ControlFlow, FnError, UnexpectedError, ValidationError } from "./error.mjs";
|
|
2
|
+
import { attrsOf, vTypes, withAttrs } from "./schema.mjs";
|
|
3
|
+
import { collectFns, collectUsable, extendVar, isFn, isNamespace, isOn, isVarExtension, on } from "./module.mjs";
|
|
4
|
+
import { deriveVar, makeVar } from "./var.mjs";
|
|
5
|
+
import { fnImpl } from "./fn.mjs";
|
|
6
|
+
import { conditionsOf, fieldsFromSchema, isStorage, makeStorage, matchesWhere, memoryAdapter, resolveModelFields } from "./storage.mjs";
|
|
7
|
+
//#region src/index.ts
|
|
8
|
+
const v = {
|
|
9
|
+
fn: fnImpl,
|
|
10
|
+
var: makeVar,
|
|
11
|
+
record: ((name, options = {}) => makeVar(name, {
|
|
12
|
+
...options,
|
|
13
|
+
accessor: true
|
|
14
|
+
})),
|
|
15
|
+
derive: deriveVar,
|
|
16
|
+
storage: makeStorage,
|
|
17
|
+
on,
|
|
18
|
+
extend: extendVar,
|
|
19
|
+
...vTypes
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ControlFlow, FnError, UnexpectedError, ValidationError, attrsOf, collectFns, collectUsable, conditionsOf, fieldsFromSchema, isFn, isNamespace, isOn, isStorage, isVarExtension, matchesWhere, memoryAdapter, resolveModelFields, v, withAttrs };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type Fn, fnImpl } from \"./fn\";\nimport { extendVar, on } from \"./module\";\n\nimport { type InferInput, vTypes } from \"./schema\";\nimport { makeStorage } from \"./storage\";\nimport type { LiteralString } from \"./types\";\nimport {\n\tderiveVar,\n\tmakeVar,\n\ttype NameOfVar,\n\ttype ValueOfVar,\n\ttype VarDefination,\n} from \"./var\";\n\ninterface V {\n\tfn: Fn;\n\tvar: <\n\t\tN extends LiteralString,\n\t\tS = undefined,\n\t\tD = undefined,\n\t\tconst M extends boolean | undefined = undefined,\n\t>(\n\t\tname: N,\n\t\toptions?: { default?: D; schema?: S; merge?: M },\n\t\t// A default the schema already covers (e.g. `{}` against an\n\t\t// all-optional shape) is absorbed; `default: null` still unions in.\n\t\t// `merge: true` - object contributions from `use` modules (same-name\n\t\t// defaults, same-key namespaces) shallow-merge onto the value;\n\t\t// writes accumulate the same way. Brand `$merge: true` so scope\n\t\t// types can fold those helpers onto the var.\n\t) => VarDefination<\n\t\tN,\n\t\t[S] extends [undefined]\n\t\t\t? D\n\t\t\t: InferInput<S> | ([D] extends [InferInput<S>] ? never : D),\n\t\tS\n\t> &\n\t\t(M extends true ? { $merge: true } : unknown);\n\t/** A var you accumulate into: `set()` merges instead of replacing. */\n\trecord: <N extends LiteralString, S = undefined>(\n\t\tname: N,\n\t\toptions?: { schema?: S },\n\t) => VarDefination<\n\t\tN,\n\t\t[S] extends [undefined] ? Record<string, unknown> : Partial<InferInput<S>>,\n\t\tS\n\t>;\n\t/**\n\t * A var computed from another. Reads run the getter against the current\n\t * source; `requires` on the SOURCE makes derived vars non-null too.\n\t */\n\tderive: <\n\t\tN extends LiteralString,\n\t\tSV extends VarDefination<any, any, any, any>,\n\t\tR,\n\t>(\n\t\tname: N,\n\t\tsource: SV,\n\t\tget: (value: NonNullable<ValueOfVar<SV>>) => R,\n\t) => VarDefination<\n\t\tN,\n\t\tR | Extract<ValueOfVar<SV>, null | undefined>,\n\t\tunknown,\n\t\tNameOfVar<SV>\n\t>;\n\t/**\n\t * MANY instances of a var, queryable: each named var becomes a\n\t * COLLECTION of rows shaped like its value - `db.user.create(row)`,\n\t * `db.user.findOne({ email })`, findMany/update/delete/count. The\n\t * adapter is the translation seam a real database implements;\n\t * `memoryAdapter()` is the built-in dummy.\n\t */\n\tstorage: typeof makeStorage;\n\ton: typeof on;\n\textend: typeof extendVar;\n\tstring: (typeof vTypes)[\"string\"];\n\tnumber: (typeof vTypes)[\"number\"];\n\tboolean: (typeof vTypes)[\"boolean\"];\n\tdate: (typeof vTypes)[\"date\"];\n\tobject: (typeof vTypes)[\"object\"];\n\tarray: (typeof vTypes)[\"array\"];\n\tunion: (typeof vTypes)[\"union\"];\n\tany: (typeof vTypes)[\"any\"];\n}\n\nexport const v: V = {\n\tfn: fnImpl as Fn,\n\tvar: makeVar as V[\"var\"],\n\trecord: ((name: string, options: any = {}) =>\n\t\tmakeVar(name, { ...options, accessor: true })) as V[\"record\"],\n\tderive: deriveVar as V[\"derive\"],\n\tstorage: makeStorage,\n\ton,\n\textend: extendVar,\n\t...vTypes,\n};\n\nexport {\n\tControlFlow,\n\tFnError,\n\ttype Issue,\n\tUnexpectedError,\n\tValidationError,\n} from \"./error\";\nexport type {\n\tContext,\n\tFn,\n\tFnDefination,\n\tFnErrors,\n\tInstance,\n\tOptionType,\n\tParentContext,\n\tUseApi,\n\tWidenedArgs,\n\tWithContext,\n\tWithSeed,\n} from \"./fn\";\nexport {\n\ttype ApplyOn,\n\ttype ApplyOns,\n\tcollectFns,\n\tcollectUsable,\n\ttype ExtendedArgs,\n\ttype InputVarExtra,\n\ttype InputVarExtraOut,\n\ttype Interceptor,\n\tisFn,\n\tisNamespace,\n\tisOn,\n\tisVarExtension,\n\ttype Module,\n\ttype ModuleFns,\n\ttype ModuleVars,\n\ttype OnDefaultContext,\n\ttype OnEntry,\n\ttype VarExtension,\n\ttype VarExtensionsFor,\n\ttype VarGetContext,\n\ttype VarSetContext,\n\ttype VarsFrom,\n} from \"./module\";\nexport {\n\ttype AttrBag,\n\tattrsOf,\n\ttype InferArgs,\n\ttype InferInput,\n\ttype InferOutput,\n\ttype InferType,\n\ttype TypeDefination,\n\twithAttrs,\n} from \"./schema\";\nexport type {\n\tResolvedVars,\n\tRowInScope,\n\tScopeOf,\n\tVarName,\n\tVarScope,\n} from \"./scope\";\nexport {\n\ttype Collection,\n\ttype Condition,\n\tconditionsOf,\n\ttype FieldMeta,\n\ttype FindManyOptions,\n\tfieldsFromSchema,\n\tisStorage,\n\ttype ModelConfig,\n\tmatchesWhere,\n\tmemoryAdapter,\n\tresolveModelFields,\n\ttype Storage,\n\ttype StorageAdapter,\n\ttype StorageApi,\n\ttype StorageHook,\n\ttype StorageHookContext,\n\ttype StorageModels,\n\ttype StorageOp,\n\ttype StorageTarget,\n\ttype Where,\n\ttype WhereOp,\n\ttype WhereOps,\n} from \"./storage\";\nexport type { LiteralString, Prettify } from \"./types\";\nexport type { VarCustomizer, VarDefination, VarMap } from \"./var\";\n"],"mappings":";;;;;;;AAqFA,MAAa,IAAO;CACnB,IAAI;CACJ,KAAK;CACL,UAAU,MAAc,UAAe,CAAC,MACvC,QAAQ,MAAM;EAAE,GAAG;EAAS,UAAU;CAAK,CAAC;CAC7C,QAAQ;CACR,SAAS;CACT;CACA,QAAQ;CACR,GAAG;AACJ"}
|
package/dist/module.cjs
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
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 stay opaque values (their `$models` are not a
|
|
41
|
+
* nested module group).
|
|
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
|
+
if ("$models" in value && typeof value.$adapter === "function") return false;
|
|
49
|
+
return Object.values(value).some((m) => isFn(m) || require_schema.isVar(m) || isOn(m) || isVarExtension(m) || isNamespace(m));
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Every exported fn AND var, with plain-record GROUPS kept as nested
|
|
53
|
+
* namespaces: `use: [{ cookie: { setCookie } }]` lands the fn on
|
|
54
|
+
* `c.cookie.setCookie`. A var member becomes an ALIAS under its export
|
|
55
|
+
* name (`{ cookie: { options: cookieOptions } }` -> `c.cookie.options`
|
|
56
|
+
* reads/writes the var). Storage mounts whole (`c.db.user.findOne`).
|
|
57
|
+
* Groups holding nothing (however deep) drop out.
|
|
58
|
+
*
|
|
59
|
+
* A `merge: true` var keeps its slot when a later module exports a
|
|
60
|
+
* namespace under the same key - those helpers are folded into the var
|
|
61
|
+
* value by {@link collectMergeSeeds}, not bound as a rival namespace.
|
|
62
|
+
*/
|
|
63
|
+
const collectUsable = (modules) => {
|
|
64
|
+
const isStorageValue = (value) => typeof value === "object" && value !== null && "$models" in value && typeof value.$adapter === "function";
|
|
65
|
+
const isMergeVar = (value) => require_schema.isVar(value) && value.$merge === true;
|
|
66
|
+
const isGroup = (value) => value !== null && typeof value === "object" && !isFn(value) && !require_schema.isVar(value) && !isStorageValue(value);
|
|
67
|
+
const walk = (mod) => {
|
|
68
|
+
const out = {};
|
|
69
|
+
for (const [name, value] of Object.entries(mod)) if (isFn(value) || require_schema.isVar(value) || isStorageValue(value)) out[name] = value;
|
|
70
|
+
else if (isNamespace(value)) {
|
|
71
|
+
const nested = walk(value);
|
|
72
|
+
if (Object.keys(nested).length > 0) out[name] = nested;
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
};
|
|
76
|
+
const assign = (target, name, value) => {
|
|
77
|
+
const existing = target[name];
|
|
78
|
+
if (isMergeVar(existing) && isGroup(value)) return;
|
|
79
|
+
if (isMergeVar(value) && isGroup(existing)) {
|
|
80
|
+
target[name] = value;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (isGroup(existing) && isGroup(value)) {
|
|
84
|
+
const group = { ...existing };
|
|
85
|
+
for (const [k, v] of Object.entries(value)) assign(group, k, v);
|
|
86
|
+
target[name] = group;
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
target[name] = value;
|
|
90
|
+
};
|
|
91
|
+
const fns = {};
|
|
92
|
+
for (const mod of resolveModules(modules)) for (const [name, value] of Object.entries(walk(mod))) assign(fns, name, value);
|
|
93
|
+
return fns;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* Initial values for `merge: true` vars: walk `use` modules in order and
|
|
97
|
+
* shallow-merge (1) each merge-var's `default` and (2) namespaces exported
|
|
98
|
+
* under the same key as a merge var. Later leaf keys win.
|
|
99
|
+
*/
|
|
100
|
+
const collectMergeSeeds = (modules) => {
|
|
101
|
+
const resolved = resolveModules(modules);
|
|
102
|
+
const seeds = {};
|
|
103
|
+
const keyToVar = /* @__PURE__ */ new Map();
|
|
104
|
+
const isMergeVar = (value) => require_schema.isVar(value) && value.$merge === true;
|
|
105
|
+
const mergeInto = (name, contribution) => {
|
|
106
|
+
if (contribution === void 0) return;
|
|
107
|
+
const current = seeds[name];
|
|
108
|
+
if (contribution !== null && typeof contribution === "object" && !Array.isArray(contribution) && current !== null && typeof current === "object" && !Array.isArray(current)) seeds[name] = {
|
|
109
|
+
...current,
|
|
110
|
+
...contribution
|
|
111
|
+
};
|
|
112
|
+
else seeds[name] = contribution;
|
|
113
|
+
};
|
|
114
|
+
const index = (mod) => {
|
|
115
|
+
for (const [key, value] of Object.entries(mod)) if (isMergeVar(value)) keyToVar.set(key, value.name);
|
|
116
|
+
else if (isNamespace(value)) index(value);
|
|
117
|
+
};
|
|
118
|
+
for (const mod of resolved) index(mod);
|
|
119
|
+
const contribute = (mod) => {
|
|
120
|
+
for (const [key, value] of Object.entries(mod)) {
|
|
121
|
+
if (isMergeVar(value)) {
|
|
122
|
+
mergeInto(value.name, value.default);
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (isNamespace(value)) {
|
|
126
|
+
const varName = keyToVar.get(key);
|
|
127
|
+
if (varName) mergeInto(varName, value);
|
|
128
|
+
else contribute(value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
for (const mod of resolved) contribute(mod);
|
|
133
|
+
return seeds;
|
|
134
|
+
};
|
|
135
|
+
const isOn = (value) => value?.$on === true;
|
|
136
|
+
function on(target, extendOrHandler, maybeHandler) {
|
|
137
|
+
const resolveOne = (member) => isFn(member) ? member.key : member;
|
|
138
|
+
const resolved = Array.isArray(target) ? target.map(resolveOne) : resolveOne(target);
|
|
139
|
+
return typeof extendOrHandler === "function" ? {
|
|
140
|
+
$on: true,
|
|
141
|
+
target: resolved,
|
|
142
|
+
handler: extendOrHandler
|
|
143
|
+
} : {
|
|
144
|
+
$on: true,
|
|
145
|
+
target: resolved,
|
|
146
|
+
extend: extendOrHandler,
|
|
147
|
+
handler: maybeHandler
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const isVarExtension = (value) => value?.$varExtend === true;
|
|
151
|
+
function extendVar(target, schema) {
|
|
152
|
+
return typeof target === "string" ? {
|
|
153
|
+
$varExtend: true,
|
|
154
|
+
name: target,
|
|
155
|
+
schema
|
|
156
|
+
} : {
|
|
157
|
+
$varExtend: true,
|
|
158
|
+
name: target.name,
|
|
159
|
+
schema,
|
|
160
|
+
base: target
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
//#endregion
|
|
164
|
+
exports.collectFns = collectFns;
|
|
165
|
+
exports.collectMergeSeeds = collectMergeSeeds;
|
|
166
|
+
exports.collectUsable = collectUsable;
|
|
167
|
+
exports.extendVar = extendVar;
|
|
168
|
+
exports.isFn = isFn;
|
|
169
|
+
exports.isNamespace = isNamespace;
|
|
170
|
+
exports.isOn = isOn;
|
|
171
|
+
exports.isVarExtension = isVarExtension;
|
|
172
|
+
exports.matchesTarget = matchesTarget;
|
|
173
|
+
exports.on = on;
|
|
174
|
+
exports.resolveModules = resolveModules;
|
|
175
|
+
|
|
176
|
+
//# 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 { WidenSchemaFns } from \"./scope\";\nimport type {\n\tLiteralString,\n\tMembers,\n\tPrettify,\n\tUnionToIntersection,\n} from \"./types\";\nimport type { VarDefination } from \"./var\";\n\n/** Storage (or a slice): `$models` plus callable `$adapter`. Kept opaque\n * in {@link ModuleFns} so collections stay on `c.db`, not walked as groups.\n * Matches runtime detection (`$adapter` required) so a plain namespace that\n * happens to have a `$models` key is still walked for nested fns/vars. */\ntype StorageLike = {\n\t$models: object;\n\t$adapter: (...args: never[]) => unknown;\n};\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. Storage\n * stays opaque (collections are not a nested module group). */\ntype GroupMember<V> = V extends\n\t| { $var: true }\n\t| { $on: true }\n\t| { $varExtend: true }\n\t| StorageLike\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: M[K] extends StorageLike\n\t\t\t\t? K\n\t\t\t\t: [GroupFns<M[K], GroupDepth[D]>] extends [never]\n\t\t\t\t\t? never\n\t\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: M[K] extends StorageLike\n\t\t\t\t? M[K]\n\t\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 stay opaque values (their `$models` are not a\n * nested module group).\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\t// Storage: `$models` + `$adapter` - not a namespace of module members.\n\tif (\n\t\t\"$models\" in value &&\n\t\ttypeof (value as { $adapter?: unknown }).$adapter === \"function\"\n\t) {\n\t\treturn false;\n\t}\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). Storage mounts whole (`c.db.user.findOne`).\n * Groups holding nothing (however deep) drop out.\n *\n * A `merge: true` var keeps its slot when a later module exports a\n * namespace under the same key - those helpers are folded into the var\n * value by {@link collectMergeSeeds}, not bound as a rival namespace.\n */\nexport const collectUsable = (\n\tmodules: readonly Module[],\n): Record<string, unknown> => {\n\tconst isStorageValue = (value: unknown) =>\n\t\ttypeof value === \"object\" &&\n\t\tvalue !== null &&\n\t\t\"$models\" in value &&\n\t\ttypeof (value as { $adapter?: unknown }).$adapter === \"function\";\n\tconst isMergeVar = (value: unknown) =>\n\t\tisVar(value) && (value as { $merge?: boolean }).$merge === true;\n\tconst isGroup = (value: unknown) =>\n\t\tvalue !== null &&\n\t\ttypeof value === \"object\" &&\n\t\t!isFn(value) &&\n\t\t!isVar(value) &&\n\t\t!isStorageValue(value);\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) || isStorageValue(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 assign = (\n\t\ttarget: Record<string, unknown>,\n\t\tname: string,\n\t\tvalue: unknown,\n\t) => {\n\t\tconst existing = target[name];\n\t\tif (isMergeVar(existing) && isGroup(value)) return;\n\t\tif (isMergeVar(value) && isGroup(existing)) {\n\t\t\ttarget[name] = value;\n\t\t\treturn;\n\t\t}\n\t\tif (isGroup(existing) && isGroup(value)) {\n\t\t\tconst group = { ...(existing as Record<string, unknown>) };\n\t\t\tfor (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n\t\t\t\tassign(group, k, v);\n\t\t\t}\n\t\t\ttarget[name] = group;\n\t\t\treturn;\n\t\t}\n\t\ttarget[name] = value;\n\t};\n\tconst fns: Record<string, unknown> = {};\n\tfor (const mod of resolveModules(modules)) {\n\t\tfor (const [name, value] of Object.entries(walk(mod))) {\n\t\t\tassign(fns, name, value);\n\t\t}\n\t}\n\treturn fns;\n};\n\n/**\n * Initial values for `merge: true` vars: walk `use` modules in order and\n * shallow-merge (1) each merge-var's `default` and (2) namespaces exported\n * under the same key as a merge var. Later leaf keys win.\n */\nexport const collectMergeSeeds = (\n\tmodules: readonly Module[],\n): Record<string, unknown> => {\n\tconst resolved = resolveModules(modules);\n\tconst seeds: Record<string, unknown> = {};\n\tconst keyToVar = new Map<string, string>();\n\n\tconst isMergeVar = (\n\t\tvalue: unknown,\n\t): value is VarDefination<string, unknown> & {\n\t\t$merge: true;\n\t\tdefault?: unknown;\n\t\tname: string;\n\t} => isVar(value) && (value as { $merge?: boolean }).$merge === true;\n\n\tconst mergeInto = (name: string, contribution: unknown) => {\n\t\tif (contribution === undefined) return;\n\t\tconst current = seeds[name];\n\t\tif (\n\t\t\tcontribution !== null &&\n\t\t\ttypeof contribution === \"object\" &&\n\t\t\t!Array.isArray(contribution) &&\n\t\t\tcurrent !== null &&\n\t\t\ttypeof current === \"object\" &&\n\t\t\t!Array.isArray(current)\n\t\t) {\n\t\t\tseeds[name] = {\n\t\t\t\t...(current as Record<string, unknown>),\n\t\t\t\t...(contribution as Record<string, unknown>),\n\t\t\t};\n\t\t} else {\n\t\t\tseeds[name] = contribution;\n\t\t}\n\t};\n\n\tconst index = (mod: Record<string, unknown>) => {\n\t\tfor (const [key, value] of Object.entries(mod)) {\n\t\t\tif (isMergeVar(value)) {\n\t\t\t\tkeyToVar.set(key, value.name);\n\t\t\t} else if (isNamespace(value)) {\n\t\t\t\tindex(value);\n\t\t\t}\n\t\t}\n\t};\n\tfor (const mod of resolved) index(mod);\n\n\tconst contribute = (mod: Record<string, unknown>) => {\n\t\tfor (const [key, value] of Object.entries(mod)) {\n\t\t\tif (isMergeVar(value)) {\n\t\t\t\tmergeInto(value.name, value.default);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (isNamespace(value)) {\n\t\t\t\tconst varName = keyToVar.get(key);\n\t\t\t\tif (varName) mergeInto(varName, value);\n\t\t\t\telse contribute(value);\n\t\t\t}\n\t\t}\n\t};\n\tfor (const mod of resolved) contribute(mod);\n\n\treturn seeds;\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/** Optional props put `undefined` in the key union; strip it so \"no var\n * fields\" still reads as `never` and InputVarExtra stays `unknown`. */\ntype VarFields<I> = Exclude<VarFieldKeys<I>, undefined>;\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: [VarFields<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/**\n * Same merge as {@link InputVarExtra}, on the PARSED side - what the\n * handler sees on `c.input` after mounted extensions have been applied.\n */\nexport type InputVarExtraOut<PL, I> = I extends {\n\t$var: true;\n\tname: infer N extends string;\n}\n\t? VarExtensionsFor<PL, N>\n\t: [VarFields<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? VarExtensionsFor<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. Storage\n * members get the same scope rewrite as a db var's value ({@link\n * WidenSchemaFns}): collections re-resolve row/`Where` types against\n * mounted `v.extend` / customize.\n */\nexport type ApplyOn<F, PL> = F extends StorageLike\n\t? WidenSchemaFns<F, PL>\n\t: F extends FnDefination<\n\t\t\t\tinfer A,\n\t\t\t\tinfer R,\n\t\t\t\tinfer K,\n\t\t\t\tinfer I,\n\t\t\t\tinfer P,\n\t\t\t\tinfer Er,\n\t\t\t\tinfer W,\n\t\t\t\tinfer O\n\t\t\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":"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"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { LiteralString, Members, Prettify, UnionToIntersection } from "./types.cjs";
|
|
2
|
+
import { InferArgs, InferInput, vTypes } from "./schema.cjs";
|
|
3
|
+
import { WidenSchemaFns } from "./scope.cjs";
|
|
4
|
+
import { FnDefination } from "./fn.cjs";
|
|
5
|
+
import { VarDefination } from "./var.cjs";
|
|
6
|
+
//#region src/module.d.ts
|
|
7
|
+
/** Storage (or a slice): `$models` plus callable `$adapter`. Kept opaque
|
|
8
|
+
* in {@link ModuleFns} so collections stay on `c.db`, not walked as groups.
|
|
9
|
+
* Matches runtime detection (`$adapter` required) so a plain namespace that
|
|
10
|
+
* happens to have a `$models` key is still walked for nested fns/vars. */
|
|
11
|
+
type StorageLike = {
|
|
12
|
+
$models: object;
|
|
13
|
+
$adapter: (...args: never[]) => unknown;
|
|
14
|
+
};
|
|
15
|
+
type Interceptor = (c: any, next: () => Promise<any>) => any;
|
|
16
|
+
/**
|
|
17
|
+
* What a GLOBAL `v.on` handler sees. Without a builder there is no scope
|
|
18
|
+
* to infer, but the context's shape is always the same - so the default
|
|
19
|
+
* is structured with loose leaves, never `any`: `c.input.code` works,
|
|
20
|
+
* `c.tpyo` does not. Mount the entry on a builder's `on` for real types.
|
|
21
|
+
*/
|
|
22
|
+
type OnDefaultContext = {
|
|
23
|
+
input: Record<string, any>;
|
|
24
|
+
types: typeof vTypes;
|
|
25
|
+
fn: unknown;
|
|
26
|
+
/** Vars and used fns live directly on `c` - untyped here; mount the
|
|
27
|
+
* entry on a builder's `on` for real types. */
|
|
28
|
+
[key: string]: any;
|
|
29
|
+
};
|
|
30
|
+
type OnEntry<N extends string, Ext = unknown> = {
|
|
31
|
+
$on: true;
|
|
32
|
+
/** Exact key, a `*` wildcard pattern, a RegExp, or a LIST of any of
|
|
33
|
+
* those - one handler mounted on several events at once. */
|
|
34
|
+
target: N | RegExp | readonly (string | RegExp)[];
|
|
35
|
+
/** Extra input fields this mount adds to the target fn. */
|
|
36
|
+
extend?: {
|
|
37
|
+
input?: Ext;
|
|
38
|
+
};
|
|
39
|
+
handler: Interceptor;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The type-level half of `matchesTarget`, for input extensions: exact
|
|
43
|
+
* keys and single-`*` patterns narrow call sites; RegExp targets (typed
|
|
44
|
+
* `string`) intercept and validate at runtime but cannot narrow.
|
|
45
|
+
*/
|
|
46
|
+
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;
|
|
47
|
+
/**
|
|
48
|
+
* A module is the unit of composition: a RECORD of members - vars, fns,
|
|
49
|
+
* `on` entries, var extensions - usually just what a file exports
|
|
50
|
+
* (`import * as twoFactor`) or a curated bundle. "Plugin" is not a
|
|
51
|
+
* concept, only a usage: mounting someone else's module. Always an
|
|
52
|
+
* object, never a bare member: `use: [{ createUser }]`, not
|
|
53
|
+
* `use: [createUser]`. The `never` marks reject bare members at the type
|
|
54
|
+
* level; `resolveModules` rejects them at runtime.
|
|
55
|
+
*/
|
|
56
|
+
type Module = Record<string, unknown> & {
|
|
57
|
+
$var?: never;
|
|
58
|
+
$varExtend?: never;
|
|
59
|
+
/** Rejects a bare `on` ENTRY (`$on: true`) while letting a storage
|
|
60
|
+
* through - its `$on` is the hook-mounting METHOD, not the brand. */
|
|
61
|
+
$on?: (...args: never[]) => unknown;
|
|
62
|
+
$fn?: never;
|
|
63
|
+
};
|
|
64
|
+
/** A module member that can NEST other members: a plain record that is
|
|
65
|
+
* not itself branded. Fn defs are callable, so they never match. Storage
|
|
66
|
+
* stays opaque (collections are not a nested module group). */
|
|
67
|
+
type GroupMember<V> = V extends {
|
|
68
|
+
$var: true;
|
|
69
|
+
} | {
|
|
70
|
+
$on: true;
|
|
71
|
+
} | {
|
|
72
|
+
$varExtend: true;
|
|
73
|
+
} | StorageLike | ((...args: any[]) => any) | readonly unknown[] ? never : V extends Record<string, unknown> ? V : never;
|
|
74
|
+
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];
|
|
75
|
+
/**
|
|
76
|
+
* Vars a module exports, keyed by their DECLARED name, not export name.
|
|
77
|
+
* Distributes over `M`: without that, a union of modules collapses to
|
|
78
|
+
* `keyof A & keyof B` and every var is lost. The `[never]` guard matters
|
|
79
|
+
* too - `UnionToIntersection<never>` is `unknown`, and one var-less module
|
|
80
|
+
* in the union would absorb every other module's vars into `unknown`.
|
|
81
|
+
*/
|
|
82
|
+
type VarsFrom<M> = M extends unknown ? [VarEntryUnion<M>] extends [never] ? never : UnionToIntersection<VarEntryUnion<M>> : never;
|
|
83
|
+
type ModuleVars<PL> = UnionToIntersection<VarsFrom<Members<PL>>>;
|
|
84
|
+
/** Depth budget for nested groups: unbranded records can be arbitrarily
|
|
85
|
+
* deep (schema shapes, values), so the walk stops after a few levels
|
|
86
|
+
* instead of blowing the instantiation limit. */
|
|
87
|
+
type GroupDepth = [never, 0, 1, 2];
|
|
88
|
+
/** A nested GROUP's fns: only real groups holding at least one fn
|
|
89
|
+
* (within the depth budget) survive - everything else drops from the
|
|
90
|
+
* surface. */
|
|
91
|
+
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>;
|
|
92
|
+
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 : M[K] extends StorageLike ? 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] : M[K] extends StorageLike ? M[K] : GroupFns<M[K], GroupDepth[D]>; };
|
|
93
|
+
/** Fns and vars a module exports, keyed by EXPORT name. A plain-record
|
|
94
|
+
* member is a GROUP: its members stay nested under the member's name
|
|
95
|
+
* (`use: [{ cookie: { setCookie } }]` -> `c.cookie.setCookie`). A var
|
|
96
|
+
* member is an ALIAS onto the var - `{ cookie: { options: cookieOptions } }`
|
|
97
|
+
* reads and writes the `cookieOptions` var through `c.cookie.options`. */
|
|
98
|
+
type FnsFrom<M> = M extends unknown ? FnEntries<M> : never;
|
|
99
|
+
type ModuleFns<PL> = UnionToIntersection<FnsFrom<Members<PL>>>;
|
|
100
|
+
declare const isFn: (value: any) => value is FnDefination<any, any>;
|
|
101
|
+
/** Collect every exported fn from a set of plugin modules. */
|
|
102
|
+
declare const collectFns: (modules: readonly Module[]) => Record<string, FnDefination<any, any>>;
|
|
103
|
+
/**
|
|
104
|
+
* A plain-record member that GROUPS other members - not itself branded
|
|
105
|
+
* (fn, var, on entry, var extension) but holding at least one such member,
|
|
106
|
+
* transitively. Storages stay opaque values (their `$models` are not a
|
|
107
|
+
* nested module group).
|
|
108
|
+
*/
|
|
109
|
+
declare const isNamespace: (value: unknown) => value is Record<string, unknown>;
|
|
110
|
+
/**
|
|
111
|
+
* Every exported fn AND var, with plain-record GROUPS kept as nested
|
|
112
|
+
* namespaces: `use: [{ cookie: { setCookie } }]` lands the fn on
|
|
113
|
+
* `c.cookie.setCookie`. A var member becomes an ALIAS under its export
|
|
114
|
+
* name (`{ cookie: { options: cookieOptions } }` -> `c.cookie.options`
|
|
115
|
+
* reads/writes the var). Storage mounts whole (`c.db.user.findOne`).
|
|
116
|
+
* Groups holding nothing (however deep) drop out.
|
|
117
|
+
*
|
|
118
|
+
* A `merge: true` var keeps its slot when a later module exports a
|
|
119
|
+
* namespace under the same key - those helpers are folded into the var
|
|
120
|
+
* value by {@link collectMergeSeeds}, not bound as a rival namespace.
|
|
121
|
+
*/
|
|
122
|
+
declare const collectUsable: (modules: readonly Module[]) => Record<string, unknown>;
|
|
123
|
+
declare const isOn: (value: any) => value is OnEntry<string>;
|
|
124
|
+
/**
|
|
125
|
+
* Mount onto another fn by name. The handler replaces that fn's body and
|
|
126
|
+
* receives `next` - call it to delegate, or don't.
|
|
127
|
+
*
|
|
128
|
+
* Sounder than a before/after hook: it has the target's signature, runs
|
|
129
|
+
* INSIDE the target's contract checks (so skipping `next` still has to
|
|
130
|
+
* satisfy `provides`), and composes as a stack.
|
|
131
|
+
*
|
|
132
|
+
* The middle argument extends the target's INPUT: those fields are
|
|
133
|
+
* validated alongside the fn's own, land on `c.input`, and - through
|
|
134
|
+
* `ApplyOns` - become required at every call site that mounts this entry.
|
|
135
|
+
*/
|
|
136
|
+
/** The var name a `var.set.…`/`var.get.…` target addresses - literal when
|
|
137
|
+
* exact. */
|
|
138
|
+
type VarNameOf<T extends string> = T extends `var.${"set" | "get"}.${infer N}` ? N extends `${string}*${string}` ? string : N : string;
|
|
139
|
+
/** What a `var.set.…` handler receives. Writes are synchronous, so the
|
|
140
|
+
* handler must be too - call `next()` to let the write land, skip it to
|
|
141
|
+
* cancel, throw to abort. */
|
|
142
|
+
type VarSetContext<N extends string = string> = {
|
|
143
|
+
/** The var being written. */
|
|
144
|
+
name: N;
|
|
145
|
+
/** The incoming value. */
|
|
146
|
+
value: unknown;
|
|
147
|
+
/** The fn frame performing the write. */
|
|
148
|
+
fn: string;
|
|
149
|
+
/** Every other property reads the scope's vars directly. */
|
|
150
|
+
[key: string]: unknown;
|
|
151
|
+
};
|
|
152
|
+
/** What a `var.get.…` handler receives. Reads are synchronous, so the
|
|
153
|
+
* handler must be too - `next()` yields the stored value and whatever the
|
|
154
|
+
* handler returns becomes the read result. */
|
|
155
|
+
type VarGetContext<N extends string = string> = {
|
|
156
|
+
/** The var being read. */
|
|
157
|
+
name: N;
|
|
158
|
+
/** The fn frame performing the read. */
|
|
159
|
+
fn: string;
|
|
160
|
+
/** Every other property reads the scope's vars (raw - no get hooks). */
|
|
161
|
+
[key: string]: unknown;
|
|
162
|
+
};
|
|
163
|
+
type FnInputOf<F> = F extends FnDefination<any, any, any, infer I, any, any> ? InferInput<I> : never;
|
|
164
|
+
type FnResultOf<F> = F extends FnDefination<any, infer R, any, any, any, any> ? Awaited<R> : never;
|
|
165
|
+
declare function on<T extends "var.set.*" | `var.set.${string}`>(target: T, handler: (c: VarSetContext<VarNameOf<T>>, next: () => void) => void): OnEntry<T>;
|
|
166
|
+
declare function on<T extends "var.get.*" | `var.get.${string}`>(target: T, handler: (c: VarGetContext<VarNameOf<T>>, next: () => unknown) => unknown): OnEntry<T>;
|
|
167
|
+
declare function on<F extends FnDefination<any, any, string, any, any, any>>(target: F, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
168
|
+
input: FnInputOf<F>;
|
|
169
|
+
}, next: () => Promise<FnResultOf<F>>) => any): OnEntry<F["key"]>;
|
|
170
|
+
declare function on<F extends FnDefination<any, any, string, any, any, any>, const Ext>(target: F, extend: {
|
|
171
|
+
input: Ext;
|
|
172
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
173
|
+
input: FnInputOf<F> & InferInput<Ext>;
|
|
174
|
+
}, next: () => Promise<FnResultOf<F>>) => any): OnEntry<F["key"], Ext>;
|
|
175
|
+
declare function on(targets: readonly (LiteralString | RegExp | FnDefination<any, any, string, any, any, any>)[], handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<string>;
|
|
176
|
+
declare function on(target: RegExp, handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<string>;
|
|
177
|
+
declare function on<N extends "*" | LiteralString>(target: N, handler: (c: OnDefaultContext, next: () => Promise<any>) => any): OnEntry<N>;
|
|
178
|
+
declare function on<const Ext>(target: RegExp, extend: {
|
|
179
|
+
input: Ext;
|
|
180
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
181
|
+
input: InferInput<Ext> & Record<string, any>;
|
|
182
|
+
}, next: () => Promise<any>) => any): OnEntry<string, Ext>;
|
|
183
|
+
declare function on<N extends LiteralString, const Ext>(target: N, extend: {
|
|
184
|
+
input: Ext;
|
|
185
|
+
}, handler: (c: Omit<OnDefaultContext, "input"> & {
|
|
186
|
+
input: InferInput<Ext> & Record<string, any>;
|
|
187
|
+
}, next: () => Promise<any>) => any): OnEntry<N, Ext>;
|
|
188
|
+
type VarExtension<N extends string, S, BaseT = unknown> = {
|
|
189
|
+
$varExtend: true;
|
|
190
|
+
name: N;
|
|
191
|
+
schema: S;
|
|
192
|
+
/** The var being extended, when handed by reference. */
|
|
193
|
+
base?: VarDefination<N, BaseT, any, any>;
|
|
194
|
+
};
|
|
195
|
+
declare const isVarExtension: (value: any) => value is VarExtension<string, any>;
|
|
196
|
+
/**
|
|
197
|
+
* Extend a var's shape from a module. Where `customize` mints a NEW var
|
|
198
|
+
* definition to re-export, an extension is a mountable value: every scope
|
|
199
|
+
* or fn that `use`s the module containing it sees the var widened, and
|
|
200
|
+
* nothing that doesn't mount it is affected.
|
|
201
|
+
*
|
|
202
|
+
* Handed the var by REFERENCE, the extension carries it: mounting just
|
|
203
|
+
* the extension brings the var itself, merged by its declared key - no
|
|
204
|
+
* need to also mount the base var.
|
|
205
|
+
*/
|
|
206
|
+
declare function extendVar<N extends LiteralString, S, BaseT>(target: VarDefination<N, BaseT, any, any>, schema: S): VarExtension<N, S, BaseT>;
|
|
207
|
+
declare function extendVar<N extends LiteralString, S>(target: N, schema: S): VarExtension<N, S>;
|
|
208
|
+
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;
|
|
209
|
+
/**
|
|
210
|
+
* Shape additions modules in `PL` mount on var `K`. Resolves to `unknown`
|
|
211
|
+
* when none - which intersects away harmlessly.
|
|
212
|
+
*/
|
|
213
|
+
type VarExtensionsFor<PL, K extends string> = UnionToIntersection<VarExtEntry<Members<PL>, K>>;
|
|
214
|
+
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;
|
|
215
|
+
/** The ARGS side of the same extensions - what a caller must send. */
|
|
216
|
+
type VarExtensionArgsFor<PL, K extends string> = UnionToIntersection<VarExtArgsEntry<Members<PL>, K>>;
|
|
217
|
+
/** The args side of every VAR named `K` a module set declares - a
|
|
218
|
+
* `customize`d re-export shadows by NAME, so mounting it counts as a
|
|
219
|
+
* declaration about the same var. Schema-less vars contribute nothing. */
|
|
220
|
+
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;
|
|
221
|
+
/**
|
|
222
|
+
* Everything a module set says about var `K`'s ARGS side: extensions
|
|
223
|
+
* mounted on it, intersected with every same-name var declaration. This is
|
|
224
|
+
* what widens a fn schema whose input references `K` (see `WidenSchemaFns`)
|
|
225
|
+
* - `unknown` when the scope adds nothing, which intersects away.
|
|
226
|
+
*/
|
|
227
|
+
type VarArgsInScope<PL, K extends string> = VarExtensionArgsFor<PL, K> & ([VarShadowArgsEntry<Members<PL>, K>] extends [never] ? unknown : UnionToIntersection<VarShadowArgsEntry<Members<PL>, K>>);
|
|
228
|
+
type VarFieldKeys<I> = { [K in keyof I]: I[K] extends {
|
|
229
|
+
$var: true;
|
|
230
|
+
} ? K : never; }[keyof I];
|
|
231
|
+
/** Optional props put `undefined` in the key union; strip it so "no var
|
|
232
|
+
* fields" still reads as `never` and InputVarExtra stays `unknown`. */
|
|
233
|
+
type VarFields<I> = Exclude<VarFieldKeys<I>, undefined>;
|
|
234
|
+
/**
|
|
235
|
+
* Extra args a fn must accept because its INPUT references vars that `PL`
|
|
236
|
+
* extends. Whole-var input (`input: user`) merges at the top level; a var
|
|
237
|
+
* used as a field widens that field. `unknown` when nothing applies.
|
|
238
|
+
*/
|
|
239
|
+
type InputVarExtra<PL, I> = I extends {
|
|
240
|
+
$var: true;
|
|
241
|
+
name: infer N extends string;
|
|
242
|
+
} ? VarExtensionArgsFor<PL, N> : [VarFields<I>] extends [never] ? unknown : { [K in keyof I as I[K] extends {
|
|
243
|
+
$var: true;
|
|
244
|
+
} ? K : never]: I[K] extends {
|
|
245
|
+
$var: true;
|
|
246
|
+
name: infer N extends string;
|
|
247
|
+
} ? VarExtensionArgsFor<PL, N> : unknown; };
|
|
248
|
+
/**
|
|
249
|
+
* Same merge as {@link InputVarExtra}, on the PARSED side - what the
|
|
250
|
+
* handler sees on `c.input` after mounted extensions have been applied.
|
|
251
|
+
*/
|
|
252
|
+
type InputVarExtraOut<PL, I> = I extends {
|
|
253
|
+
$var: true;
|
|
254
|
+
name: infer N extends string;
|
|
255
|
+
} ? VarExtensionsFor<PL, N> : [VarFields<I>] extends [never] ? unknown : { [K in keyof I as I[K] extends {
|
|
256
|
+
$var: true;
|
|
257
|
+
} ? K : never]: I[K] extends {
|
|
258
|
+
$var: true;
|
|
259
|
+
name: infer N extends string;
|
|
260
|
+
} ? VarExtensionsFor<PL, N> : unknown; };
|
|
261
|
+
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;
|
|
262
|
+
/** Args added onto fn `K` by every extending `on` entry in `PL`. */
|
|
263
|
+
type ExtendedArgs<PL, K extends string> = UnionToIntersection<ExtEntryArgs<Members<PL>, K>>;
|
|
264
|
+
/**
|
|
265
|
+
* Rewrite a fn's type with the input extensions the module set `PL`
|
|
266
|
+
* mounts on it. This is how a plugin's extra field becomes REQUIRED at
|
|
267
|
+
* the call site, even though the fn itself never declared it. Storage
|
|
268
|
+
* members get the same scope rewrite as a db var's value ({@link
|
|
269
|
+
* WidenSchemaFns}): collections re-resolve row/`Where` types against
|
|
270
|
+
* mounted `v.extend` / customize.
|
|
271
|
+
*/
|
|
272
|
+
type ApplyOn<F, PL> = F extends StorageLike ? WidenSchemaFns<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;
|
|
273
|
+
type ApplyOns<Fns, PL> = { [P in keyof Fns]: ApplyOn<Fns[P], PL>; };
|
|
274
|
+
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;
|
|
275
|
+
/**
|
|
276
|
+
* Expand a `requires` set with every var DERIVED from a required source:
|
|
277
|
+
* requiring `request` makes `method`, `path`, ... non-null too, because
|
|
278
|
+
* they are computed from it.
|
|
279
|
+
*/
|
|
280
|
+
type WithDerived<PL, BasePL, Q> = Q | DerivedNamesEntry<Members<PL> | Members<BasePL>, Q>;
|
|
281
|
+
//#endregion
|
|
282
|
+
export { ApplyOn, ApplyOns, ExtendedArgs, FnsFrom, InputVarExtra, InputVarExtraOut, Interceptor, Module, ModuleFns, ModuleVars, OnDefaultContext, OnEntry, TargetMatches, VarArgsInScope, VarExtension, VarExtensionArgsFor, VarExtensionsFor, VarGetContext, VarSetContext, VarsFrom, WithDerived, collectFns, collectUsable, extendVar, isFn, isNamespace, isOn, isVarExtension, on };
|
|
283
|
+
//# sourceMappingURL=module.d.cts.map
|