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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +254 -0
  3. package/dist/error.cjs +67 -0
  4. package/dist/error.cjs.map +1 -0
  5. package/dist/error.d.cts +46 -0
  6. package/dist/error.d.mts +46 -0
  7. package/dist/error.mjs +64 -0
  8. package/dist/error.mjs.map +1 -0
  9. package/dist/fn.cjs +335 -0
  10. package/dist/fn.cjs.map +1 -0
  11. package/dist/fn.d.cts +312 -0
  12. package/dist/fn.d.mts +312 -0
  13. package/dist/fn.mjs +335 -0
  14. package/dist/fn.mjs.map +1 -0
  15. package/dist/index.cjs +37 -0
  16. package/dist/index.cjs.map +1 -0
  17. package/dist/index.d.cts +46 -0
  18. package/dist/index.d.mts +46 -0
  19. package/dist/index.mjs +24 -0
  20. package/dist/index.mjs.map +1 -0
  21. package/dist/module.cjs +111 -0
  22. package/dist/module.cjs.map +1 -0
  23. package/dist/module.d.cts +249 -0
  24. package/dist/module.d.mts +249 -0
  25. package/dist/module.mjs +102 -0
  26. package/dist/module.mjs.map +1 -0
  27. package/dist/plugins/http.cjs +185 -0
  28. package/dist/plugins/http.cjs.map +1 -0
  29. package/dist/plugins/http.d.cts +1261 -0
  30. package/dist/plugins/http.d.mts +1261 -0
  31. package/dist/plugins/http.mjs +175 -0
  32. package/dist/plugins/http.mjs.map +1 -0
  33. package/dist/plugins/read-only.cjs +19 -0
  34. package/dist/plugins/read-only.cjs.map +1 -0
  35. package/dist/plugins/read-only.d.cts +17 -0
  36. package/dist/plugins/read-only.d.mts +17 -0
  37. package/dist/plugins/read-only.mjs +19 -0
  38. package/dist/plugins/read-only.mjs.map +1 -0
  39. package/dist/schema.cjs +167 -0
  40. package/dist/schema.cjs.map +1 -0
  41. package/dist/schema.d.cts +225 -0
  42. package/dist/schema.d.mts +225 -0
  43. package/dist/schema.mjs +159 -0
  44. package/dist/schema.mjs.map +1 -0
  45. package/dist/scope.d.cts +18 -0
  46. package/dist/scope.d.mts +18 -0
  47. package/dist/storage.cjs +256 -0
  48. package/dist/storage.cjs.map +1 -0
  49. package/dist/storage.d.cts +195 -0
  50. package/dist/storage.d.mts +195 -0
  51. package/dist/storage.mjs +253 -0
  52. package/dist/storage.mjs.map +1 -0
  53. package/dist/types.d.cts +8 -0
  54. package/dist/types.d.mts +8 -0
  55. package/dist/var.cjs +162 -0
  56. package/dist/var.cjs.map +1 -0
  57. package/dist/var.d.cts +36 -0
  58. package/dist/var.d.mts +36 -0
  59. package/dist/var.mjs +154 -0
  60. package/dist/var.mjs.map +1 -0
  61. package/package.json +92 -0
