@supalive/core 1.12.0 → 1.13.1
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/dist/db-ClCicssQ.js.map +1 -1
- package/dist/helper-zdJT5FUc.js +58 -0
- package/dist/helper-zdJT5FUc.js.map +1 -0
- package/dist/index-BIIcgvu8.d.ts +2300 -0
- package/dist/index-BIIcgvu8.d.ts.map +1 -0
- package/dist/index-BedlCLem.d.ts +2292 -0
- package/dist/index-BedlCLem.d.ts.map +1 -0
- package/dist/index-BnV8LnOf.d.ts +2286 -0
- package/dist/index-BnV8LnOf.d.ts.map +1 -0
- package/dist/index-BziK63TZ.d.ts +2271 -0
- package/dist/index-BziK63TZ.d.ts.map +1 -0
- package/dist/index-C_i3U4cd.d.ts +2302 -0
- package/dist/index-C_i3U4cd.d.ts.map +1 -0
- package/dist/index-D31hMBDE.d.ts +2288 -0
- package/dist/index-D31hMBDE.d.ts.map +1 -0
- package/dist/index-DZhReQWB.d.ts +2288 -0
- package/dist/index-DZhReQWB.d.ts.map +1 -0
- package/dist/index-DlG8HDCO.d.ts +2215 -0
- package/dist/index-DlG8HDCO.d.ts.map +1 -0
- package/dist/index-Wgi_dGzj.d.ts +2215 -0
- package/dist/index-Wgi_dGzj.d.ts.map +1 -0
- package/dist/index-j3YTMNlF.d.ts +2223 -0
- package/dist/index-j3YTMNlF.d.ts.map +1 -0
- package/dist/mysql-BBbilD4I.js +614 -0
- package/dist/mysql-BBbilD4I.js.map +1 -0
- package/dist/mysql-BHJ2mrwZ.d.ts +113 -0
- package/dist/mysql-BHJ2mrwZ.d.ts.map +1 -0
- package/dist/mysql-BPWGBeum.js +623 -0
- package/dist/mysql-BPWGBeum.js.map +1 -0
- package/dist/mysql-BQbHJ_rK.js +617 -0
- package/dist/mysql-BQbHJ_rK.js.map +1 -0
- package/dist/mysql-BXoAON7c.d.ts +113 -0
- package/dist/mysql-BXoAON7c.d.ts.map +1 -0
- package/dist/mysql-BgfCUYGt.d.ts +113 -0
- package/dist/mysql-BgfCUYGt.d.ts.map +1 -0
- package/dist/mysql-Br7MqUXg.js +620 -0
- package/dist/mysql-Br7MqUXg.js.map +1 -0
- package/dist/mysql-BrueWtO9.d.ts +113 -0
- package/dist/mysql-BrueWtO9.d.ts.map +1 -0
- package/dist/mysql-CCo--z6n.d.ts +113 -0
- package/dist/mysql-CCo--z6n.d.ts.map +1 -0
- package/dist/mysql-CQcbcZB8.d.ts +113 -0
- package/dist/mysql-CQcbcZB8.d.ts.map +1 -0
- package/dist/mysql-CbgCY89y.d.ts +113 -0
- package/dist/mysql-CbgCY89y.d.ts.map +1 -0
- package/dist/mysql-CqD1ZnNY.js +619 -0
- package/dist/mysql-CqD1ZnNY.js.map +1 -0
- package/dist/mysql-D3Db2q3_.js +619 -0
- package/dist/mysql-D3Db2q3_.js.map +1 -0
- package/dist/mysql-DBrhTmYm.js +619 -0
- package/dist/mysql-DBrhTmYm.js.map +1 -0
- package/dist/mysql-DX7YA7IS.d.ts +113 -0
- package/dist/mysql-DX7YA7IS.d.ts.map +1 -0
- package/dist/mysql-DZ0_Rsjc.js +614 -0
- package/dist/mysql-DZ0_Rsjc.js.map +1 -0
- package/dist/mysql-Di6iuKWT.d.ts +114 -0
- package/dist/mysql-Di6iuKWT.d.ts.map +1 -0
- package/dist/mysql-Dko1MJqQ.js +619 -0
- package/dist/mysql-Dko1MJqQ.js.map +1 -0
- package/dist/mysql-F831ycnw.d.ts +113 -0
- package/dist/mysql-F831ycnw.d.ts.map +1 -0
- package/dist/mysql-TLPG62tv.js +618 -0
- package/dist/mysql-TLPG62tv.js.map +1 -0
- package/dist/mysql-e-ii74yG.js +613 -0
- package/dist/mysql-e-ii74yG.js.map +1 -0
- package/dist/overlap-checker-BBkqRdcs.js +259 -0
- package/dist/overlap-checker-BBkqRdcs.js.map +1 -0
- package/dist/overlap-checker-DHUJe-Yy.js +259 -0
- package/dist/overlap-checker-DHUJe-Yy.js.map +1 -0
- package/dist/overlap-checker-DWeQLpSD.js +259 -0
- package/dist/overlap-checker-DWeQLpSD.js.map +1 -0
- package/dist/overlap-checker-Kz7B_jsp.js +259 -0
- package/dist/overlap-checker-Kz7B_jsp.js.map +1 -0
- package/dist/postgres-7hJuQX4Y.d.ts +117 -0
- package/dist/postgres-7hJuQX4Y.d.ts.map +1 -0
- package/dist/postgres-B4H78IkW.js +864 -0
- package/dist/postgres-B4H78IkW.js.map +1 -0
- package/dist/postgres-B4h8D5U2.d.ts +117 -0
- package/dist/postgres-B4h8D5U2.d.ts.map +1 -0
- package/dist/postgres-BXFPamkP.js +864 -0
- package/dist/postgres-BXFPamkP.js.map +1 -0
- package/dist/postgres-CkhK4DqZ.d.ts +118 -0
- package/dist/postgres-CkhK4DqZ.d.ts.map +1 -0
- package/dist/postgres-CoR3TUMd.js +865 -0
- package/dist/postgres-CoR3TUMd.js.map +1 -0
- package/dist/postgres-D-RsG12a.d.ts +117 -0
- package/dist/postgres-D-RsG12a.d.ts.map +1 -0
- package/dist/postgres-D5Z1S3J9.d.ts +117 -0
- package/dist/postgres-D5Z1S3J9.d.ts.map +1 -0
- package/dist/postgres-DHs89iWT.js +864 -0
- package/dist/postgres-DHs89iWT.js.map +1 -0
- package/dist/postgres-DvBIRnYA.d.ts +117 -0
- package/dist/postgres-DvBIRnYA.d.ts.map +1 -0
- package/dist/postgres-Eo-lpUiX.d.ts +117 -0
- package/dist/postgres-Eo-lpUiX.d.ts.map +1 -0
- package/dist/postgres-VLAGIFLR.d.ts +117 -0
- package/dist/postgres-VLAGIFLR.d.ts.map +1 -0
- package/dist/postgres-ZKEo6Mjg.js +868 -0
- package/dist/postgres-ZKEo6Mjg.js.map +1 -0
- package/dist/postgres-hrQmDPUp.d.ts +117 -0
- package/dist/postgres-hrQmDPUp.d.ts.map +1 -0
- package/dist/postgres-pXrP9Fx0.d.ts +117 -0
- package/dist/postgres-pXrP9Fx0.d.ts.map +1 -0
- package/dist/query-BuOs9w-U.js +730 -0
- package/dist/query-BuOs9w-U.js.map +1 -0
- package/dist/query-Xw2hi6TB.js +730 -0
- package/dist/query-Xw2hi6TB.js.map +1 -0
- package/dist/read-routing-BUcQAwnd.js +62 -0
- package/dist/read-routing-BUcQAwnd.js.map +1 -0
- package/dist/read-routing-BXflWzQI.js +61 -0
- package/dist/read-routing-BXflWzQI.js.map +1 -0
- package/dist/read-routing-CcsLp0ow.js +61 -0
- package/dist/read-routing-CcsLp0ow.js.map +1 -0
- package/dist/realtime_db-B70vqtRZ.js +272 -0
- package/dist/realtime_db-B70vqtRZ.js.map +1 -0
- package/dist/realtime_db-BMysrWym.js +312 -0
- package/dist/realtime_db-BMysrWym.js.map +1 -0
- package/dist/realtime_db-Bi7S938D.js +312 -0
- package/dist/realtime_db-Bi7S938D.js.map +1 -0
- package/dist/realtime_db-CVze3gM0.js +312 -0
- package/dist/realtime_db-CVze3gM0.js.map +1 -0
- package/dist/realtime_db-DMipkUmD.js +312 -0
- package/dist/realtime_db-DMipkUmD.js.map +1 -0
- package/dist/realtime_db-XWzuq2JS.js +272 -0
- package/dist/realtime_db-XWzuq2JS.js.map +1 -0
- package/dist/router-Kgrlci8X.js +337 -0
- package/dist/router-Kgrlci8X.js.map +1 -0
- package/dist/router-g8qSQb8m.js +314 -0
- package/dist/router-g8qSQb8m.js.map +1 -0
- package/dist/schema-DLue3K2s.js +134 -0
- package/dist/schema-DLue3K2s.js.map +1 -0
- package/dist/schema-DfowI0ip.js +134 -0
- package/dist/schema-DfowI0ip.js.map +1 -0
- package/dist/schema-fKldWzwc.js +134 -0
- package/dist/schema-fKldWzwc.js.map +1 -0
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.js +69 -29
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +1 -1
- package/dist/src/exports/procedure.js +5 -6
- package/dist/src/exports/procedure.js.map +1 -1
- package/dist/src/exports/schema-sql.d.ts +18 -1
- package/dist/src/exports/schema-sql.d.ts.map +1 -1
- package/dist/src/exports/schema-sql.js +91 -15
- package/dist/src/exports/schema-sql.js.map +1 -1
- package/dist/src/exports/server.d.ts +16 -6
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +136 -59
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/sub-manager-worker-entry.js +1 -1
- package/dist/src/exports/types.d.ts +2 -2
- package/dist/src/exports/types.js +4 -4
- package/dist/sub-worker-dispatch-CO9nVdSG.js +813 -0
- package/dist/sub-worker-dispatch-CO9nVdSG.js.map +1 -0
- package/dist/sub-worker-dispatch-CQBknEL7.js +813 -0
- package/dist/sub-worker-dispatch-CQBknEL7.js.map +1 -0
- package/dist/sub-worker-dispatch-CQHeit6D.js +813 -0
- package/dist/sub-worker-dispatch-CQHeit6D.js.map +1 -0
- package/dist/sub-worker-dispatch-CmEW10it.js +813 -0
- package/dist/sub-worker-dispatch-CmEW10it.js.map +1 -0
- package/dist/sub-worker-dispatch-CunxuOPT.js +823 -0
- package/dist/sub-worker-dispatch-CunxuOPT.js.map +1 -0
- package/dist/sub-worker-dispatch-CvvFm40r.js +817 -0
- package/dist/sub-worker-dispatch-CvvFm40r.js.map +1 -0
- package/dist/sub-worker-dispatch-CzwVHQVi.js +826 -0
- package/dist/sub-worker-dispatch-CzwVHQVi.js.map +1 -0
- package/dist/sub-worker-dispatch-D15UHo1g.js +813 -0
- package/dist/sub-worker-dispatch-D15UHo1g.js.map +1 -0
- package/dist/sub-worker-dispatch-D8DY56ry.js +817 -0
- package/dist/sub-worker-dispatch-D8DY56ry.js.map +1 -0
- package/dist/sub-worker-dispatch-DJgWxtSQ.js +823 -0
- package/dist/sub-worker-dispatch-DJgWxtSQ.js.map +1 -0
- package/dist/sub-worker-dispatch-Dplqeubd.js +813 -0
- package/dist/sub-worker-dispatch-Dplqeubd.js.map +1 -0
- package/dist/sub-worker-dispatch-MneZYjXl.js +823 -0
- package/dist/sub-worker-dispatch-MneZYjXl.js.map +1 -0
- package/dist/sub-worker-dispatch-dXjUmSMN.js +813 -0
- package/dist/sub-worker-dispatch-dXjUmSMN.js.map +1 -0
- package/dist/table-id-BJdmctEQ.js +91 -0
- package/dist/table-id-BJdmctEQ.js.map +1 -0
- package/dist/table-id-DyrJBKAQ.js +105 -0
- package/dist/table-id-DyrJBKAQ.js.map +1 -0
- package/dist/types_client_rpc-BGMwPJst.js +123 -0
- package/dist/types_client_rpc-BGMwPJst.js.map +1 -0
- package/dist/types_client_rpc-ByGwoRCL.js +132 -0
- package/dist/types_client_rpc-ByGwoRCL.js.map +1 -0
- package/dist/types_db-BsR02Sph.js +171 -0
- package/dist/types_db-BsR02Sph.js.map +1 -0
- package/dist/types_db-CgGlOMmr.js +187 -0
- package/dist/types_db-CgGlOMmr.js.map +1 -0
- package/dist/types_db-OUou3o2Z.js +172 -0
- package/dist/types_db-OUou3o2Z.js.map +1 -0
- package/dist/types_server-B3z8hi-Z.d.ts +523 -0
- package/dist/types_server-B3z8hi-Z.d.ts.map +1 -0
- package/dist/types_server-BXBvz4T8.d.ts +523 -0
- package/dist/types_server-BXBvz4T8.d.ts.map +1 -0
- package/dist/types_server-BhC19R2a.js +27 -0
- package/dist/types_server-BhC19R2a.js.map +1 -0
- package/dist/types_server-CClUPZn1.d.ts +521 -0
- package/dist/types_server-CClUPZn1.d.ts.map +1 -0
- package/dist/types_server-CG453PHb.d.ts +523 -0
- package/dist/types_server-CG453PHb.d.ts.map +1 -0
- package/dist/types_server-CnYTSPhM.d.ts +523 -0
- package/dist/types_server-CnYTSPhM.d.ts.map +1 -0
- package/dist/types_server-DHpGUHB_.d.ts +521 -0
- package/dist/types_server-DHpGUHB_.d.ts.map +1 -0
- package/dist/types_server-Ded1u3bL.d.ts +521 -0
- package/dist/types_server-Ded1u3bL.d.ts.map +1 -0
- package/dist/types_server-fons7nmo.d.ts +523 -0
- package/dist/types_server-fons7nmo.d.ts.map +1 -0
- package/dist/types_server-okvBG4v_.d.ts +521 -0
- package/dist/types_server-okvBG4v_.d.ts.map +1 -0
- package/dist/types_server-pD8DiI7s.d.ts +521 -0
- package/dist/types_server-pD8DiI7s.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
import { z as z$2 } from "zod";
|
|
2
|
+
//#region src/router/procedure.ts
|
|
3
|
+
/**
|
|
4
|
+
* Patch all Zod schema types on the given `z` namespace with `.modelName(name)`.
|
|
5
|
+
* Must be called with the same `z` instance the consumer uses so the prototypes
|
|
6
|
+
* match. Runs once per unique `z` instance (tracked via a WeakSet).
|
|
7
|
+
*/
|
|
8
|
+
function patchZod(zInstance) {
|
|
9
|
+
const key = "__supalivePatchedZodInstances";
|
|
10
|
+
const patchedInstances = globalThis[key] ??= /* @__PURE__ */ new Set();
|
|
11
|
+
if (patchedInstances.has(zInstance)) return;
|
|
12
|
+
patchedInstances.add(zInstance);
|
|
13
|
+
const patched = /* @__PURE__ */ new Set();
|
|
14
|
+
const samples = [
|
|
15
|
+
zInstance.string(),
|
|
16
|
+
zInstance.number(),
|
|
17
|
+
zInstance.boolean(),
|
|
18
|
+
zInstance.bigint(),
|
|
19
|
+
zInstance.date(),
|
|
20
|
+
zInstance.object({}),
|
|
21
|
+
zInstance.array(zInstance.string()),
|
|
22
|
+
zInstance.enum(["_"]),
|
|
23
|
+
zInstance.literal("_"),
|
|
24
|
+
zInstance.null(),
|
|
25
|
+
zInstance.undefined(),
|
|
26
|
+
zInstance.any(),
|
|
27
|
+
zInstance.unknown(),
|
|
28
|
+
zInstance.void(),
|
|
29
|
+
zInstance.never(),
|
|
30
|
+
zInstance.nan(),
|
|
31
|
+
zInstance.record(zInstance.string(), zInstance.string()),
|
|
32
|
+
zInstance.union([zInstance.string(), zInstance.number()]),
|
|
33
|
+
zInstance.intersection(zInstance.string(), zInstance.string()),
|
|
34
|
+
zInstance.tuple([zInstance.string()]),
|
|
35
|
+
zInstance.promise(zInstance.string()),
|
|
36
|
+
zInstance.custom(),
|
|
37
|
+
zInstance.coerce.string(),
|
|
38
|
+
zInstance.coerce.number(),
|
|
39
|
+
zInstance.coerce.boolean(),
|
|
40
|
+
zInstance.coerce.bigint(),
|
|
41
|
+
zInstance.coerce.date(),
|
|
42
|
+
zInstance.string().optional(),
|
|
43
|
+
zInstance.string().nullable(),
|
|
44
|
+
zInstance.string().default(""),
|
|
45
|
+
zInstance.string().pipe(zInstance.string()),
|
|
46
|
+
zInstance.string().transform(() => "")
|
|
47
|
+
];
|
|
48
|
+
for (const inst of samples) {
|
|
49
|
+
const proto = Object.getPrototypeOf(inst);
|
|
50
|
+
if (patched.has(proto)) continue;
|
|
51
|
+
patched.add(proto);
|
|
52
|
+
const metaDesc = Object.getOwnPropertyDescriptor(proto, "meta");
|
|
53
|
+
if (!metaDesc || !metaDesc.get) continue;
|
|
54
|
+
if (proto.modelName) continue;
|
|
55
|
+
Object.defineProperty(proto, "modelName", {
|
|
56
|
+
value: function modelName(name) {
|
|
57
|
+
return this.meta({ __genType: name });
|
|
58
|
+
},
|
|
59
|
+
writable: true,
|
|
60
|
+
configurable: true
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
patchZod(z$2);
|
|
65
|
+
/**
|
|
66
|
+
* No-op connection routing for query paths that always run on the primary — live
|
|
67
|
+
* subscription re-execution and the internal server-to-server caller. Spread into
|
|
68
|
+
* the `QueryCtx` so `ctx.usePrimaryConn()`/`useReplicaConn()` are always callable.
|
|
69
|
+
*/
|
|
70
|
+
const primaryOnlyConn = {
|
|
71
|
+
usePrimaryConn: () => {},
|
|
72
|
+
useReplicaConn: () => {}
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Create a query builder with a pre-defined context type.
|
|
76
|
+
* This allows you to define the context type once and have it inferred
|
|
77
|
+
* automatically in all your query handlers.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* // Define your server context
|
|
81
|
+
* interface ServerContext {
|
|
82
|
+
* auth: { userId: string };
|
|
83
|
+
* requestId: string;
|
|
84
|
+
* }
|
|
85
|
+
*
|
|
86
|
+
* // Create a typed query builder
|
|
87
|
+
* const query = createQueryBuilder<ServerContext>();
|
|
88
|
+
*
|
|
89
|
+
* // Use it - context type is automatically inferred!
|
|
90
|
+
* const getUser = query({
|
|
91
|
+
* args: z.object({ id: z.string() }),
|
|
92
|
+
* handler: async (ctx, { id }) => {
|
|
93
|
+
* // ctx.db for database queries
|
|
94
|
+
* const user = await ctx.db.query(UsersSchema).find(id);
|
|
95
|
+
* // ctx.context for server context
|
|
96
|
+
* console.log(ctx.context.requestId);
|
|
97
|
+
* return user;
|
|
98
|
+
* }
|
|
99
|
+
* });
|
|
100
|
+
*/
|
|
101
|
+
function createQueryBuilder() {
|
|
102
|
+
return function query(config) {
|
|
103
|
+
return {
|
|
104
|
+
_type: "procedure",
|
|
105
|
+
procedureType: "query",
|
|
106
|
+
inputSchema: config.args,
|
|
107
|
+
outputSchema: config.returns,
|
|
108
|
+
fn: config.handler,
|
|
109
|
+
internal: config.internal ?? false,
|
|
110
|
+
queryIdentity: config.queryIdentity
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Create a mutation builder with a pre-defined context type.
|
|
116
|
+
* This allows you to define the context type once and have it inferred
|
|
117
|
+
* automatically in all your mutation handlers.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Define your server context
|
|
121
|
+
* interface ServerContext {
|
|
122
|
+
* auth: { userId: string };
|
|
123
|
+
* requestId: string;
|
|
124
|
+
* }
|
|
125
|
+
*
|
|
126
|
+
* // Create a typed mutation builder
|
|
127
|
+
* const mutation = createMutationBuilder<ServerContext>();
|
|
128
|
+
*
|
|
129
|
+
* // Use it - context type is automatically inferred!
|
|
130
|
+
* const createUser = mutation({
|
|
131
|
+
* args: z.object({ name: z.string() }),
|
|
132
|
+
* handler: async (ctx, { name }) => {
|
|
133
|
+
* // ctx.db for mutations
|
|
134
|
+
* await ctx.db.insert(UsersSchema, id, { name });
|
|
135
|
+
* // ctx.context for server context
|
|
136
|
+
* console.log(ctx.context.requestId);
|
|
137
|
+
* return { success: true };
|
|
138
|
+
* }
|
|
139
|
+
* });
|
|
140
|
+
*/
|
|
141
|
+
function createMutationBuilder() {
|
|
142
|
+
return function mutation(config) {
|
|
143
|
+
return {
|
|
144
|
+
_type: "procedure",
|
|
145
|
+
procedureType: "mutation",
|
|
146
|
+
inputSchema: config.args,
|
|
147
|
+
outputSchema: config.returns,
|
|
148
|
+
fn: config.handler,
|
|
149
|
+
internal: config.internal ?? false
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create an action builder with a pre-defined context type.
|
|
155
|
+
* Actions have access to the full SupaliveDb for both queries and mutations.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* // Define your server context
|
|
159
|
+
* interface ServerContext {
|
|
160
|
+
* auth: { userId: string };
|
|
161
|
+
* requestId: string;
|
|
162
|
+
* }
|
|
163
|
+
*
|
|
164
|
+
* // Create a typed action builder
|
|
165
|
+
* const action = createActionBuilder<ServerContext>();
|
|
166
|
+
*
|
|
167
|
+
* // Use it - context type is automatically inferred!
|
|
168
|
+
* const processOrder = action({
|
|
169
|
+
* args: z.object({ orderId: z.string() }),
|
|
170
|
+
* handler: async (ctx, { orderId }) => {
|
|
171
|
+
* // ctx.db for full database access
|
|
172
|
+
* const order = await ctx.db.query(async (db) => {
|
|
173
|
+
* return db.query(OrdersSchema).find(orderId);
|
|
174
|
+
* });
|
|
175
|
+
* await ctx.db.mutation(async (db) => {
|
|
176
|
+
* await db.update(OrdersSchema, orderId, { status: "processed" });
|
|
177
|
+
* });
|
|
178
|
+
* // ctx.context for server context
|
|
179
|
+
* console.log(ctx.context.requestId);
|
|
180
|
+
* return { success: true };
|
|
181
|
+
* }
|
|
182
|
+
* });
|
|
183
|
+
*/
|
|
184
|
+
function createActionBuilder() {
|
|
185
|
+
return function action(config) {
|
|
186
|
+
return {
|
|
187
|
+
_type: "procedure",
|
|
188
|
+
procedureType: "action",
|
|
189
|
+
inputSchema: config.args,
|
|
190
|
+
outputSchema: config.returns,
|
|
191
|
+
fn: config.handler,
|
|
192
|
+
internal: config.internal ?? false
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Create a job builder with a pre-defined context type. A job is a
|
|
198
|
+
* server-only procedure invoked by the scheduler over HTTP — either on its
|
|
199
|
+
* declared `cron` schedule or as a precise one-shot enqueued via the
|
|
200
|
+
* server's job API. Jobs run like actions (full db, non-transactional) and
|
|
201
|
+
* are always internal, so they are never reachable from a client.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* const job = createJobBuilder<ServerContext>();
|
|
205
|
+
*
|
|
206
|
+
* export const cleanupOtps = job({
|
|
207
|
+
* cron: "0 * * * *", // hourly
|
|
208
|
+
* args: z.object({}),
|
|
209
|
+
* handler: async (ctx) => {
|
|
210
|
+
* await ctx.db.mutation(async (db) => { ... });
|
|
211
|
+
* return { ok: true };
|
|
212
|
+
* },
|
|
213
|
+
* });
|
|
214
|
+
*/
|
|
215
|
+
function createJobBuilder() {
|
|
216
|
+
return function job(config) {
|
|
217
|
+
return {
|
|
218
|
+
_type: "procedure",
|
|
219
|
+
procedureType: "job",
|
|
220
|
+
inputSchema: config.args,
|
|
221
|
+
fn: config.handler,
|
|
222
|
+
internal: true,
|
|
223
|
+
cron: config.cron
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/router/router.ts
|
|
229
|
+
/**
|
|
230
|
+
* Create a router from procedure definitions.
|
|
231
|
+
* Procedure names are inferred from the object keys.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* const appRouter = router({
|
|
235
|
+
* procedures: {
|
|
236
|
+
* getUser,
|
|
237
|
+
* createUser,
|
|
238
|
+
* internalGetAll,
|
|
239
|
+
* },
|
|
240
|
+
* });
|
|
241
|
+
*
|
|
242
|
+
* export type AppRouter = typeof appRouter;
|
|
243
|
+
*/
|
|
244
|
+
function router(config) {
|
|
245
|
+
const registry = getContextRegistry(config.contextName ?? "default");
|
|
246
|
+
for (const [name, proc] of Object.entries(config.procedures)) registry.registerProcedure(name, proc);
|
|
247
|
+
return {
|
|
248
|
+
_type: "router",
|
|
249
|
+
procedures: config.procedures,
|
|
250
|
+
contextName: config.contextName ?? "default"
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const contextRegistry = /* @__PURE__ */ new Map();
|
|
254
|
+
function getContextRegistry(inContext) {
|
|
255
|
+
let registry = contextRegistry.get(inContext);
|
|
256
|
+
if (!registry) {
|
|
257
|
+
registry = new ContextRegistry();
|
|
258
|
+
contextRegistry.set(inContext, registry);
|
|
259
|
+
}
|
|
260
|
+
return registry;
|
|
261
|
+
}
|
|
262
|
+
var ContextRegistry = class {
|
|
263
|
+
procedureRegistry = /* @__PURE__ */ new Map();
|
|
264
|
+
internalProcedureNames = /* @__PURE__ */ new Set();
|
|
265
|
+
registerProcedure(name, proc) {
|
|
266
|
+
if (this.procedureRegistry.has(name)) throw new Error(`[router] Procedure "${name}" is already registered. Each procedure name must be unique across the application.`);
|
|
267
|
+
if (proc.internal) this.markInternalProcedure(name);
|
|
268
|
+
const queryIdentity = proc.procedureType === "query" ? proc.queryIdentity : void 0;
|
|
269
|
+
const cron = proc.procedureType === "job" ? proc.cron : void 0;
|
|
270
|
+
this.procedureRegistry.set(name, {
|
|
271
|
+
name,
|
|
272
|
+
type: proc.procedureType,
|
|
273
|
+
internal: proc.internal,
|
|
274
|
+
inputSchema: proc.inputSchema,
|
|
275
|
+
fn: proc.fn,
|
|
276
|
+
queryIdentity,
|
|
277
|
+
cron
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
/** Get a registered procedure by name (runtime lookup) */
|
|
281
|
+
getProcedure(name) {
|
|
282
|
+
return this.procedureRegistry.get(name);
|
|
283
|
+
}
|
|
284
|
+
/** Get all registered procedure names */
|
|
285
|
+
getProcedureNames() {
|
|
286
|
+
return [...this.procedureRegistry.keys()];
|
|
287
|
+
}
|
|
288
|
+
/** Get all registered procedures */
|
|
289
|
+
getAllProcedures() {
|
|
290
|
+
return new Map(this.procedureRegistry);
|
|
291
|
+
}
|
|
292
|
+
/** Get all registered job procedures (server-only, scheduler-triggered). */
|
|
293
|
+
getJobProcedures() {
|
|
294
|
+
return [...this.procedureRegistry.values()].filter((p) => p.type === "job");
|
|
295
|
+
}
|
|
296
|
+
/** Clear the registry (for testing only) */
|
|
297
|
+
clearRegistry() {
|
|
298
|
+
this.procedureRegistry.clear();
|
|
299
|
+
this.clearInternalProcedures();
|
|
300
|
+
}
|
|
301
|
+
markInternalProcedure(name) {
|
|
302
|
+
this.internalProcedureNames.add(name);
|
|
303
|
+
}
|
|
304
|
+
isInternalProcedure(name) {
|
|
305
|
+
return this.internalProcedureNames.has(name);
|
|
306
|
+
}
|
|
307
|
+
clearInternalProcedures() {
|
|
308
|
+
this.internalProcedureNames.clear();
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
//#endregion
|
|
312
|
+
export { createJobBuilder as a, patchZod as c, createActionBuilder as i, primaryOnlyConn as l, getContextRegistry as n, createMutationBuilder as o, router as r, createQueryBuilder as s, ContextRegistry as t, z$2 as u };
|
|
313
|
+
|
|
314
|
+
//# sourceMappingURL=router-g8qSQb8m.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-g8qSQb8m.js","names":["z"],"sources":["../src/router/procedure.ts","../src/router/router.ts"],"sourcesContent":["import type { ZodType } from \"zod\";\nimport { z } from \"zod\";\nimport type { DbReader, DbWriter } from \"../db/context\";\nimport type { SupaliveDb } from \"../db/realtime_db\";\nimport type { ObjectStorage } from \"../storage\";\nimport type { JobClient } from \"../jobs/scheduler\";\n\nexport { z };\n\n// ─── Zod .modelName() extension ─────────────────────────────────────────────\n// Patches every Zod schema type with a `.modelName(\"Name\")` method that sets\n// `__genType` metadata for codegen model renaming.\n//\n// Zod v4: `z.string` is a factory — `z.string.prototype` is NOT the real class\n// prototype. We collect the actual prototypes by creating one instance per type,\n// then patch each unique prototype once.\n\n/**\n * Patch all Zod schema types on the given `z` namespace with `.modelName(name)`.\n * Must be called with the same `z` instance the consumer uses so the prototypes\n * match. Runs once per unique `z` instance (tracked via a WeakSet).\n */\nexport function patchZod(zInstance: typeof z): void {\n const key = \"__supalivePatchedZodInstances\";\n const patchedInstances: Set<object> = (globalThis as any)[key] ??= new Set();\n if (patchedInstances.has(zInstance)) return;\n patchedInstances.add(zInstance);\n\n const patched = new Set<object>();\n const samples: ZodType[] = [\n zInstance.string(), zInstance.number(), zInstance.boolean(), zInstance.bigint(), zInstance.date(),\n zInstance.object({}), zInstance.array(zInstance.string()), zInstance.enum([\"_\"]),\n zInstance.literal(\"_\"), zInstance.null(), zInstance.undefined(), zInstance.any(), zInstance.unknown(),\n zInstance.void(), zInstance.never(), zInstance.nan(), zInstance.record(zInstance.string(), zInstance.string()),\n zInstance.union([zInstance.string(), zInstance.number()]), zInstance.intersection(zInstance.string(), zInstance.string()),\n zInstance.tuple([zInstance.string()]), zInstance.promise(zInstance.string()), zInstance.custom(),\n zInstance.coerce.string(), zInstance.coerce.number(), zInstance.coerce.boolean(),\n zInstance.coerce.bigint(), zInstance.coerce.date(),\n zInstance.string().optional(), zInstance.string().nullable(), zInstance.string().default(\"\"),\n zInstance.string().pipe(zInstance.string()), zInstance.string().transform(() => \"\"),\n ];\n for (const inst of samples) {\n const proto = Object.getPrototypeOf(inst);\n if (patched.has(proto)) continue;\n patched.add(proto);\n const metaDesc = Object.getOwnPropertyDescriptor(proto, \"meta\");\n if (!metaDesc || !metaDesc.get) continue;\n if ((proto as any).modelName) continue;\n Object.defineProperty(proto, \"modelName\", {\n value: function modelName(name: string) {\n return this.meta({ __genType: name });\n },\n writable: true,\n configurable: true,\n });\n }\n}\n\n// Auto-patch this module's own z instance so the re-exported z already has .modelName()\npatchZod(z);\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\n/**\n * Query context passed to query handlers.\n * Contains database reader and user-defined server context.\n */\n/** Options for the per-handler connection-routing methods (see {@link QueryCtx}). */\nexport interface ConnOpts {\n /**\n * Force the PRIMARY (writer) connection even inside a query/action —\n * read-your-writes. On `useReplicaConn` this overrides the replica choice.\n */\n readOwnWrite?: boolean;\n}\n\nexport interface QueryCtx<TContext = unknown> {\n /** Database reader for queries */\n db: DbReader;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n /**\n * Route this query's reads to the PRIMARY (writer) connection. Must be called\n * BEFORE any read (throws otherwise). A no-op when reads already run on primary\n * (no replica configured, or a primary-only path like a live query).\n */\n usePrimaryConn(opts?: ConnOpts): void;\n /**\n * Route this query's reads to the read REPLICA, if one is configured. Must be\n * called BEFORE any read. `{ readOwnWrite: true }` forces the PRIMARY instead.\n */\n useReplicaConn(opts?: ConnOpts): void;\n}\n\n/**\n * No-op connection routing for query paths that always run on the primary — live\n * subscription re-execution and the internal server-to-server caller. Spread into\n * the `QueryCtx` so `ctx.usePrimaryConn()`/`useReplicaConn()` are always callable.\n */\nexport const primaryOnlyConn: Pick<QueryCtx, \"usePrimaryConn\" | \"useReplicaConn\"> = {\n usePrimaryConn: () => { },\n useReplicaConn: () => { },\n};\n\n/**\n * Mutation context passed to mutation handlers.\n * Contains database writer and user-defined server context.\n */\nexport interface MutationCtx<TContext = unknown> {\n /** Database writer for mutations (includes insert/update/delete) */\n db: DbWriter;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n /**\n * No-op on a mutation — writes (and their reads) always run on the primary.\n * Present so a `MutationCtx` stays structurally assignable to `QueryCtx`, i.e.\n * a mutation can reuse a `(ctx: QueryCtx) => …` read helper.\n */\n usePrimaryConn(opts?: ConnOpts): void;\n /** No-op on a mutation (always primary). See {@link usePrimaryConn}. */\n useReplicaConn(opts?: ConnOpts): void;\n}\n\n/**\n * Action context passed to action handlers.\n *\n * Actions are the \"external I/O\" tier: they hold the full {@link SupaliveDb}\n * (queries + mutations) and are the ONLY context that also carries side-effect\n * services — {@link ObjectStorage} for uploads/downloads and a {@link JobClient}\n * for scheduling precise one-shots. Queries/mutations stay pure (db only) so\n * they remain cacheable and transaction-scoped.\n */\nexport interface ActionCtx<TContext = unknown> {\n /** Full database interface for queries and mutations */\n db: SupaliveDb;\n /** Object storage, when the server was configured with one (see\n * `SupaliveServerConfig.storage`); `undefined` otherwise. The usual home\n * for upload/download presigning. */\n storage: ObjectStorage;\n /** Job scheduler client for enqueueing precise one-shots, when the server\n * was configured with a `scheduler`; `undefined` otherwise. Scheduling is\n * network I/O, so it belongs in actions, never inside a DB transaction. */\n scheduler: JobClient;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n}\n\n/**\n * Job context passed to job handlers. Structurally identical to\n * {@link ActionCtx}: a job is a server-only procedure that runs\n * non-transactionally against the full {@link SupaliveDb} and may perform\n * external work, but it is triggered by the scheduler over HTTP (a cron tick\n * or a precise one-shot) rather than by a connected client. `serverCtx` is\n * the system context built by the server's `jobContext` factory.\n */\nexport interface JobCtx<TContext = unknown> {\n /** Full database interface for queries and mutations */\n db: SupaliveDb;\n /** System server context built by the server for scheduler-triggered runs */\n serverCtx?: TContext;\n}\n\n/**\n * Context handed to `caller.<proc>.runQuery` / `.runMutation` when invoking one\n * procedure from inside another. Pass the caller handler's own `ctx` — its `db`\n * carries the parent's live transaction (a {@link DbReader}/{@link DbWriter}) or,\n * inside an action, the full {@link SupaliveDb}. The caller uses this to decide\n * whether the nested call joins the parent's snapshot (queries) or runs as an\n * independent sub-transaction (mutations).\n */\nexport interface ParentCtx<TContext = unknown> {\n db: DbReader | DbWriter | SupaliveDb;\n serverCtx?: TContext | undefined;\n}\n\n// ─── Procedure Types ─────────────────────────────────────────────────────────\n\nexport type QueryFn<TInput, TResult, TContext = unknown> = (\n ctx: QueryCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type MutationFn<TInput, TResult, TContext = unknown> = (\n ctx: MutationCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type ActionFn<TInput, TResult, TContext = unknown> = (\n ctx: ActionCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type JobFn<TInput, TResult, TContext = unknown> = (\n ctx: JobCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\n/**\n * Per-procedure override for the cache/subscription segmentation key.\n *\n * omitted | undefined → fall back to `config.getUserId(serverCtx)` (default)\n * false → no identity in hash; cache/sub shared across all users\n * string → a static identity literal (e.g. \"public\" or a tenant id)\n * function → compute from serverCtx + input (sync)\n *\n * When the result of a query is identical regardless of who calls it, set\n * this to a literal (or `false`) so a single cache entry serves everyone.\n *\n * The function form intentionally takes `serverCtx` (not the full `QueryCtx`)\n * because identity is resolved before any DB read is issued.\n */\nexport type QueryIdentitySpec<TInput, TContext> =\n | false\n | string\n | ((serverCtx: TContext, input: TInput) => string | null | undefined);\n\nexport interface BaseProcedure<\n TInput, TResult,\n TType extends \"query\" | \"mutation\" | \"action\" | \"job\",\n TContext = unknown,\n TInternal extends boolean = boolean\n> {\n readonly _type: \"procedure\";\n readonly procedureType: TType;\n readonly inputSchema: ZodType<TInput>;\n readonly outputSchema?: ZodType<any>;\n readonly fn: QueryFn<TInput, TResult, TContext> | MutationFn<TInput, TResult, TContext> | ActionFn<TInput, TResult, TContext> | JobFn<TInput, TResult, TContext>;\n readonly internal: TInternal;\n}\n\nexport interface QueryProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"query\", TContext, TInternal> {\n readonly procedureType: \"query\";\n readonly fn: QueryFn<TInput, TResult, TContext>;\n readonly queryIdentity?: QueryIdentitySpec<TInput, TContext>;\n}\n\nexport interface MutationProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"mutation\", TContext, TInternal> {\n readonly procedureType: \"mutation\";\n readonly fn: MutationFn<TInput, TResult, TContext>;\n}\n\nexport interface ActionProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"action\", TContext, TInternal> {\n readonly procedureType: \"action\";\n readonly fn: ActionFn<TInput, TResult, TContext>;\n}\n\n/**\n * A scheduler-triggered, server-only procedure. Runs like an action (full\n * db, non-transactional, may do external work) but is dispatched by the\n * server's HTTP job endpoint on a cron tick or a precise one-shot rather\n * than over the client WebSocket. Always {@link internal}: true, so it is\n * never reachable via `call`/`subscribe`.\n */\nexport interface JobProcedure<\n TInput, TResult,\n TContext = unknown,\n> extends BaseProcedure<TInput, TResult, \"job\", TContext, true> {\n readonly procedureType: \"job\";\n readonly fn: JobFn<TInput, TResult, TContext>;\n /**\n * Cron expression for a recurring job (e.g. `\"0 3 * * *\"`). Declared crons\n * are synced to the scheduler at server startup. Omit for a job that is\n * only ever invoked as a precise one-shot via the scheduler API.\n */\n readonly cron?: string;\n}\n\nexport type AnyProcedure<TContext = unknown> =\n | QueryProcedure<any, any, TContext>\n | MutationProcedure<any, any, TContext>\n | ActionProcedure<any, any, TContext>\n | JobProcedure<any, any, TContext>;\n\n// ─── Procedure Configuration Types ───────────────────────────────────────────\n\nexport interface QueryConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The query handler function */\n handler: QueryFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n /**\n * Optional Zod schema describing return-type overrides for codegen.\n * Only the fields you specify are overridden; everything else is inferred\n * from the handler's TypeScript return type. Use `.modelName(\"Name\")`\n * on a Zod object to rename the generated model class.\n */\n returns?: ZodType<any>;\n /**\n * Override the cache/subscription segmentation key for this procedure.\n * See {@link QueryIdentitySpec}. Omit to keep the default (per-user) behavior.\n */\n queryIdentity?: QueryIdentitySpec<TInput, TContext>;\n}\n\nexport interface MutationConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The mutation handler function */\n handler: MutationFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n /**\n * Optional Zod schema describing return-type overrides for codegen.\n * Only the fields you specify are overridden; everything else is inferred\n * from the handler's TypeScript return type.\n */\n returns?: ZodType<any>;\n}\n\nexport interface ActionConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The action handler function */\n handler: ActionFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n /**\n * Optional Zod schema describing return-type overrides for codegen.\n * Only the fields you specify are overridden; everything else is inferred\n * from the handler's TypeScript return type.\n */\n returns?: ZodType<any>;\n}\n\nexport interface JobConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for the job payload */\n args: ZodType<TInput>;\n /** The job handler function */\n handler: JobFn<TInput, TResult, TContext>;\n /**\n * Cron expression for a recurring schedule (e.g. `\"0 3 * * *\"`). Omit for a\n * job that is only invoked as a precise one-shot.\n */\n cron?: string;\n}\n\n/** Extract input type from a procedure */\nexport type InputOf<T> = T extends BaseProcedure<infer I, any, any, any> ? I : never;\n\n/** Extract output type from a procedure */\nexport type OutputOf<T> = T extends BaseProcedure<any, infer O, any, any> ? O : never;\n\n/** Extract procedure type (query/mutation) */\nexport type TypeOf<T> = T extends BaseProcedure<any, any, infer Type, any> ? Type : never;\n\n/** Extract server context type from a procedure */\nexport type ContextOf<T> = T extends BaseProcedure<any, any, any, infer C> ? C : never;\n\n/**\n * Create a query builder with a pre-defined context type.\n * This allows you to define the context type once and have it inferred\n * automatically in all your query handlers.\n * \n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n * \n * // Create a typed query builder\n * const query = createQueryBuilder<ServerContext>();\n * \n * // Use it - context type is automatically inferred!\n * const getUser = query({\n * args: z.object({ id: z.string() }),\n * handler: async (ctx, { id }) => {\n * // ctx.db for database queries\n * const user = await ctx.db.query(UsersSchema).find(id);\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return user;\n * }\n * });\n */\nexport function createQueryBuilder<TContext = unknown>() {\n return function query<TInput, TResult, const TInternal extends boolean = false>(\n config: QueryConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): QueryProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"query\",\n inputSchema: config.args,\n outputSchema: config.returns,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n queryIdentity: config.queryIdentity,\n };\n };\n}\n\n// ─── Mutation Builder Factory ────────────────────────────────────────────────\n\n/**\n * Create a mutation builder with a pre-defined context type.\n * This allows you to define the context type once and have it inferred\n * automatically in all your mutation handlers.\n * \n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n * \n * // Create a typed mutation builder\n * const mutation = createMutationBuilder<ServerContext>();\n * \n * // Use it - context type is automatically inferred!\n * const createUser = mutation({\n * args: z.object({ name: z.string() }),\n * handler: async (ctx, { name }) => {\n * // ctx.db for mutations\n * await ctx.db.insert(UsersSchema, id, { name });\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return { success: true };\n * }\n * });\n */\nexport function createMutationBuilder<TContext = unknown>() {\n return function mutation<TInput, TResult, const TInternal extends boolean = false>(\n config: MutationConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): MutationProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"mutation\",\n inputSchema: config.args,\n outputSchema: config.returns,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n };\n };\n}\n\n// ─── Action Builder Factory ──────────────────────────────────────────────────\n\n/**\n * Create an action builder with a pre-defined context type.\n * Actions have access to the full SupaliveDb for both queries and mutations.\n *\n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n *\n * // Create a typed action builder\n * const action = createActionBuilder<ServerContext>();\n *\n * // Use it - context type is automatically inferred!\n * const processOrder = action({\n * args: z.object({ orderId: z.string() }),\n * handler: async (ctx, { orderId }) => {\n * // ctx.db for full database access\n * const order = await ctx.db.query(async (db) => {\n * return db.query(OrdersSchema).find(orderId);\n * });\n * await ctx.db.mutation(async (db) => {\n * await db.update(OrdersSchema, orderId, { status: \"processed\" });\n * });\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return { success: true };\n * }\n * });\n */\nexport function createActionBuilder<TContext = unknown>() {\n return function action<TInput, TResult, const TInternal extends boolean = false>(\n config: ActionConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): ActionProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"action\",\n inputSchema: config.args,\n outputSchema: config.returns,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n };\n };\n}\n\n// ─── Job Builder Factory ─────────────────────────────────────────────────────\n\n/**\n * Create a job builder with a pre-defined context type. A job is a\n * server-only procedure invoked by the scheduler over HTTP — either on its\n * declared `cron` schedule or as a precise one-shot enqueued via the\n * server's job API. Jobs run like actions (full db, non-transactional) and\n * are always internal, so they are never reachable from a client.\n *\n * @example\n * const job = createJobBuilder<ServerContext>();\n *\n * export const cleanupOtps = job({\n * cron: \"0 * * * *\", // hourly\n * args: z.object({}),\n * handler: async (ctx) => {\n * await ctx.db.mutation(async (db) => { ... });\n * return { ok: true };\n * },\n * });\n */\nexport function createJobBuilder<TContext = unknown>() {\n return function job<TInput, TResult>(\n config: JobConfig<TInput, TResult, TContext>\n ): JobProcedure<TInput, TResult, TContext> {\n return {\n _type: \"procedure\",\n procedureType: \"job\",\n inputSchema: config.args,\n fn: config.handler,\n internal: true,\n cron: config.cron,\n };\n };\n}","import type { AnyProcedure } from \"./procedure\";\n\n// ─── Router Types ────────────────────────────────────────────────────────────\n\n/**\n * A router maps procedure names (keys) to their definitions.\n * This is the type that's exported from your app and used by the client.\n */\nexport type Router<TProcedures extends Record<string, AnyProcedure<TContext>>, TContext = unknown> = {\n _type: \"router\";\n procedures: TProcedures;\n contextName: string;\n};\n\n/**\n * Inferred AppRouter type from router() call.\n * Captures the full procedure map for client type inference.\n */\nexport type AppRouter<\n TProcedures extends Record<string, AnyProcedure<TContext>> = Record<string, AnyProcedure>,\n TContext = unknown\n> = Router<TProcedures, TContext>;\n\n// ─── Type Helpers for Client ─────────────────────────────────────────────────\n\n/** Get query procedures only from a router */\nexport type QueryProcedures<T extends Record<string, AnyProcedure>> = {\n [K in keyof T as T[K] extends { procedureType: \"query\" } ? K : never]: T[K];\n};\n\n/** Get mutation procedures only from a router */\nexport type MutationProcedures<T extends Record<string, AnyProcedure>> = {\n [K in keyof T as T[K] extends { procedureType: \"mutation\" } ? K : never]: T[K];\n};\n\n/** Get action procedures only from a router */\nexport type ActionProcedures<T extends Record<string, AnyProcedure>> = {\n [K in keyof T as T[K] extends { procedureType: \"action\" } ? K : never]: T[K];\n};\n\n/** Get job procedures only from a router */\nexport type JobProcedures<T extends Record<string, AnyProcedure>> = {\n [K in keyof T as T[K] extends { procedureType: \"job\" } ? K : never]: T[K];\n};\n\n/** Get public (non-internal) procedures - for client type safety */\nexport type PublicProcedures<T extends Record<string, AnyProcedure>> = Pick<\n T,\n { [K in keyof T]: T[K] extends { internal: true } ? never : K }[keyof T]\n>;\n\n/** Extract procedure names from a router */\nexport type ProcedureNames<T extends Router<any>> =\n T extends Router<infer P> ? keyof P : never;\n\nexport type RouterConfig<T> = {\n procedures: T;\n contextName?: string;\n};\n\n/**\n* Create a router from procedure definitions.\n* Procedure names are inferred from the object keys.\n* \n* @example\n* const appRouter = router({\n* procedures: {\n* getUser,\n* createUser,\n* internalGetAll,\n* },\n* });\n* \n* export type AppRouter = typeof appRouter;\n*/\nexport function router<TProcedures extends Record<string, AnyProcedure<any>>, TContext = unknown>(\n config: RouterConfig<TProcedures>,\n): Router<TProcedures, TContext> {\n const registry = getContextRegistry(config.contextName ?? \"default\");\n\n // Register all procedures at router creation time\n for (const [name, proc] of Object.entries(config.procedures)) {\n registry.registerProcedure(name, proc);\n }\n\n\n return {\n _type: \"router\",\n procedures: config.procedures,\n contextName: config.contextName ?? \"default\",\n };\n}\n\nexport interface RegisteredProcedure<TContext = unknown> {\n name: string;\n type: \"query\" | \"mutation\" | \"action\" | \"job\";\n internal: boolean;\n inputSchema: AnyProcedure<TContext>[\"inputSchema\"];\n fn: AnyProcedure<TContext>[\"fn\"];\n /** Only present for `type === \"query\"`. See `QueryIdentitySpec`. */\n queryIdentity?: false | string | ((serverCtx: TContext | undefined, input: unknown) => string | null | undefined);\n /** Only present for `type === \"job\"`. Cron expression for recurring jobs. */\n cron?: string;\n}\n\n\nconst contextRegistry = new Map<string, ContextRegistry>();\n\nexport function getContextRegistry(inContext: string): ContextRegistry {\n let registry = contextRegistry.get(inContext);\n if (!registry) {\n registry = new ContextRegistry();\n contextRegistry.set(inContext, registry);\n }\n return registry;\n}\n\nexport class ContextRegistry {\n procedureRegistry = new Map<string, RegisteredProcedure<any>>();\n internalProcedureNames = new Set<string>();\n\n registerProcedure<TContext>(name: string, proc: AnyProcedure<TContext>): void {\n if (this.procedureRegistry.has(name)) {\n throw new Error(\n `[router] Procedure \"${name}\" is already registered. ` +\n `Each procedure name must be unique across the application.`\n );\n }\n\n // Track internal procedures by name\n if (proc.internal) {\n this.markInternalProcedure(name);\n }\n\n const queryIdentity =\n proc.procedureType === \"query\"\n ? (proc as { queryIdentity?: RegisteredProcedure<TContext>[\"queryIdentity\"] }).queryIdentity\n : undefined;\n\n const cron =\n proc.procedureType === \"job\"\n ? (proc as { cron?: string }).cron\n : undefined;\n\n this.procedureRegistry.set(name, {\n name,\n type: proc.procedureType,\n internal: proc.internal,\n inputSchema: proc.inputSchema,\n fn: proc.fn,\n queryIdentity,\n cron,\n });\n }\n\n /** Get a registered procedure by name (runtime lookup) */\n getProcedure<TContext = unknown>(name: string): RegisteredProcedure<TContext> | undefined {\n return this.procedureRegistry.get(name);\n }\n\n /** Get all registered procedure names */\n getProcedureNames(): string[] {\n return [...this.procedureRegistry.keys()];\n }\n\n /** Get all registered procedures */\n getAllProcedures(): Map<string, RegisteredProcedure> {\n return new Map(this.procedureRegistry);\n }\n\n /** Get all registered job procedures (server-only, scheduler-triggered). */\n getJobProcedures(): RegisteredProcedure[] {\n return [...this.procedureRegistry.values()].filter((p) => p.type === \"job\");\n }\n\n /** Clear the registry (for testing only) */\n clearRegistry(): void {\n this.procedureRegistry.clear();\n this.clearInternalProcedures();\n }\n\n markInternalProcedure(name: string): void {\n this.internalProcedureNames.add(name);\n }\n\n isInternalProcedure(name: string): boolean {\n return this.internalProcedureNames.has(name);\n }\n\n clearInternalProcedures(): void {\n this.internalProcedureNames.clear();\n }\n}"],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { a as unwrapZodType, n as tableNameToId, r as commitTsCodec } from "./table-id-DyrJBKAQ.js";
|
|
2
|
+
import { z as z$1 } from "zod";
|
|
3
|
+
//#region src/db/schema.ts
|
|
4
|
+
function defineComputedField(config) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Read the effective max length declared on a Zod string (via `.max(n)` /
|
|
9
|
+
* `.length(n)`), unwrapping optional/nullable/default wrappers. Returns
|
|
10
|
+
* undefined for non-strings or unconstrained strings. Mirrors the reader in
|
|
11
|
+
* schema-sql.ts (kept local to avoid a schema <-> schema-sql import cycle) and
|
|
12
|
+
* is used to carry a primary-key id's length into the injected id column so the
|
|
13
|
+
* SQL generator can emit `VARCHAR(n)` instead of an (invalid-as-a-MySQL-PK)
|
|
14
|
+
* `TEXT`.
|
|
15
|
+
*/
|
|
16
|
+
function readZodStringMax(field) {
|
|
17
|
+
const unwrapped = unwrapZodType(field);
|
|
18
|
+
if (!(unwrapped instanceof z$1.ZodString)) return void 0;
|
|
19
|
+
const checks = unwrapped.def?.checks ?? [];
|
|
20
|
+
let maxLength;
|
|
21
|
+
for (const check of checks) {
|
|
22
|
+
const c = check?._zod?.def ?? check;
|
|
23
|
+
if (c?.check === "length_equals" || c?.check === "size_equals") {
|
|
24
|
+
const len = c.length ?? c.size;
|
|
25
|
+
if (typeof len === "number") maxLength = maxLength === void 0 ? len : Math.min(maxLength, len);
|
|
26
|
+
}
|
|
27
|
+
if (c?.check === "max_length" || c?.check === "max_size") {
|
|
28
|
+
const len = c.maximum ?? c.max;
|
|
29
|
+
if (typeof len === "number") maxLength = maxLength === void 0 ? len : Math.min(maxLength, len);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return maxLength;
|
|
33
|
+
}
|
|
34
|
+
const schemaRegistry = /* @__PURE__ */ new Map();
|
|
35
|
+
let shouldTrackSchema = false;
|
|
36
|
+
function trackSchema() {
|
|
37
|
+
shouldTrackSchema = true;
|
|
38
|
+
}
|
|
39
|
+
function getAutoIdCodec(idMeta, userCodec) {
|
|
40
|
+
if (userCodec) return void 0;
|
|
41
|
+
const rawType = idMeta.pgType ?? idMeta.mysqlType;
|
|
42
|
+
if (!rawType) return void 0;
|
|
43
|
+
const upper = rawType.toUpperCase().replace(/\(.*\)/, "").trim();
|
|
44
|
+
if ([
|
|
45
|
+
"BIGINT",
|
|
46
|
+
"INT8",
|
|
47
|
+
"SERIAL8"
|
|
48
|
+
].includes(upper)) return {
|
|
49
|
+
encode: (v) => BigInt(v),
|
|
50
|
+
decode: (v) => String(v)
|
|
51
|
+
};
|
|
52
|
+
if ([
|
|
53
|
+
"INTEGER",
|
|
54
|
+
"INT",
|
|
55
|
+
"SERIAL",
|
|
56
|
+
"INT4",
|
|
57
|
+
"SMALLINT",
|
|
58
|
+
"INT2"
|
|
59
|
+
].includes(upper)) return {
|
|
60
|
+
encode: (v) => Number(v),
|
|
61
|
+
decode: (v) => String(v)
|
|
62
|
+
};
|
|
63
|
+
if ([
|
|
64
|
+
"DECIMAL",
|
|
65
|
+
"NUMERIC",
|
|
66
|
+
"FLOAT",
|
|
67
|
+
"REAL",
|
|
68
|
+
"DOUBLE",
|
|
69
|
+
"FLOAT8",
|
|
70
|
+
"FLOAT4"
|
|
71
|
+
].includes(upper)) return {
|
|
72
|
+
encode: (v) => Number(v),
|
|
73
|
+
decode: (v) => String(v)
|
|
74
|
+
};
|
|
75
|
+
if (upper === "BOOLEAN" || upper === "BOOL" || upper.startsWith("TINYINT") && rawType.toUpperCase().includes("(1)")) return {
|
|
76
|
+
encode: (v) => v === "true" || v === "1",
|
|
77
|
+
decode: (v) => String(v)
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function defineSchema(config) {
|
|
81
|
+
const columns = {};
|
|
82
|
+
if (config.columns) Object.assign(columns, {
|
|
83
|
+
"id": "id",
|
|
84
|
+
"commitTs": "commit_ts",
|
|
85
|
+
...config.columns
|
|
86
|
+
});
|
|
87
|
+
else {
|
|
88
|
+
for (const key of Object.keys(config.schema)) columns[key] = key;
|
|
89
|
+
columns["id"] = "id";
|
|
90
|
+
columns["commitTs"] = "commit_ts";
|
|
91
|
+
}
|
|
92
|
+
const idMeta = {};
|
|
93
|
+
if ("id" in config.schema) {
|
|
94
|
+
const userMeta = config.schema.id?.meta?.();
|
|
95
|
+
if (userMeta) for (const k of Object.keys(userMeta)) {
|
|
96
|
+
if (k === "primaryKey") continue;
|
|
97
|
+
idMeta[k] = userMeta[k];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const userIdMax = "id" in config.schema ? readZodStringMax(config.schema.id) : void 0;
|
|
101
|
+
const idColumn = userIdMax !== void 0 ? z$1.string().max(userIdMax) : z$1.string();
|
|
102
|
+
const augmentedShape = {
|
|
103
|
+
...config.schema,
|
|
104
|
+
id: idColumn.meta({
|
|
105
|
+
primaryKey: true,
|
|
106
|
+
...idMeta
|
|
107
|
+
}),
|
|
108
|
+
commitTs: z$1.coerce.bigint()
|
|
109
|
+
};
|
|
110
|
+
const userCodecs = config.options?.codecs;
|
|
111
|
+
const autoIdCodec = getAutoIdCodec(idMeta, userCodecs?.id);
|
|
112
|
+
const mergedCodecs = {
|
|
113
|
+
...autoIdCodec ? { id: autoIdCodec } : {},
|
|
114
|
+
...userCodecs,
|
|
115
|
+
commitTs: commitTsCodec
|
|
116
|
+
};
|
|
117
|
+
const result = {
|
|
118
|
+
tableId: tableNameToId(config.name),
|
|
119
|
+
table: config.name,
|
|
120
|
+
schema: z$1.object(augmentedShape),
|
|
121
|
+
columnsOptions: {
|
|
122
|
+
...config.options,
|
|
123
|
+
codecs: mergedCodecs
|
|
124
|
+
},
|
|
125
|
+
columns,
|
|
126
|
+
indexes: config.indexes ?? []
|
|
127
|
+
};
|
|
128
|
+
if (shouldTrackSchema) schemaRegistry.set(config.name, result);
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { trackSchema as a, shouldTrackSchema as i, defineSchema as n, schemaRegistry as r, defineComputedField as t };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=schema-DLue3K2s.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-DLue3K2s.js","names":["z"],"sources":["../src/db/schema.ts"],"sourcesContent":["import { z, ZodRawShape } from \"zod\";\nimport { commitTsCodec, unwrapZodType } from \"./codec\";\nimport { LiveQueryHandle } from \"../client\";\nimport { tableNameToId } from \"./table-id\";\n\nexport interface ColumnCodec<TData = unknown, TDriver = unknown> {\n encode?: (value: TData) => TDriver;\n decode?: (value: TDriver) => TData;\n}\n\n// Constrain a codec's decoded type to match the schema field's inferred type.\n// Without this, `decode` could return any value (e.g. a string outside an enum)\n// and silently override the schema's declared type at the type level.\ntype SchemaCodecField<TSchema extends ZodRawShape, K extends PropertyKey> =\n K extends keyof TSchema\n ? TSchema[K] extends z.ZodTypeAny ? z.infer<TSchema[K]> : unknown\n : K extends keyof BaseSchema\n ? BaseSchema[K] extends z.ZodTypeAny ? z.infer<BaseSchema[K]> : unknown\n : unknown;\n\nexport type SchemaCodecs<TSchema extends ZodRawShape> = Partial<{\n [K in keyof (TSchema & BaseSchema)]: ColumnCodec<SchemaCodecField<TSchema, K>, any>;\n}>;\n\nexport interface SchemaColumnsOptions<TSchema extends ZodRawShape = ZodRawShape> {\n codecs?: SchemaCodecs<TSchema>;\n}\n\nexport interface SchemaColumnMapping {\n [key: string]: string;\n}\n\nexport interface DeclarativeIndex {\n name: string;\n columns: string[];\n unique?: boolean;\n type?: \"btree\" | \"hash\" | \"gin\" | \"gist\" | \"spgist\" | \"brin\";\n where?: string;\n using?: string;\n options?: string;\n tablespace?: string;\n}\n\nexport type IndexDefinition = string | DeclarativeIndex;\n\nexport interface DefineSchemaConfig<TSchema extends ZodRawShape> {\n name: string;\n schema: TSchema;\n options?: SchemaColumnsOptions<TSchema>;\n columns?: SchemaColumnMapping;\n indexes?: IndexDefinition[];\n}\n\n// Tuple-wrap the `undefined` checks so they don't fall through to the\n// object-shape conditionals when consumers compile under `strict: false`\n// (without `strictNullChecks`, `undefined` is assignable to object types and\n// would otherwise match `{ decode: ... }` / `ColumnCodec<...>` with all\n// `infer`s collapsing to `unknown`).\ntype CodecDataType<TCodec, TFallback> =\n [TCodec] extends [undefined] ? TFallback\n : TCodec extends { decode: (...args: any[]) => infer TDecoded } ? TDecoded\n : TCodec extends ColumnCodec<infer TData, unknown> ? TData\n : TFallback;\n\ntype ZodOutput<TZod> =\n [TZod] extends [undefined] ? unknown\n : TZod extends z.ZodTypeAny ? z.infer<TZod>\n : unknown;\nexport type ComputedDataType<TCodec, TZod> = CodecDataType<TCodec, ZodOutput<TZod>>;\nexport type ComputedCodec<TData, TDriver> = ColumnCodec<TData, TDriver>;\nexport type OverrideField<TObj, TKey extends PropertyKey, TValue> = Omit<TObj, TKey> & { [K in TKey]: TValue };\nexport interface ComputedFieldConfig<\n TField extends string,\n TDecoded = unknown,\n TDriver = unknown,\n TZodType extends z.ZodTypeAny | undefined = undefined,\n> {\n field: TField;\n sql: string;\n alias?: string;\n type?: TZodType;\n codec?: {\n decode: (value: TDriver) => TDecoded;\n encode?: (value: TDecoded) => TDriver;\n };\n}\n\nexport type ComputedField = {\n field: string;\n sql: string;\n alias: string;\n codec?: ColumnCodec<any, any>;\n};\n\nexport function defineComputedField<\n TField extends string,\n TDecoded = unknown,\n TDriver = unknown,\n TZodType extends z.ZodTypeAny | undefined = undefined,\n>(\n config: ComputedFieldConfig<TField, TDecoded, TDriver, TZodType>\n): ComputedFieldConfig<TField, TDecoded, TDriver, TZodType> {\n return config;\n}\n\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\n\ntype ApplySchemaCodecs<TBase, TCodecs> = {\n [K in keyof TBase]: K extends keyof TCodecs ? CodecDataType<TCodecs[K], TBase[K]> : TBase[K];\n};\n\n// Only apply codecs when `options.codecs` is explicitly provided. Without this\n// guard, the default constraint (`SchemaColumnsOptions<TSchema>`) makes\n// `TOptions[\"codecs\"]` infer as `Partial<{ [K]: ColumnCodec<any, any> }>`,\n// which collapses every field to `any` inside `ApplySchemaCodecs`.\ntype ResolveSchemaType<\n TSchema extends ZodRawShape,\n TOptions extends SchemaColumnsOptions<TSchema>\n> = TOptions extends { codecs: infer TCodecs }\n ? Prettify<ApplySchemaCodecs<z.infer<z.ZodObject<TSchema & BaseSchema>>, TCodecs>>\n : Prettify<z.infer<z.ZodObject<TSchema & BaseSchema>>>;\n\n// Helper to detect if a Zod type is optional or nullable\ntype IsOptionalField<T> =\n T extends z.ZodOptional<any> ? true :\n T extends z.ZodNullable<any> ? true :\n T extends z.ZodDefault<any> ? true :\n false;\n\n// Split schema keys into required vs optional\ntype OptionalKeys<TZod extends ZodRawShape> = {\n [K in keyof TZod]: IsOptionalField<TZod[K]> extends true ? K : never\n}[keyof TZod];\n\ntype RequiredKeys<TZod extends ZodRawShape> = {\n [K in keyof TZod]: IsOptionalField<TZod[K]> extends true ? never : K\n}[keyof TZod];\n\n// Add a base shape type that all schemas will include\ntype BaseSchema = {\n id: z.ZodString;\n commitTs: z.ZodCoercedBigInt;\n};\n\n/**\n * Read the effective max length declared on a Zod string (via `.max(n)` /\n * `.length(n)`), unwrapping optional/nullable/default wrappers. Returns\n * undefined for non-strings or unconstrained strings. Mirrors the reader in\n * schema-sql.ts (kept local to avoid a schema <-> schema-sql import cycle) and\n * is used to carry a primary-key id's length into the injected id column so the\n * SQL generator can emit `VARCHAR(n)` instead of an (invalid-as-a-MySQL-PK)\n * `TEXT`.\n */\nfunction readZodStringMax(field: unknown): number | undefined {\n const unwrapped = unwrapZodType(field);\n if (!(unwrapped instanceof z.ZodString)) return undefined;\n\n const checks = unwrapped.def?.checks ?? [];\n let maxLength: number | undefined;\n for (const check of checks) {\n const c = check?._zod?.def ?? check;\n if (c?.check === \"length_equals\" || c?.check === \"size_equals\") {\n const len = (c as any).length ?? (c as any).size;\n if (typeof len === \"number\") maxLength = maxLength === undefined ? len : Math.min(maxLength, len);\n }\n if (c?.check === \"max_length\" || c?.check === \"max_size\") {\n const len = (c as any).maximum ?? (c as any).max;\n if (typeof len === \"number\") maxLength = maxLength === undefined ? len : Math.min(maxLength, len);\n }\n }\n return maxLength;\n}\n\nexport const schemaRegistry = new Map<string, SchemaDefinition<string, any, any>>();\nexport let shouldTrackSchema = false;\n\nexport function trackSchema(): void {\n shouldTrackSchema = true;\n}\n\nfunction getAutoIdCodec(idMeta: Record<string, unknown>, userCodec: unknown): ColumnCodec<any, any> | undefined {\n if (userCodec) return undefined;\n const rawType = (idMeta.pgType as string | undefined) ?? (idMeta.mysqlType as string | undefined);\n if (!rawType) return undefined;\n\n const upper = rawType.toUpperCase().replace(/\\(.*\\)/, \"\").trim();\n if ([\"BIGINT\", \"INT8\", \"SERIAL8\"].includes(upper)) {\n return {\n encode: (v: string) => BigInt(v),\n decode: (v: unknown) => String(v),\n };\n }\n if ([\"INTEGER\", \"INT\", \"SERIAL\", \"INT4\", \"SMALLINT\", \"INT2\"].includes(upper)) {\n return {\n encode: (v: string) => Number(v),\n decode: (v: unknown) => String(v),\n };\n }\n if ([\"DECIMAL\", \"NUMERIC\", \"FLOAT\", \"REAL\", \"DOUBLE\", \"FLOAT8\", \"FLOAT4\"].includes(upper)) {\n return {\n encode: (v: string) => Number(v),\n decode: (v: unknown) => String(v),\n };\n }\n if (upper === \"BOOLEAN\" || upper === \"BOOL\" || (upper.startsWith(\"TINYINT\") && rawType.toUpperCase().includes(\"(1)\"))) {\n return {\n encode: (v: string) => v === \"true\" || v === \"1\",\n decode: (v: unknown) => String(v),\n };\n }\n return undefined;\n}\n\nexport function defineSchema<\n TSchema extends ZodRawShape,\n TName extends string,\n TOptions extends SchemaColumnsOptions<TSchema> = SchemaColumnsOptions<TSchema>\n>(\n config: DefineSchemaConfig<TSchema> & { name: TName; options?: TOptions }\n): SchemaDefinition<TName, Prettify<TSchema & BaseSchema>, ResolveSchemaType<TSchema, TOptions>> {\n const columns: Record<string, string> = {};\n\n if (config.columns) {\n Object.assign(columns, { 'id': 'id', 'commitTs': 'commit_ts', ...config.columns });\n } else {\n // Default: use field name as column name\n for (const key of Object.keys(config.schema)) {\n columns[key] = key;\n }\n columns['id'] = 'id';\n columns['commitTs'] = 'commit_ts';\n }\n\n // Preserve user's id meta (pgType, mysqlType, etc.) so the SQL generator\n // can emit the correct raw type. primaryKey is always forced true.\n const idMeta: Record<string, unknown> = {};\n if (\"id\" in config.schema) {\n const userMeta = (config.schema.id as any)?.meta?.();\n if (userMeta) {\n for (const k of Object.keys(userMeta)) {\n if (k === \"primaryKey\") continue; // always forced true\n idMeta[k] = userMeta[k];\n }\n }\n }\n\n // Preserve the user's id length constraint (`zodPrimaryIdColumn().max(32)`)\n // so the SQL generator emits `VARCHAR(n)`. `.max()` is a Zod *check*, not\n // metadata, so it isn't captured by `idMeta` above and must be re-applied to\n // the injected id — otherwise the PK renders as `TEXT`, which MySQL cannot\n // use as a primary key.\n const userIdMax = \"id\" in config.schema ? readZodStringMax(config.schema.id) : undefined;\n const idColumn = userIdMax !== undefined ? z.string().max(userIdMax) : z.string();\n\n // Merge id/commitTs into the Zod schema, user-defined fields win for everything else,\n // but id/commitTs are always forced to their correct types\n const augmentedShape = {\n ...config.schema,\n // always string, even if user defined it differently\n id: idColumn.meta({ primaryKey: true, ...idMeta }),\n // always bigint, mapped to commit_ts column\n commitTs: z.coerce.bigint(),\n };\n\n const userCodecs = config.options?.codecs;\n const autoIdCodec = getAutoIdCodec(idMeta, userCodecs?.id);\n const mergedCodecs = {\n ...(autoIdCodec ? { id: autoIdCodec } : {}),\n ...userCodecs,\n commitTs: commitTsCodec,\n };\n\n const result = {\n tableId: tableNameToId(config.name),\n table: config.name,\n schema: z.object(augmentedShape),\n columnsOptions: {\n ...config.options,\n codecs: mergedCodecs,\n },\n columns,\n indexes: config.indexes ?? [],\n } as SchemaDefinition<TName, TSchema & BaseSchema, ResolveSchemaType<TSchema, TOptions>>;\n\n if (shouldTrackSchema) {\n schemaRegistry.set(config.name, result);\n }\n\n return result\n}\n\n// tableNameToId now lives in ./table-id (leaf module, also home to the memoized\n// tableIdOf). Re-exported here so existing `from \"./schema\"` imports still resolve.\nexport { tableNameToId };\n\n// Stored codec map is intentionally loose: the strict per-field constraint is\n// enforced on the input (`DefineSchemaConfig.options`), but storing the strict\n// type here would break variance when assigning a specific schema to a generic\n// `SchemaDefinition<string, ZodRawShape>` constraint (ColumnCodec.encode is\n// contravariant in its data type).\nexport interface StoredSchemaColumnsOptions {\n codecs?: Record<string, ColumnCodec<any, any>>;\n}\n\nexport interface SchemaDefinition<\n TTable extends string,\n TZod extends ZodRawShape,\n TData = z.infer<z.ZodObject<TZod>>\n> {\n tableId: Uint8Array;\n table: TTable;\n schema: z.ZodObject<TZod>;\n columnsOptions: StoredSchemaColumnsOptions;\n columns: Record<string, string>;\n indexes: IndexDefinition[];\n _dataType?: TData;\n}\n\nexport type InferSchema<T extends SchemaDefinition<string, ZodRawShape, any>> =\n T extends SchemaDefinition<string, ZodRawShape, infer TData> ? TData : never;\n\nexport type InsertData<T extends SchemaDefinition<string, ZodRawShape, any>> =\n T extends SchemaDefinition<string, infer TZod, infer TData>\n ? {\n [K in RequiredKeys<TZod> as K extends \"id\" | \"commitTs\" ? never : K]:\n Extract<K, string> extends keyof TData ? TData[Extract<K, string>] : never\n } & {\n [K in OptionalKeys<TZod> as K extends \"id\" | \"commitTs\" ? never : K]?:\n Extract<K, string> extends keyof TData ? TData[Extract<K, string>] : never\n }\n : never;\n\nexport interface Model {\n table: string;\n tableId: Uint8Array,\n columns: Record<string, string>;\n reverseColumns: Record<string, string>;\n types: Record<string, string>;\n codecs: Record<string, ColumnCodec<any, any>>;\n}\n\n\ntype MaybePromise<T> = T | Promise<T>;\n\nexport type ReturnLiveQuery<\n TLiveQueryFn extends (...args: any[]) => MaybePromise<LiveQueryHandle<any>>\n> =\n Awaited<ReturnType<TLiveQueryFn>> extends LiveQueryHandle<infer T>\n ? T\n : never;\n\nexport type ReturnQuery<TQueryFn extends (...args: any[]) => any> =\n Awaited<ReturnType<TQueryFn>>;\n\nexport type QueryArgs<TQueryFn extends (...args: any[]) => any> =\n Parameters<TQueryFn>[0];\n\nexport type MutationArgs<TMutationFn extends (...args: any[]) => any> =\n Parameters<TMutationFn>[0];\n\nexport type ActionArgs<TActionFn extends (...args: any[]) => any> =\n Parameters<TActionFn>[0];"],"mappings":"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"}
|