package/dist/var.cjs ADDED
@@ -0,0 +1,162 @@
1
+ const require_error = require("./error.cjs");
2
+ const require_schema = require("./schema.cjs");
3
+ const require_module = require("./module.cjs");
4
+ const require_fn = require("./fn.cjs");
5
+ //#region src/var.ts
6
+ const varRegistry = /* @__PURE__ */ new Map();
7
+ const makeVar = (name, options = {}) => {
8
+ const schema = options.schema === void 0 ? void 0 : require_schema.asType(options.schema);
9
+ const def = {
10
+ $var: true,
11
+ name,
12
+ default: options.default,
13
+ schema,
14
+ $accessor: options.accessor === true,
15
+ $derive: options.derive,
16
+ customize: (opts) => makeVar(name, {
17
+ ...options,
18
+ default: def.default,
19
+ schema: opts.schema({
20
+ ...require_schema.vTypes,
21
+ fn: require_fn.fnImpl,
22
+ add: (shape) => ({
23
+ name: "object",
24
+ shape: {
25
+ ...def.schema?.shape ?? {},
26
+ ...shape
27
+ }
28
+ }),
29
+ replace: (schema) => schema
30
+ })
31
+ })
32
+ };
33
+ varRegistry.set(name, def);
34
+ return def;
35
+ };
36
+ /**
37
+ * Derive a var from another: computed lazily off the source on every read,
38
+ * so setting the source "sets" every var derived from it. Writing a
39
+ * derived var directly shadows the computation for that scope.
40
+ */
41
+ const deriveVar = (name, source, get) => makeVar(name, { derive: {
42
+ source: source.name,
43
+ get
44
+ } });
45
+ /** Cells materialize lazily from the registry on first touch. */
46
+ const getCell = (cells, name) => {
47
+ const existing = cells[name];
48
+ if (existing) return existing;
49
+ const def = varRegistry.get(name);
50
+ const cell = {
51
+ value: def?.$derive ? void 0 : def?.$accessor ? {} : def?.default,
52
+ derive: def?.$derive,
53
+ shadowed: false,
54
+ accumulate: def?.$accessor === true
55
+ };
56
+ cells[name] = cell;
57
+ return cell;
58
+ };
59
+ /** The current value, deriveds computed off their source. A name never
60
+ * declared and never written reads `undefined` WITHOUT materializing a
61
+ * cell - property probes on the context must not pollute the scope. */
62
+ const readVar = (cells, name) => {
63
+ if (!cells[name] && !varRegistry.has(name)) return void 0;
64
+ const cell = getCell(cells, name);
65
+ if (cell.derive && !cell.shadowed) {
66
+ const src = readVar(cells, cell.derive.source);
67
+ return src == null ? null : cell.derive.get(src);
68
+ }
69
+ return cell.value;
70
+ };
71
+ /**
72
+ * A handler's `c`: its own fields (`name`, `value`, `fn`) in front, every
73
+ * other property a RAW read of the scope's vars - so a handler reading
74
+ * `c.<var>` cannot recurse into its own get hook.
75
+ */
76
+ const handlerContext = (cells, base) => new Proxy(base, {
77
+ get: (t, prop) => prop in t ? t[prop] : typeof prop === "string" ? readVar(cells, prop) : void 0,
78
+ has: (t, prop) => prop in t || typeof prop === "string" && prop in cells
79
+ });
80
+ const isThenable = (value) => typeof value?.then === "function";
81
+ /**
82
+ * Every write funnels here: the readonly lock beats everything, then the
83
+ * matching `var.set.<name>` entries run SYNCHRONOUSLY around the actual
84
+ * write - call `next()` to land it, skip to cancel, throw to abort. The
85
+ * bare "*" target means "every fn", never "every var write".
86
+ */
87
+ const writeVar = (frame, name, value) => {
88
+ if (frame.lockedBy) throw new require_error.ValidationError(`${frame.key}.readonly`, `"${frame.lockedBy}" is readonly: attempted to write var "${name}"`);
89
+ const cell = getCell(frame.cells, name);
90
+ const merged = () => {
91
+ const current = cell.derive && !cell.shadowed ? readVar(frame.cells, name) : cell.value;
92
+ return {
93
+ ...typeof current === "object" && current !== null ? current : {},
94
+ ...value
95
+ };
96
+ };
97
+ const next = cell.accumulate ? merged() : value;
98
+ const apply = () => {
99
+ cell.value = next;
100
+ if (cell.derive) cell.shadowed = true;
101
+ };
102
+ const hooks = frame.entries.filter((e) => e.target !== "*" && require_module.matchesTarget(e.target, `var.set.${name}`));
103
+ if (hooks.length === 0) return apply();
104
+ hooks.reduceRight((proceed, entry) => () => {
105
+ const result = entry.handler(handlerContext(frame.cells, {
106
+ name,
107
+ value: next,
108
+ fn: frame.key
109
+ }), proceed);
110
+ if (isThenable(result)) throw new require_error.ValidationError(`${frame.key}.set`, `var-set handlers must be synchronous - "${String(entry.target)}" returned a promise`);
111
+ }, apply)();
112
+ };
113
+ /**
114
+ * Context reads funnel here: matching `var.get.<name>` entries run
115
+ * SYNCHRONOUSLY around the raw read - `next()` yields the stored value and
116
+ * whatever the handler returns becomes the read result. Internal reads
117
+ * stay raw: derive sources, handler contexts (so a handler reading a var
118
+ * off `c` cannot recurse into itself) and contract checks all see the
119
+ * cell as-is.
120
+ */
121
+ const readVarThrough = (frame, name) => {
122
+ const hooks = frame.entries.filter((e) => e.target !== "*" && require_module.matchesTarget(e.target, `var.get.${name}`));
123
+ if (hooks.length === 0) return readVar(frame.cells, name);
124
+ return hooks.reduceRight((proceed, entry) => () => {
125
+ const result = entry.handler(handlerContext(frame.cells, {
126
+ name,
127
+ fn: frame.key
128
+ }), proceed);
129
+ if (isThenable(result)) throw new require_error.ValidationError(`${frame.key}.get`, `var-get handlers must be synchronous - "${String(entry.target)}" returned a promise`);
130
+ return result;
131
+ }, () => readVar(frame.cells, name))();
132
+ };
133
+ /**
134
+ * The fn context for one frame: `base` carries the fixed surface (input,
135
+ * error, fn, types, the bound `use` fns, internal symbols) and EVERY
136
+ * other property is a var - read it directly (`c.session.userId`) and
137
+ * write it by plain assignment (`c.session = {...}`). Reads route through
138
+ * the `var.get` entries, writes through `writeVar` (readonly lock,
139
+ * `var.set` entries), and both are always synchronous. Base keys shadow
140
+ * vars of the same name, both ways.
141
+ */
142
+ const contextScope = (frame, base) => new Proxy(base, {
143
+ get: (t, prop, receiver) => prop in t ? Reflect.get(t, prop, receiver) : typeof prop === "string" ? readVarThrough(frame, prop) : void 0,
144
+ set: (t, prop, value, receiver) => {
145
+ if (typeof prop !== "string" || prop in t) return Reflect.set(t, prop, value, receiver);
146
+ writeVar(frame, prop, value);
147
+ return true;
148
+ },
149
+ has: (t, prop) => prop in t || typeof prop === "string"
150
+ });
151
+ //#endregion
152
+ exports.contextScope = contextScope;
153
+ exports.deriveVar = deriveVar;
154
+ exports.getCell = getCell;
155
+ exports.handlerContext = handlerContext;
156
+ exports.makeVar = makeVar;
157
+ exports.readVar = readVar;
158
+ exports.readVarThrough = readVarThrough;
159
+ exports.varRegistry = varRegistry;
160
+ exports.writeVar = writeVar;
161
+
162
+ //# sourceMappingURL=var.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"var.cjs","names":["asType","vTypes","fnImpl","ValidationError","matchesTarget"],"sources":["../src/var.ts"],"sourcesContent":["import { ValidationError } from \"./error\";\n// Cycle with fn.ts (it imports our cells): fine, `fnImpl` is only touched\n// inside the lazy `customize` closure, never during module evaluation.\nimport { type Fn, fnImpl } from \"./fn\";\nimport { matchesTarget, type OnEntry } from \"./module\";\nimport {\n\tasType,\n\ttype DefineOutput,\n\ttype InferArgs,\n\ttype InferInput,\n\ttype TypeDefination,\n\tvTypes,\n} from \"./schema\";\nimport type { LiteralString, Prettify } from \"./types\";\n\nexport interface VarDefination<\n\tN extends LiteralString,\n\tT,\n\tSchema = unknown,\n\tSource extends string = never,\n> {\n\t$var: true;\n\tname: N;\n\tdefault?: T;\n\t/** Kept in the type so the var can also be used as an input field. */\n\tschema?: Schema;\n\ttype?: T;\n\t/** Phantom: the var this one derives from - `requires` on the source\n\t * makes this one non-null too. */\n\t$source?: Source;\n\tcustomize: <S>(options: {\n\t\tschema: (v: VarCustomizer<T>) => S;\n\t}) => VarDefination<N, InferInput<S>, S>;\n}\n\nexport type ValueOfVar<SV> =\n\tSV extends VarDefination<any, infer T, any, any> ? T : never;\n\nexport type NameOfVar<SV> =\n\tSV extends VarDefination<infer N, any, any, any> ? N : never;\n\n/**\n * The toolkit a `customize` callback receives. It is a SUPERSET of `v`'s\n * type constructors on purpose - the callback parameter shadows `v`, so\n * `(v) => v.add({ role: v.string() })` has to keep working.\n */\nexport type VarCustomizer<T> = typeof vTypes & {\n\t/** Declare a fn-typed field: bare `fn` is \"any function\",\n\t * `fn({ input, output })` a specific signature. */\n\tfn: Fn;\n\t// The args side goes through InferArgs, not a plain key-for-key map, so\n\t// an `optional`/defaulted added field is optional to SEND as well - the\n\t// same rule `v.object` applies to its shape.\n\tadd: <S>(\n\t\tshape: S,\n\t) => TypeDefination<InferArgs<S>, Prettify<NonNullable<T> & DefineOutput<S>>>;\n\treplace: <S>(schema: S) => S;\n};\n\nexport type VarMap = Record<string, VarDefination<any, any, any, any>>;\n\nexport const varRegistry = new Map<string, any>();\n\nexport const makeVar = (name: string, options: any = {}): any => {\n\tconst schema =\n\t\toptions.schema === undefined ? undefined : asType(options.schema);\n\tconst def: any = {\n\t\t$var: true,\n\t\tname,\n\t\tdefault: options.default,\n\t\tschema,\n\t\t$accessor: options.accessor === true,\n\t\t$derive: options.derive,\n\t\tcustomize: (opts: any) =>\n\t\t\tmakeVar(name, {\n\t\t\t\t...options,\n\t\t\t\tdefault: def.default,\n\t\t\t\tschema: opts.schema({\n\t\t\t\t\t...vTypes,\n\t\t\t\t\tfn: fnImpl,\n\t\t\t\t\tadd: (shape: any) => ({\n\t\t\t\t\t\tname: \"object\",\n\t\t\t\t\t\tshape: { ...((def.schema?.shape as any) ?? {}), ...shape },\n\t\t\t\t\t}),\n\t\t\t\t\treplace: (schema: any) => schema,\n\t\t\t\t}),\n\t\t\t}),\n\t};\n\tvarRegistry.set(name, def);\n\treturn def;\n};\n\n/**\n * Derive a var from another: computed lazily off the source on every read,\n * so setting the source \"sets\" every var derived from it. Writing a\n * derived var directly shadows the computation for that scope.\n */\nexport const deriveVar = (name: string, source: any, get: any): any =>\n\tmakeVar(name, { derive: { source: source.name, get } });\n\n/* ---------------------------------- cells ---------------------------------- */\n\n/**\n * One var's state within a scope: the current value, plus how reads and\n * writes behave for it (derived computation, record accumulation).\n */\nexport type Cell = {\n\tvalue: unknown;\n\tderive?: { source: string; get: (value: any) => any };\n\t/** A direct write to a derived var shadows its computation. */\n\tshadowed: boolean;\n\t/** Record var: `set()` merges instead of replacing. */\n\taccumulate: boolean;\n};\n\nexport type Cells = Record<string, Cell>;\n\n/** Cells materialize lazily from the registry on first touch. */\nexport const getCell = (cells: Cells, name: string): Cell => {\n\tconst existing = cells[name];\n\tif (existing) return existing;\n\tconst def = varRegistry.get(name);\n\tconst cell: Cell = {\n\t\tvalue: def?.$derive ? undefined : def?.$accessor ? {} : def?.default,\n\t\tderive: def?.$derive,\n\t\tshadowed: false,\n\t\taccumulate: def?.$accessor === true,\n\t};\n\tcells[name] = cell;\n\treturn cell;\n};\n\n/** The current value, deriveds computed off their source. A name never\n * declared and never written reads `undefined` WITHOUT materializing a\n * cell - property probes on the context must not pollute the scope. */\nexport const readVar = (cells: Cells, name: string): unknown => {\n\tif (!cells[name] && !varRegistry.has(name)) return undefined;\n\tconst cell = getCell(cells, name);\n\tif (cell.derive && !cell.shadowed) {\n\t\tconst src = readVar(cells, cell.derive.source);\n\t\treturn src == null ? null : cell.derive.get(src);\n\t}\n\treturn cell.value;\n};\n\n/**\n * A handler's `c`: its own fields (`name`, `value`, `fn`) in front, every\n * other property a RAW read of the scope's vars - so a handler reading\n * `c.<var>` cannot recurse into its own get hook.\n */\nexport const handlerContext = (\n\tcells: Cells,\n\tbase: Record<string, unknown>,\n): Record<string, unknown> =>\n\tnew Proxy(base, {\n\t\tget: (t, prop) =>\n\t\t\tprop in t\n\t\t\t\t? (t as any)[prop]\n\t\t\t\t: typeof prop === \"string\"\n\t\t\t\t\t? readVar(cells, prop)\n\t\t\t\t\t: undefined,\n\t\thas: (t, prop) => prop in t || (typeof prop === \"string\" && prop in cells),\n\t});\n\n/* ---------------------------------- frame ---------------------------------- */\n\n/**\n * What a handle needs to know about the fn frame it was created in: the\n * scope's cells, plus everything write behavior depends on there.\n */\nexport type Frame = {\n\tcells: Cells;\n\tkey: string;\n\tlockedBy: string | undefined;\n\tentries: readonly OnEntry<string>[];\n};\n\nconst isThenable = (value: any): value is Promise<unknown> =>\n\ttypeof value?.then === \"function\";\n\n/**\n * Every write funnels here: the readonly lock beats everything, then the\n * matching `var.set.<name>` entries run SYNCHRONOUSLY around the actual\n * write - call `next()` to land it, skip to cancel, throw to abort. The\n * bare \"*\" target means \"every fn\", never \"every var write\".\n */\nexport const writeVar = (frame: Frame, name: string, value: unknown) => {\n\tif (frame.lockedBy) {\n\t\tthrow new ValidationError(\n\t\t\t`${frame.key}.readonly`,\n\t\t\t`\"${frame.lockedBy}\" is readonly: attempted to write var \"${name}\"`,\n\t\t);\n\t}\n\tconst cell = getCell(frame.cells, name);\n\tconst merged = () => {\n\t\tconst current =\n\t\t\tcell.derive && !cell.shadowed ? readVar(frame.cells, name) : cell.value;\n\t\tconst base = typeof current === \"object\" && current !== null ? current : {};\n\t\treturn { ...base, ...(value as Record<string, unknown>) };\n\t};\n\tconst next = cell.accumulate ? merged() : value;\n\tconst apply = () => {\n\t\tcell.value = next;\n\t\tif (cell.derive) cell.shadowed = true;\n\t};\n\tconst hooks = frame.entries.filter(\n\t\t(e) => e.target !== \"*\" && matchesTarget(e.target, `var.set.${name}`),\n\t);\n\tif (hooks.length === 0) return apply();\n\tconst chain = hooks.reduceRight<() => void>(\n\t\t(proceed, entry) => () => {\n\t\t\tconst result = entry.handler(\n\t\t\t\thandlerContext(frame.cells, {\n\t\t\t\t\tname,\n\t\t\t\t\tvalue: next,\n\t\t\t\t\tfn: frame.key,\n\t\t\t\t}) as any,\n\t\t\t\tproceed as any,\n\t\t\t);\n\t\t\tif (isThenable(result)) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t`${frame.key}.set`,\n\t\t\t\t\t`var-set handlers must be synchronous - \"${String(entry.target)}\" returned a promise`,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\tapply,\n\t);\n\tchain();\n};\n\n/**\n * Context reads funnel here: matching `var.get.<name>` entries run\n * SYNCHRONOUSLY around the raw read - `next()` yields the stored value and\n * whatever the handler returns becomes the read result. Internal reads\n * stay raw: derive sources, handler contexts (so a handler reading a var\n * off `c` cannot recurse into itself) and contract checks all see the\n * cell as-is.\n */\nexport const readVarThrough = (frame: Frame, name: string): unknown => {\n\tconst hooks = frame.entries.filter(\n\t\t(e) => e.target !== \"*\" && matchesTarget(e.target, `var.get.${name}`),\n\t);\n\tif (hooks.length === 0) return readVar(frame.cells, name);\n\tconst chain = hooks.reduceRight<() => unknown>(\n\t\t(proceed, entry) => () => {\n\t\t\tconst result = entry.handler(\n\t\t\t\thandlerContext(frame.cells, {\n\t\t\t\t\tname,\n\t\t\t\t\tfn: frame.key,\n\t\t\t\t}) as any,\n\t\t\t\tproceed as any,\n\t\t\t);\n\t\t\tif (isThenable(result)) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t`${frame.key}.get`,\n\t\t\t\t\t`var-get handlers must be synchronous - \"${String(entry.target)}\" returned a promise`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\t\t() => readVar(frame.cells, name),\n\t);\n\treturn chain();\n};\n\n/* ---------------------------------- scope ---------------------------------- */\n\n/**\n * The fn context for one frame: `base` carries the fixed surface (input,\n * error, fn, types, the bound `use` fns, internal symbols) and EVERY\n * other property is a var - read it directly (`c.session.userId`) and\n * write it by plain assignment (`c.session = {...}`). Reads route through\n * the `var.get` entries, writes through `writeVar` (readonly lock,\n * `var.set` entries), and both are always synchronous. Base keys shadow\n * vars of the same name, both ways.\n */\nexport const contextScope = (frame: Frame, base: object): any =>\n\tnew Proxy(base, {\n\t\tget: (t, prop, receiver) =>\n\t\t\tprop in t\n\t\t\t\t? Reflect.get(t, prop, receiver)\n\t\t\t\t: typeof prop === \"string\"\n\t\t\t\t\t? readVarThrough(frame, prop)\n\t\t\t\t\t: undefined,\n\t\tset: (t, prop, value, receiver) => {\n\t\t\tif (typeof prop !== \"string\" || prop in t) {\n\t\t\t\treturn Reflect.set(t, prop, value, receiver);\n\t\t\t}\n\t\t\twriteVar(frame, prop, value);\n\t\t\treturn true;\n\t\t},\n\t\thas: (t, prop) => prop in t || typeof prop === \"string\",\n\t});\n"],"mappings":"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"}
package/dist/var.d.cts ADDED
@@ -0,0 +1,36 @@
1
+ import { LiteralString, Prettify } from "./types.cjs";
2
+ import { DefineOutput, InferArgs, InferInput, TypeDefination, vTypes } from "./schema.cjs";
3
+ import { Fn } from "./fn.cjs";
4
+ //#region src/var.d.ts
5
+ interface VarDefination<N extends LiteralString, T, Schema = unknown, Source extends string = never> {
6
+ $var: true;
7
+ name: N;
8
+ default?: T;
9
+ /** Kept in the type so the var can also be used as an input field. */
10
+ schema?: Schema;
11
+ type?: T;
12
+ /** Phantom: the var this one derives from - `requires` on the source
13
+ * makes this one non-null too. */
14
+ $source?: Source;
15
+ customize: <S>(options: {
16
+ schema: (v: VarCustomizer<T>) => S;
17
+ }) => VarDefination<N, InferInput<S>, S>;
18
+ }
19
+ type ValueOfVar<SV> = SV extends VarDefination<any, infer T, any, any> ? T : never;
20
+ type NameOfVar<SV> = SV extends VarDefination<infer N, any, any, any> ? N : never;
21
+ /**
22
+ * The toolkit a `customize` callback receives. It is a SUPERSET of `v`'s
23
+ * type constructors on purpose - the callback parameter shadows `v`, so
24
+ * `(v) => v.add({ role: v.string() })` has to keep working.
25
+ */
26
+ type VarCustomizer<T> = typeof vTypes & {
27
+ /** Declare a fn-typed field: bare `fn` is "any function",
28
+ * `fn({ input, output })` a specific signature. */
29
+ fn: Fn;
30
+ add: <S>(shape: S) => TypeDefination<InferArgs<S>, Prettify<NonNullable<T> & DefineOutput<S>>>;
31
+ replace: <S>(schema: S) => S;
32
+ };
33
+ type VarMap = Record<string, VarDefination<any, any, any, any>>;
34
+ //#endregion
35
+ export { NameOfVar, ValueOfVar, VarCustomizer, VarDefination, VarMap };
36
+ //# sourceMappingURL=var.d.cts.map
package/dist/var.d.mts ADDED
@@ -0,0 +1,36 @@
1
+ import { LiteralString, Prettify } from "./types.mjs";
2
+ import { DefineOutput, InferArgs, InferInput, TypeDefination, vTypes } from "./schema.mjs";
3
+ import { Fn } from "./fn.mjs";
4
+ //#region src/var.d.ts
5
+ interface VarDefination<N extends LiteralString, T, Schema = unknown, Source extends string = never> {
6
+ $var: true;
7
+ name: N;
8
+ default?: T;
9
+ /** Kept in the type so the var can also be used as an input field. */
10
+ schema?: Schema;
11
+ type?: T;
12
+ /** Phantom: the var this one derives from - `requires` on the source
13
+ * makes this one non-null too. */
14
+ $source?: Source;
15
+ customize: <S>(options: {
16
+ schema: (v: VarCustomizer<T>) => S;
17
+ }) => VarDefination<N, InferInput<S>, S>;
18
+ }
19
+ type ValueOfVar<SV> = SV extends VarDefination<any, infer T, any, any> ? T : never;
20
+ type NameOfVar<SV> = SV extends VarDefination<infer N, any, any, any> ? N : never;
21
+ /**
22
+ * The toolkit a `customize` callback receives. It is a SUPERSET of `v`'s
23
+ * type constructors on purpose - the callback parameter shadows `v`, so
24
+ * `(v) => v.add({ role: v.string() })` has to keep working.
25
+ */
26
+ type VarCustomizer<T> = typeof vTypes & {
27
+ /** Declare a fn-typed field: bare `fn` is "any function",
28
+ * `fn({ input, output })` a specific signature. */
29
+ fn: Fn;
30
+ add: <S>(shape: S) => TypeDefination<InferArgs<S>, Prettify<NonNullable<T> & DefineOutput<S>>>;
31
+ replace: <S>(schema: S) => S;
32
+ };
33
+ type VarMap = Record<string, VarDefination<any, any, any, any>>;
34
+ //#endregion
35
+ export { NameOfVar, ValueOfVar, VarCustomizer, VarDefination, VarMap };
36
+ //# sourceMappingURL=var.d.mts.map
package/dist/var.mjs ADDED
@@ -0,0 +1,154 @@
1
+ import { ValidationError } from "./error.mjs";
2
+ import { asType, vTypes } from "./schema.mjs";
3
+ import { matchesTarget } from "./module.mjs";
4
+ import { fnImpl } from "./fn.mjs";
5
+ //#region src/var.ts
6
+ const varRegistry = /* @__PURE__ */ new Map();
7
+ const makeVar = (name, options = {}) => {
8
+ const schema = options.schema === void 0 ? void 0 : asType(options.schema);
9
+ const def = {
10
+ $var: true,
11
+ name,
12
+ default: options.default,
13
+ schema,
14
+ $accessor: options.accessor === true,
15
+ $derive: options.derive,
16
+ customize: (opts) => makeVar(name, {
17
+ ...options,
18
+ default: def.default,
19
+ schema: opts.schema({
20
+ ...vTypes,
21
+ fn: fnImpl,
22
+ add: (shape) => ({
23
+ name: "object",
24
+ shape: {
25
+ ...def.schema?.shape ?? {},
26
+ ...shape
27
+ }
28
+ }),
29
+ replace: (schema) => schema
30
+ })
31
+ })
32
+ };
33
+ varRegistry.set(name, def);
34
+ return def;
35
+ };
36
+ /**
37
+ * Derive a var from another: computed lazily off the source on every read,
38
+ * so setting the source "sets" every var derived from it. Writing a
39
+ * derived var directly shadows the computation for that scope.
40
+ */
41
+ const deriveVar = (name, source, get) => makeVar(name, { derive: {
42
+ source: source.name,
43
+ get
44
+ } });
45
+ /** Cells materialize lazily from the registry on first touch. */
46
+ const getCell = (cells, name) => {
47
+ const existing = cells[name];
48
+ if (existing) return existing;
49
+ const def = varRegistry.get(name);
50
+ const cell = {
51
+ value: def?.$derive ? void 0 : def?.$accessor ? {} : def?.default,
52
+ derive: def?.$derive,
53
+ shadowed: false,
54
+ accumulate: def?.$accessor === true
55
+ };
56
+ cells[name] = cell;
57
+ return cell;
58
+ };
59
+ /** The current value, deriveds computed off their source. A name never
60
+ * declared and never written reads `undefined` WITHOUT materializing a
61
+ * cell - property probes on the context must not pollute the scope. */
62
+ const readVar = (cells, name) => {
63
+ if (!cells[name] && !varRegistry.has(name)) return void 0;
64
+ const cell = getCell(cells, name);
65
+ if (cell.derive && !cell.shadowed) {
66
+ const src = readVar(cells, cell.derive.source);
67
+ return src == null ? null : cell.derive.get(src);
68
+ }
69
+ return cell.value;
70
+ };
71
+ /**
72
+ * A handler's `c`: its own fields (`name`, `value`, `fn`) in front, every
73
+ * other property a RAW read of the scope's vars - so a handler reading
74
+ * `c.<var>` cannot recurse into its own get hook.
75
+ */
76
+ const handlerContext = (cells, base) => new Proxy(base, {
77
+ get: (t, prop) => prop in t ? t[prop] : typeof prop === "string" ? readVar(cells, prop) : void 0,
78
+ has: (t, prop) => prop in t || typeof prop === "string" && prop in cells
79
+ });
80
+ const isThenable = (value) => typeof value?.then === "function";
81
+ /**
82
+ * Every write funnels here: the readonly lock beats everything, then the
83
+ * matching `var.set.<name>` entries run SYNCHRONOUSLY around the actual
84
+ * write - call `next()` to land it, skip to cancel, throw to abort. The
85
+ * bare "*" target means "every fn", never "every var write".
86
+ */
87
+ const writeVar = (frame, name, value) => {
88
+ if (frame.lockedBy) throw new ValidationError(`${frame.key}.readonly`, `"${frame.lockedBy}" is readonly: attempted to write var "${name}"`);
89
+ const cell = getCell(frame.cells, name);
90
+ const merged = () => {
91
+ const current = cell.derive && !cell.shadowed ? readVar(frame.cells, name) : cell.value;
92
+ return {
93
+ ...typeof current === "object" && current !== null ? current : {},
94
+ ...value
95
+ };
96
+ };
97
+ const next = cell.accumulate ? merged() : value;
98
+ const apply = () => {
99
+ cell.value = next;
100
+ if (cell.derive) cell.shadowed = true;
101
+ };
102
+ const hooks = frame.entries.filter((e) => e.target !== "*" && matchesTarget(e.target, `var.set.${name}`));
103
+ if (hooks.length === 0) return apply();
104
+ hooks.reduceRight((proceed, entry) => () => {
105
+ const result = entry.handler(handlerContext(frame.cells, {
106
+ name,
107
+ value: next,
108
+ fn: frame.key
109
+ }), proceed);
110
+ if (isThenable(result)) throw new ValidationError(`${frame.key}.set`, `var-set handlers must be synchronous - "${String(entry.target)}" returned a promise`);
111
+ }, apply)();
112
+ };
113
+ /**
114
+ * Context reads funnel here: matching `var.get.<name>` entries run
115
+ * SYNCHRONOUSLY around the raw read - `next()` yields the stored value and
116
+ * whatever the handler returns becomes the read result. Internal reads
117
+ * stay raw: derive sources, handler contexts (so a handler reading a var
118
+ * off `c` cannot recurse into itself) and contract checks all see the
119
+ * cell as-is.
120
+ */
121
+ const readVarThrough = (frame, name) => {
122
+ const hooks = frame.entries.filter((e) => e.target !== "*" && matchesTarget(e.target, `var.get.${name}`));
123
+ if (hooks.length === 0) return readVar(frame.cells, name);
124
+ return hooks.reduceRight((proceed, entry) => () => {
125
+ const result = entry.handler(handlerContext(frame.cells, {
126
+ name,
127
+ fn: frame.key
128
+ }), proceed);
129
+ if (isThenable(result)) throw new ValidationError(`${frame.key}.get`, `var-get handlers must be synchronous - "${String(entry.target)}" returned a promise`);
130
+ return result;
131
+ }, () => readVar(frame.cells, name))();
132
+ };
133
+ /**
134
+ * The fn context for one frame: `base` carries the fixed surface (input,
135
+ * error, fn, types, the bound `use` fns, internal symbols) and EVERY
136
+ * other property is a var - read it directly (`c.session.userId`) and
137
+ * write it by plain assignment (`c.session = {...}`). Reads route through
138
+ * the `var.get` entries, writes through `writeVar` (readonly lock,
139
+ * `var.set` entries), and both are always synchronous. Base keys shadow
140
+ * vars of the same name, both ways.
141
+ */
142
+ const contextScope = (frame, base) => new Proxy(base, {
143
+ get: (t, prop, receiver) => prop in t ? Reflect.get(t, prop, receiver) : typeof prop === "string" ? readVarThrough(frame, prop) : void 0,
144
+ set: (t, prop, value, receiver) => {
145
+ if (typeof prop !== "string" || prop in t) return Reflect.set(t, prop, value, receiver);
146
+ writeVar(frame, prop, value);
147
+ return true;
148
+ },
149
+ has: (t, prop) => prop in t || typeof prop === "string"
150
+ });
151
+ //#endregion
152
+ export { contextScope, deriveVar, getCell, handlerContext, makeVar, readVar, readVarThrough, varRegistry, writeVar };
153
+
154
+ //# sourceMappingURL=var.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"var.mjs","names":[],"sources":["../src/var.ts"],"sourcesContent":["import { ValidationError } from \"./error\";\n// Cycle with fn.ts (it imports our cells): fine, `fnImpl` is only touched\n// inside the lazy `customize` closure, never during module evaluation.\nimport { type Fn, fnImpl } from \"./fn\";\nimport { matchesTarget, type OnEntry } from \"./module\";\nimport {\n\tasType,\n\ttype DefineOutput,\n\ttype InferArgs,\n\ttype InferInput,\n\ttype TypeDefination,\n\tvTypes,\n} from \"./schema\";\nimport type { LiteralString, Prettify } from \"./types\";\n\nexport interface VarDefination<\n\tN extends LiteralString,\n\tT,\n\tSchema = unknown,\n\tSource extends string = never,\n> {\n\t$var: true;\n\tname: N;\n\tdefault?: T;\n\t/** Kept in the type so the var can also be used as an input field. */\n\tschema?: Schema;\n\ttype?: T;\n\t/** Phantom: the var this one derives from - `requires` on the source\n\t * makes this one non-null too. */\n\t$source?: Source;\n\tcustomize: <S>(options: {\n\t\tschema: (v: VarCustomizer<T>) => S;\n\t}) => VarDefination<N, InferInput<S>, S>;\n}\n\nexport type ValueOfVar<SV> =\n\tSV extends VarDefination<any, infer T, any, any> ? T : never;\n\nexport type NameOfVar<SV> =\n\tSV extends VarDefination<infer N, any, any, any> ? N : never;\n\n/**\n * The toolkit a `customize` callback receives. It is a SUPERSET of `v`'s\n * type constructors on purpose - the callback parameter shadows `v`, so\n * `(v) => v.add({ role: v.string() })` has to keep working.\n */\nexport type VarCustomizer<T> = typeof vTypes & {\n\t/** Declare a fn-typed field: bare `fn` is \"any function\",\n\t * `fn({ input, output })` a specific signature. */\n\tfn: Fn;\n\t// The args side goes through InferArgs, not a plain key-for-key map, so\n\t// an `optional`/defaulted added field is optional to SEND as well - the\n\t// same rule `v.object` applies to its shape.\n\tadd: <S>(\n\t\tshape: S,\n\t) => TypeDefination<InferArgs<S>, Prettify<NonNullable<T> & DefineOutput<S>>>;\n\treplace: <S>(schema: S) => S;\n};\n\nexport type VarMap = Record<string, VarDefination<any, any, any, any>>;\n\nexport const varRegistry = new Map<string, any>();\n\nexport const makeVar = (name: string, options: any = {}): any => {\n\tconst schema =\n\t\toptions.schema === undefined ? undefined : asType(options.schema);\n\tconst def: any = {\n\t\t$var: true,\n\t\tname,\n\t\tdefault: options.default,\n\t\tschema,\n\t\t$accessor: options.accessor === true,\n\t\t$derive: options.derive,\n\t\tcustomize: (opts: any) =>\n\t\t\tmakeVar(name, {\n\t\t\t\t...options,\n\t\t\t\tdefault: def.default,\n\t\t\t\tschema: opts.schema({\n\t\t\t\t\t...vTypes,\n\t\t\t\t\tfn: fnImpl,\n\t\t\t\t\tadd: (shape: any) => ({\n\t\t\t\t\t\tname: \"object\",\n\t\t\t\t\t\tshape: { ...((def.schema?.shape as any) ?? {}), ...shape },\n\t\t\t\t\t}),\n\t\t\t\t\treplace: (schema: any) => schema,\n\t\t\t\t}),\n\t\t\t}),\n\t};\n\tvarRegistry.set(name, def);\n\treturn def;\n};\n\n/**\n * Derive a var from another: computed lazily off the source on every read,\n * so setting the source \"sets\" every var derived from it. Writing a\n * derived var directly shadows the computation for that scope.\n */\nexport const deriveVar = (name: string, source: any, get: any): any =>\n\tmakeVar(name, { derive: { source: source.name, get } });\n\n/* ---------------------------------- cells ---------------------------------- */\n\n/**\n * One var's state within a scope: the current value, plus how reads and\n * writes behave for it (derived computation, record accumulation).\n */\nexport type Cell = {\n\tvalue: unknown;\n\tderive?: { source: string; get: (value: any) => any };\n\t/** A direct write to a derived var shadows its computation. */\n\tshadowed: boolean;\n\t/** Record var: `set()` merges instead of replacing. */\n\taccumulate: boolean;\n};\n\nexport type Cells = Record<string, Cell>;\n\n/** Cells materialize lazily from the registry on first touch. */\nexport const getCell = (cells: Cells, name: string): Cell => {\n\tconst existing = cells[name];\n\tif (existing) return existing;\n\tconst def = varRegistry.get(name);\n\tconst cell: Cell = {\n\t\tvalue: def?.$derive ? undefined : def?.$accessor ? {} : def?.default,\n\t\tderive: def?.$derive,\n\t\tshadowed: false,\n\t\taccumulate: def?.$accessor === true,\n\t};\n\tcells[name] = cell;\n\treturn cell;\n};\n\n/** The current value, deriveds computed off their source. A name never\n * declared and never written reads `undefined` WITHOUT materializing a\n * cell - property probes on the context must not pollute the scope. */\nexport const readVar = (cells: Cells, name: string): unknown => {\n\tif (!cells[name] && !varRegistry.has(name)) return undefined;\n\tconst cell = getCell(cells, name);\n\tif (cell.derive && !cell.shadowed) {\n\t\tconst src = readVar(cells, cell.derive.source);\n\t\treturn src == null ? null : cell.derive.get(src);\n\t}\n\treturn cell.value;\n};\n\n/**\n * A handler's `c`: its own fields (`name`, `value`, `fn`) in front, every\n * other property a RAW read of the scope's vars - so a handler reading\n * `c.<var>` cannot recurse into its own get hook.\n */\nexport const handlerContext = (\n\tcells: Cells,\n\tbase: Record<string, unknown>,\n): Record<string, unknown> =>\n\tnew Proxy(base, {\n\t\tget: (t, prop) =>\n\t\t\tprop in t\n\t\t\t\t? (t as any)[prop]\n\t\t\t\t: typeof prop === \"string\"\n\t\t\t\t\t? readVar(cells, prop)\n\t\t\t\t\t: undefined,\n\t\thas: (t, prop) => prop in t || (typeof prop === \"string\" && prop in cells),\n\t});\n\n/* ---------------------------------- frame ---------------------------------- */\n\n/**\n * What a handle needs to know about the fn frame it was created in: the\n * scope's cells, plus everything write behavior depends on there.\n */\nexport type Frame = {\n\tcells: Cells;\n\tkey: string;\n\tlockedBy: string | undefined;\n\tentries: readonly OnEntry<string>[];\n};\n\nconst isThenable = (value: any): value is Promise<unknown> =>\n\ttypeof value?.then === \"function\";\n\n/**\n * Every write funnels here: the readonly lock beats everything, then the\n * matching `var.set.<name>` entries run SYNCHRONOUSLY around the actual\n * write - call `next()` to land it, skip to cancel, throw to abort. The\n * bare \"*\" target means \"every fn\", never \"every var write\".\n */\nexport const writeVar = (frame: Frame, name: string, value: unknown) => {\n\tif (frame.lockedBy) {\n\t\tthrow new ValidationError(\n\t\t\t`${frame.key}.readonly`,\n\t\t\t`\"${frame.lockedBy}\" is readonly: attempted to write var \"${name}\"`,\n\t\t);\n\t}\n\tconst cell = getCell(frame.cells, name);\n\tconst merged = () => {\n\t\tconst current =\n\t\t\tcell.derive && !cell.shadowed ? readVar(frame.cells, name) : cell.value;\n\t\tconst base = typeof current === \"object\" && current !== null ? current : {};\n\t\treturn { ...base, ...(value as Record<string, unknown>) };\n\t};\n\tconst next = cell.accumulate ? merged() : value;\n\tconst apply = () => {\n\t\tcell.value = next;\n\t\tif (cell.derive) cell.shadowed = true;\n\t};\n\tconst hooks = frame.entries.filter(\n\t\t(e) => e.target !== \"*\" && matchesTarget(e.target, `var.set.${name}`),\n\t);\n\tif (hooks.length === 0) return apply();\n\tconst chain = hooks.reduceRight<() => void>(\n\t\t(proceed, entry) => () => {\n\t\t\tconst result = entry.handler(\n\t\t\t\thandlerContext(frame.cells, {\n\t\t\t\t\tname,\n\t\t\t\t\tvalue: next,\n\t\t\t\t\tfn: frame.key,\n\t\t\t\t}) as any,\n\t\t\t\tproceed as any,\n\t\t\t);\n\t\t\tif (isThenable(result)) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t`${frame.key}.set`,\n\t\t\t\t\t`var-set handlers must be synchronous - \"${String(entry.target)}\" returned a promise`,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t\tapply,\n\t);\n\tchain();\n};\n\n/**\n * Context reads funnel here: matching `var.get.<name>` entries run\n * SYNCHRONOUSLY around the raw read - `next()` yields the stored value and\n * whatever the handler returns becomes the read result. Internal reads\n * stay raw: derive sources, handler contexts (so a handler reading a var\n * off `c` cannot recurse into itself) and contract checks all see the\n * cell as-is.\n */\nexport const readVarThrough = (frame: Frame, name: string): unknown => {\n\tconst hooks = frame.entries.filter(\n\t\t(e) => e.target !== \"*\" && matchesTarget(e.target, `var.get.${name}`),\n\t);\n\tif (hooks.length === 0) return readVar(frame.cells, name);\n\tconst chain = hooks.reduceRight<() => unknown>(\n\t\t(proceed, entry) => () => {\n\t\t\tconst result = entry.handler(\n\t\t\t\thandlerContext(frame.cells, {\n\t\t\t\t\tname,\n\t\t\t\t\tfn: frame.key,\n\t\t\t\t}) as any,\n\t\t\t\tproceed as any,\n\t\t\t);\n\t\t\tif (isThenable(result)) {\n\t\t\t\tthrow new ValidationError(\n\t\t\t\t\t`${frame.key}.get`,\n\t\t\t\t\t`var-get handlers must be synchronous - \"${String(entry.target)}\" returned a promise`,\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\t\t() => readVar(frame.cells, name),\n\t);\n\treturn chain();\n};\n\n/* ---------------------------------- scope ---------------------------------- */\n\n/**\n * The fn context for one frame: `base` carries the fixed surface (input,\n * error, fn, types, the bound `use` fns, internal symbols) and EVERY\n * other property is a var - read it directly (`c.session.userId`) and\n * write it by plain assignment (`c.session = {...}`). Reads route through\n * the `var.get` entries, writes through `writeVar` (readonly lock,\n * `var.set` entries), and both are always synchronous. Base keys shadow\n * vars of the same name, both ways.\n */\nexport const contextScope = (frame: Frame, base: object): any =>\n\tnew Proxy(base, {\n\t\tget: (t, prop, receiver) =>\n\t\t\tprop in t\n\t\t\t\t? Reflect.get(t, prop, receiver)\n\t\t\t\t: typeof prop === \"string\"\n\t\t\t\t\t? readVarThrough(frame, prop)\n\t\t\t\t\t: undefined,\n\t\tset: (t, prop, value, receiver) => {\n\t\t\tif (typeof prop !== \"string\" || prop in t) {\n\t\t\t\treturn Reflect.set(t, prop, value, receiver);\n\t\t\t}\n\t\t\twriteVar(frame, prop, value);\n\t\t\treturn true;\n\t\t},\n\t\thas: (t, prop) => prop in t || typeof prop === \"string\",\n\t});\n"],"mappings":";;;;;AA6DA,MAAa,8BAAc,IAAI,IAAiB;AAEhD,MAAa,WAAW,MAAc,UAAe,CAAC,MAAW;CAChE,MAAM,SACL,QAAQ,WAAW,KAAA,IAAY,KAAA,IAAY,OAAO,QAAQ,MAAM;CACjE,MAAM,MAAW;EAChB,MAAM;EACN;EACA,SAAS,QAAQ;EACjB;EACA,WAAW,QAAQ,aAAa;EAChC,SAAS,QAAQ;EACjB,YAAY,SACX,QAAQ,MAAM;GACb,GAAG;GACH,SAAS,IAAI;GACb,QAAQ,KAAK,OAAO;IACnB,GAAG;IACH,IAAI;IACJ,MAAM,WAAgB;KACrB,MAAM;KACN,OAAO;MAAE,GAAK,IAAI,QAAQ,SAAiB,CAAC;MAAI,GAAG;KAAM;IAC1D;IACA,UAAU,WAAgB;GAC3B,CAAC;EACF,CAAC;CACH;CACA,YAAY,IAAI,MAAM,GAAG;CACzB,OAAO;AACR;;;;;;AAOA,MAAa,aAAa,MAAc,QAAa,QACpD,QAAQ,MAAM,EAAE,QAAQ;CAAE,QAAQ,OAAO;CAAM;AAAI,EAAE,CAAC;;AAoBvD,MAAa,WAAW,OAAc,SAAuB;CAC5D,MAAM,WAAW,MAAM;CACvB,IAAI,UAAU,OAAO;CACrB,MAAM,MAAM,YAAY,IAAI,IAAI;CAChC,MAAM,OAAa;EAClB,OAAO,KAAK,UAAU,KAAA,IAAY,KAAK,YAAY,CAAC,IAAI,KAAK;EAC7D,QAAQ,KAAK;EACb,UAAU;EACV,YAAY,KAAK,cAAc;CAChC;CACA,MAAM,QAAQ;CACd,OAAO;AACR;;;;AAKA,MAAa,WAAW,OAAc,SAA0B;CAC/D,IAAI,CAAC,MAAM,SAAS,CAAC,YAAY,IAAI,IAAI,GAAG,OAAO,KAAA;CACnD,MAAM,OAAO,QAAQ,OAAO,IAAI;CAChC,IAAI,KAAK,UAAU,CAAC,KAAK,UAAU;EAClC,MAAM,MAAM,QAAQ,OAAO,KAAK,OAAO,MAAM;EAC7C,OAAO,OAAO,OAAO,OAAO,KAAK,OAAO,IAAI,GAAG;CAChD;CACA,OAAO,KAAK;AACb;;;;;;AAOA,MAAa,kBACZ,OACA,SAEA,IAAI,MAAM,MAAM;CACf,MAAM,GAAG,SACR,QAAQ,IACJ,EAAU,QACX,OAAO,SAAS,WACf,QAAQ,OAAO,IAAI,IACnB,KAAA;CACL,MAAM,GAAG,SAAS,QAAQ,KAAM,OAAO,SAAS,YAAY,QAAQ;AACrE,CAAC;AAeF,MAAM,cAAc,UACnB,OAAO,OAAO,SAAS;;;;;;;AAQxB,MAAa,YAAY,OAAc,MAAc,UAAmB;CACvE,IAAI,MAAM,UACT,MAAM,IAAI,gBACT,GAAG,MAAM,IAAI,YACb,IAAI,MAAM,SAAS,yCAAyC,KAAK,EAClE;CAED,MAAM,OAAO,QAAQ,MAAM,OAAO,IAAI;CACtC,MAAM,eAAe;EACpB,MAAM,UACL,KAAK,UAAU,CAAC,KAAK,WAAW,QAAQ,MAAM,OAAO,IAAI,IAAI,KAAK;EAEnE,OAAO;GAAE,GADI,OAAO,YAAY,YAAY,YAAY,OAAO,UAAU,CAAC;GACxD,GAAI;EAAkC;CACzD;CACA,MAAM,OAAO,KAAK,aAAa,OAAO,IAAI;CAC1C,MAAM,cAAc;EACnB,KAAK,QAAQ;EACb,IAAI,KAAK,QAAQ,KAAK,WAAW;CAClC;CACA,MAAM,QAAQ,MAAM,QAAQ,QAC1B,MAAM,EAAE,WAAW,OAAO,cAAc,EAAE,QAAQ,WAAW,MAAM,CACrE;CACA,IAAI,MAAM,WAAW,GAAG,OAAO,MAAM;CAoBrC,MAnBoB,aAClB,SAAS,gBAAgB;EACzB,MAAM,SAAS,MAAM,QACpB,eAAe,MAAM,OAAO;GAC3B;GACA,OAAO;GACP,IAAI,MAAM;EACX,CAAC,GACD,OACD;EACA,IAAI,WAAW,MAAM,GACpB,MAAM,IAAI,gBACT,GAAG,MAAM,IAAI,OACb,2CAA2C,OAAO,MAAM,MAAM,EAAE,qBACjE;CAEF,GACA,KAEG,CAAC,CAAC;AACP;;;;;;;;;AAUA,MAAa,kBAAkB,OAAc,SAA0B;CACtE,MAAM,QAAQ,MAAM,QAAQ,QAC1B,MAAM,EAAE,WAAW,OAAO,cAAc,EAAE,QAAQ,WAAW,MAAM,CACrE;CACA,IAAI,MAAM,WAAW,GAAG,OAAO,QAAQ,MAAM,OAAO,IAAI;CAoBxD,OAnBc,MAAM,aAClB,SAAS,gBAAgB;EACzB,MAAM,SAAS,MAAM,QACpB,eAAe,MAAM,OAAO;GAC3B;GACA,IAAI,MAAM;EACX,CAAC,GACD,OACD;EACA,IAAI,WAAW,MAAM,GACpB,MAAM,IAAI,gBACT,GAAG,MAAM,IAAI,OACb,2CAA2C,OAAO,MAAM,MAAM,EAAE,qBACjE;EAED,OAAO;CACR,SACM,QAAQ,MAAM,OAAO,IAAI,CAErB,CAAC,CAAC;AACd;;;;;;;;;;AAaA,MAAa,gBAAgB,OAAc,SAC1C,IAAI,MAAM,MAAM;CACf,MAAM,GAAG,MAAM,aACd,QAAQ,IACL,QAAQ,IAAI,GAAG,MAAM,QAAQ,IAC7B,OAAO,SAAS,WACf,eAAe,OAAO,IAAI,IAC1B,KAAA;CACL,MAAM,GAAG,MAAM,OAAO,aAAa;EAClC,IAAI,OAAO,SAAS,YAAY,QAAQ,GACvC,OAAO,QAAQ,IAAI,GAAG,MAAM,OAAO,QAAQ;EAE5C,SAAS,OAAO,MAAM,KAAK;EAC3B,OAAO;CACR;CACA,MAAM,GAAG,SAAS,QAAQ,KAAK,OAAO,SAAS;AAChD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "better-call",
3
+ "version": "0.0.0-experimental.06264e12",
4
+ "type": "module",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/better-auth/better-call.git",
8
+ "directory": "packages/experimental"
9
+ },
10
+ "copyright": "Copyright (C) 2025 Bereket Engida",
11
+ "license": "MIT",
12
+ "main": "./dist/index.mjs",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.mts",
15
+ "peerDependencies": {
16
+ "zod": "^4.0.0"
17
+ },
18
+ "peerDependenciesMeta": {
19
+ "zod": {
20
+ "optional": true
21
+ }
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "dev-source": "./src/index.ts",
26
+ "import": {
27
+ "types": "./dist/index.d.mts",
28
+ "default": "./dist/index.mjs"
29
+ },
30
+ "require": {
31
+ "types": "./dist/index.d.cts",
32
+ "default": "./dist/index.cjs"
33
+ }
34
+ },
35
+ "./error": {
36
+ "dev-source": "./src/error.ts",
37
+ "import": {
38
+ "types": "./dist/error.d.mts",
39
+ "default": "./dist/error.mjs"
40
+ },
41
+ "require": {
42
+ "types": "./dist/error.d.cts",
43
+ "default": "./dist/error.cjs"
44
+ }
45
+ },
46
+ "./schema": {
47
+ "dev-source": "./src/schema.ts",
48
+ "import": {
49
+ "types": "./dist/schema.d.mts",
50
+ "default": "./dist/schema.mjs"
51
+ },
52
+ "require": {
53
+ "types": "./dist/schema.d.cts",
54
+ "default": "./dist/schema.cjs"
55
+ }
56
+ },
57
+ "./plugins/http": {
58
+ "dev-source": "./src/plugins/http.ts",
59
+ "import": {
60
+ "types": "./dist/plugins/http.d.mts",
61
+ "default": "./dist/plugins/http.mjs"
62
+ },
63
+ "require": {
64
+ "types": "./dist/plugins/http.d.cts",
65
+ "default": "./dist/plugins/http.cjs"
66
+ }
67
+ },
68
+ "./plugins/read-only": {
69
+ "dev-source": "./src/plugins/read-only.ts",
70
+ "import": {
71
+ "types": "./dist/plugins/read-only.d.mts",
72
+ "default": "./dist/plugins/read-only.mjs"
73
+ },
74
+ "require": {
75
+ "types": "./dist/plugins/read-only.d.cts",
76
+ "default": "./dist/plugins/read-only.cjs"
77
+ }
78
+ }
79
+ },
80
+ "files": [
81
+ "dist"
82
+ ],
83
+ "scripts": {
84
+ "lint:package": "publint --strict .",
85
+ "lint:type": "attw --profile node16 --pack .",
86
+ "test": "vitest",
87
+ "coverage": "vitest run --coverage",
88
+ "coverage:open": "open coverage/index.html",
89
+ "build": "tsdown",
90
+ "dev": "tsdown --watch"
91
+ }
92
+ }