surreal-zod 0.0.0-alpha.1 → 0.0.0-alpha.10

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.
@@ -0,0 +1,603 @@
1
+ /** biome-ignore-all lint/suspicious/noExplicitAny: needed for conversion */
2
+ /** biome-ignore-all lint/style/noNonNullAssertion: needed for conversion */
3
+ import { BoundQuery, escapeIdent, RecordId, surql, Table, } from "surrealdb";
4
+ import * as core from "zod/v4/core";
5
+ import * as classic from "zod/v4";
6
+ import { defineTable, inferSurrealType, tableToSurql, } from "../surql";
7
+ export const SurrealZodType = core.$constructor("SurrealZodType", (inst, def) => {
8
+ // @ts-expect-error - unknown assertion error
9
+ core.$ZodType.init(inst, def);
10
+ // Casting as _surreal.type is built while the schema is initialized
11
+ inst._zod.def.surreal ??= {};
12
+ // base methods
13
+ inst.clone = (def, params) => core.clone(inst, def, params);
14
+ // parsing
15
+ inst.parse = (data, params) => {
16
+ return core.parse(inst, data, params);
17
+ };
18
+ // inst.safeParse = (data, params) => {
19
+ // return core.safeParse(inst, data, params);
20
+ // };
21
+ // wrappers
22
+ inst.optional = () => optional(inst);
23
+ inst.nonoptional = () => nonoptional(inst);
24
+ inst.nullable = () => nullable(inst);
25
+ inst.nullish = () => nullish(inst);
26
+ return inst;
27
+ });
28
+ export const SurrealZodAny = core.$constructor("SurrealZodAny", (inst, def) => {
29
+ // @ts-expect-error - unknown assertion error
30
+ core.$ZodAny.init(inst, def);
31
+ SurrealZodType.init(inst, def);
32
+ // surreal internals
33
+ inst._zod.def.surreal.type = "any";
34
+ });
35
+ export function any() {
36
+ return core._any(SurrealZodAny);
37
+ }
38
+ export const SurrealZodUnknown = core.$constructor("SurrealZodUnknown", (inst, def) => {
39
+ // @ts-expect-error - unknown assertion error
40
+ core.$ZodUnknown.init(inst, def);
41
+ SurrealZodType.init(inst, def);
42
+ // surreal internals
43
+ inst._zod.def.surreal.type = "unknown";
44
+ });
45
+ export function unknown() {
46
+ return core._unknown(SurrealZodUnknown);
47
+ }
48
+ export const SurrealZodNever = core.$constructor("SurrealZodNever", (inst, def) => {
49
+ // @ts-expect-error - unknown assertion error
50
+ core.$ZodNever.init(inst, def);
51
+ SurrealZodType.init(inst, def);
52
+ // surreal internals
53
+ inst._zod.def.surreal.type = "never";
54
+ });
55
+ export function never(params) {
56
+ return core._never(SurrealZodNever, params);
57
+ }
58
+ export const SurrealZodUndefined = core.$constructor("SurrealZodUndefined", (inst, def) => {
59
+ // @ts-expect-error - unknown assertion error
60
+ core.$ZodUndefined.init(inst, def);
61
+ SurrealZodType.init(inst, def);
62
+ // surreal internals
63
+ inst._zod.def.surreal.type = "undefined";
64
+ });
65
+ function _undefined(params) {
66
+ return core._undefined(SurrealZodUndefined, params);
67
+ }
68
+ export { _undefined as undefined };
69
+ export const SurrealZodNull = core.$constructor("SurrealZodNull", (inst, def) => {
70
+ // @ts-expect-error - unknown assertion error
71
+ core.$ZodNull.init(inst, def);
72
+ SurrealZodType.init(inst, def);
73
+ // surreal internals
74
+ inst._zod.def.surreal.type = "null";
75
+ });
76
+ function _null(params) {
77
+ return core._null(SurrealZodNull, params);
78
+ }
79
+ export { _null as null };
80
+ export const SurrealZodBoolean = core.$constructor("SurrealZodBoolean", (inst, def) => {
81
+ // @ts-expect-error - unknown assertion error
82
+ core.$ZodBoolean.init(inst, def);
83
+ SurrealZodType.init(inst, def);
84
+ // surreal internals
85
+ inst._zod.def.surreal.type = "boolean";
86
+ });
87
+ export function boolean(params) {
88
+ return core._boolean(SurrealZodBoolean, params);
89
+ }
90
+ export const SurrealZodString = core.$constructor("SurrealZodString", (inst, def) => {
91
+ // @ts-expect-error - unknown assertion error
92
+ core.$ZodString.init(inst, def);
93
+ SurrealZodType.init(inst, def);
94
+ // surreal internals
95
+ inst._zod.def.surreal.type = "string";
96
+ });
97
+ export function string(params) {
98
+ return core._string(SurrealZodString, params);
99
+ }
100
+ export const SurrealZodNumber = core.$constructor("SurrealZodNumber", (inst, def) => {
101
+ // @ts-expect-error - unknown assertion error
102
+ core.$ZodNumber.init(inst, def);
103
+ SurrealZodType.init(inst, def);
104
+ // surreal internals
105
+ inst._zod.def.surreal.type = "number";
106
+ });
107
+ export function number(params) {
108
+ return core._number(SurrealZodNumber, params);
109
+ }
110
+ export const SurrealZodBigInt = core.$constructor("SurrealZodBigInt", (inst, def) => {
111
+ // @ts-expect-error - unknown assertion error
112
+ core.$ZodBigInt.init(inst, def);
113
+ SurrealZodType.init(inst, def);
114
+ // surreal internals
115
+ inst._zod.def.surreal.type = "bigint";
116
+ });
117
+ export function bigint(params) {
118
+ return core._bigint(SurrealZodBigInt, params);
119
+ }
120
+ export const SurrealZodObject = core.$constructor("SurrealZodObject", (inst, def) => {
121
+ // TODO: Inline implementation and use core instead
122
+ // @ts-expect-error - unknown assertion error
123
+ core.$ZodObject.init(inst, def);
124
+ SurrealZodType.init(inst, def);
125
+ // surreal internals
126
+ inst._zod.def.surreal.type = "object";
127
+ // inst._zod.def.surreal.flexible = false;
128
+ inst.loose = () => inst.clone({
129
+ ...def,
130
+ catchall: unknown(),
131
+ });
132
+ inst.flexible = inst.loose;
133
+ inst.strict = () => inst.clone({
134
+ ...def,
135
+ catchall: never(),
136
+ });
137
+ inst.extend = (incoming) => core.util.extend(inst, incoming);
138
+ inst.safeExtend = (incoming) => core.util.safeExtend(inst, incoming);
139
+ });
140
+ export function object(shape, params) {
141
+ const def = {
142
+ type: "object",
143
+ shape: shape ?? {},
144
+ ...core.util.normalizeParams(params),
145
+ };
146
+ return new SurrealZodObject({
147
+ ...def,
148
+ surreal: {
149
+ type: "object",
150
+ flexible: false,
151
+ },
152
+ });
153
+ }
154
+ function normalizeRecordIdDef(def) {
155
+ const invalidType = getInvalidRecordIdValueSchema(def.innerType);
156
+ if (invalidType) {
157
+ throw new Error(`${invalidType} is not valid as a RecordId's value`);
158
+ }
159
+ return {
160
+ ...def,
161
+ };
162
+ }
163
+ function getInvalidRecordIdValueSchema(schema) {
164
+ const def = schema._zod.def;
165
+ switch (def.type) {
166
+ case "any":
167
+ case "string":
168
+ case "number":
169
+ return "";
170
+ default:
171
+ return def.type;
172
+ }
173
+ }
174
+ export const SurrealZodRecordId = core.$constructor("SurrealZodRecordId", (inst, def) => {
175
+ SurrealZodType.init(inst, def);
176
+ // surreal internals
177
+ inst._zod.def.surreal.type = "record_id";
178
+ const normalized = normalizeRecordIdDef(def);
179
+ inst.anytable = () => {
180
+ return inst.clone({
181
+ ...def,
182
+ table: undefined,
183
+ });
184
+ };
185
+ inst.table = (table) => {
186
+ return inst.clone({
187
+ ...inst._zod.def,
188
+ table: Array.isArray(table) ? table : [table],
189
+ });
190
+ };
191
+ inst.type = (innerType) => {
192
+ return inst.clone({
193
+ ...inst._zod.def,
194
+ innerType,
195
+ });
196
+ };
197
+ inst._zod.parse = (payload, ctx) => {
198
+ if (payload.value instanceof RecordId) {
199
+ if (normalized.table &&
200
+ !normalized.table.includes(payload.value.table.name)) {
201
+ payload.issues.push({
202
+ code: "invalid_value",
203
+ values: normalized.table,
204
+ input: payload.value.table.name,
205
+ message: normalized.table.length > 1
206
+ ? `Expected RecordId's table to be one of ${normalized.table.map(escapeIdent).join(" | ")} but found ${payload.value.table.name}`
207
+ : `Expected RecordId's table to be ${normalized.table[0]} but found ${payload.value.table.name}`,
208
+ });
209
+ }
210
+ const schema = normalized.innerType._zod;
211
+ const result = schema.run({ value: payload.value.id, issues: [] }, ctx);
212
+ if (result instanceof Promise) {
213
+ return result.then((result) => {
214
+ if (result.issues.length) {
215
+ payload.issues.push(...core.util.prefixIssues("id", result.issues));
216
+ }
217
+ payload.value = new RecordId(payload.value.table.name, result.value);
218
+ return payload;
219
+ });
220
+ }
221
+ else if (result.issues.length) {
222
+ payload.issues.push(...core.util.prefixIssues("id", result.issues));
223
+ }
224
+ payload.value = new RecordId(payload.value.table.name, result.value);
225
+ }
226
+ else {
227
+ payload.issues.push({
228
+ code: "invalid_type",
229
+ // TODO: Surreal specific issues
230
+ expected: "custom",
231
+ input: payload.value,
232
+ });
233
+ }
234
+ return payload;
235
+ };
236
+ return inst;
237
+ });
238
+ export function recordId(what, innerType) {
239
+ return new SurrealZodRecordId({
240
+ // Zod would not be happy if we have a custom type here, so we use any
241
+ type: "any",
242
+ table: what ? (Array.isArray(what) ? what : [what]) : undefined,
243
+ innerType: innerType ?? any(),
244
+ surreal: {
245
+ type: "record_id",
246
+ },
247
+ });
248
+ }
249
+ function handleFieldResult(result, final, field, input) {
250
+ if (result.issues.length) {
251
+ final.issues.push(...core.util.prefixIssues(field, result.issues));
252
+ }
253
+ if (result.value === undefined) {
254
+ if (field in input) {
255
+ // @ts-expect-error: field not index-checked on final.value
256
+ final.value[field] = undefined;
257
+ }
258
+ }
259
+ else {
260
+ // @ts-expect-error: field not index-checked on final.value
261
+ final.value[field] = result.value;
262
+ }
263
+ }
264
+ function handleCatchall(promises, input, payload, ctx, def, inst) {
265
+ const unrecognized = [];
266
+ const known = def.fieldNamesSet;
267
+ const _catchall = def.catchall._zod;
268
+ const type = _catchall.def.type;
269
+ for (const field in input) {
270
+ if (known.has(field))
271
+ continue;
272
+ if (type === "never") {
273
+ unrecognized.push(field);
274
+ continue;
275
+ }
276
+ const result = _catchall.run({ value: input[field], issues: [] }, ctx);
277
+ if (result instanceof Promise) {
278
+ promises.push(result.then((result) => handleFieldResult(result, payload, field, input)));
279
+ }
280
+ else {
281
+ handleFieldResult(result, payload, field, input);
282
+ }
283
+ }
284
+ if (unrecognized.length) {
285
+ payload.issues.push({
286
+ code: "unrecognized_keys",
287
+ keys: unrecognized,
288
+ input,
289
+ inst,
290
+ });
291
+ }
292
+ if (!promises.length)
293
+ return payload;
294
+ return Promise.all(promises).then(() => payload);
295
+ }
296
+ function normalizeTableDef(def) {
297
+ const fields = {};
298
+ const fieldNames = Object.keys(def.fields);
299
+ if (def.fields.id) {
300
+ if (def.fields.id instanceof SurrealZodRecordId) {
301
+ fields.id = def.fields.id.table(def.name);
302
+ }
303
+ else {
304
+ fields.id = recordId(def.name).type(def.fields.id);
305
+ }
306
+ }
307
+ else {
308
+ fields.id = recordId(def.name).type(any());
309
+ fieldNames.push("id");
310
+ }
311
+ if (def.dto && !(fields.id instanceof SurrealZodOptional)) {
312
+ fields.id = optional(fields.id);
313
+ }
314
+ for (const field of fieldNames) {
315
+ if (field === "id")
316
+ continue;
317
+ // if (!def.fields[field]?._zod.traits.has("SurrealZodType")) {
318
+ // throw new Error(
319
+ // `Invalid field definition for "${field}": expected a Surreal Zod schema`,
320
+ // );
321
+ // }
322
+ fields[field] = def.fields[field];
323
+ }
324
+ return {
325
+ ...def,
326
+ fields,
327
+ fieldNames,
328
+ fieldNamesSet: new Set(fieldNames),
329
+ };
330
+ }
331
+ export const SurrealZodTable = core.$constructor("SurrealZodTable", (inst, def) => {
332
+ SurrealZodType.init(inst, def);
333
+ const normalized = normalizeTableDef(def);
334
+ // @ts-expect-error - through normalization id is always present
335
+ inst._zod.def.fields = normalized.fields;
336
+ const catchall = normalized.catchall;
337
+ inst.name = (name) => {
338
+ return inst.clone({
339
+ ...inst._zod.def,
340
+ name,
341
+ });
342
+ };
343
+ inst.fields = (fields) => {
344
+ return inst.clone({
345
+ ...inst._zod.def,
346
+ // @ts-expect-error - id may or may not be provided
347
+ fields,
348
+ });
349
+ };
350
+ inst.any = () => {
351
+ return inst.clone({
352
+ ...inst._zod.def,
353
+ surreal: {
354
+ ...inst._zod.def.surreal,
355
+ tableType: "any",
356
+ },
357
+ });
358
+ };
359
+ inst.normal = () => {
360
+ return new SurrealZodTableNormal({
361
+ ...inst._zod.def,
362
+ surreal: {
363
+ ...inst._zod.def.surreal,
364
+ tableType: "normal",
365
+ },
366
+ });
367
+ };
368
+ inst.relation = () => {
369
+ // @ts-expect-error - id set in constructor
370
+ return new SurrealZodTableRelation({
371
+ ...inst._zod.def,
372
+ // fields: {
373
+ // in: recordId().type(any()),
374
+ // out: recordId().type(any()),
375
+ // ...def.fields,
376
+ // },
377
+ surreal: {
378
+ ...inst._zod.def.surreal,
379
+ tableType: "relation",
380
+ },
381
+ });
382
+ };
383
+ inst.comment = (comment) => {
384
+ return inst.clone({
385
+ ...inst._zod.def,
386
+ surreal: {
387
+ ...inst._zod.def.surreal,
388
+ comment,
389
+ },
390
+ });
391
+ };
392
+ inst.schemafull = () => {
393
+ return inst.clone({
394
+ ...inst._zod.def,
395
+ catchall: never(),
396
+ surreal: {
397
+ ...inst._zod.def.surreal,
398
+ schemafull: true,
399
+ },
400
+ });
401
+ };
402
+ inst.schemaless = () => {
403
+ return inst.clone({
404
+ ...inst._zod.def,
405
+ catchall: unknown(),
406
+ surreal: {
407
+ ...inst._zod.def.surreal,
408
+ schemafull: false,
409
+ },
410
+ });
411
+ };
412
+ inst.drop = () => {
413
+ return inst.clone({
414
+ ...inst._zod.def,
415
+ surreal: {
416
+ ...inst._zod.def.surreal,
417
+ drop: true,
418
+ },
419
+ });
420
+ };
421
+ inst.nodrop = () => {
422
+ return inst.clone({
423
+ ...inst._zod.def,
424
+ surreal: {
425
+ ...inst._zod.def.surreal,
426
+ drop: false,
427
+ },
428
+ });
429
+ };
430
+ // @ts-expect-error - through normalization id is always present
431
+ inst.record = () => normalized.fields.id;
432
+ inst.dto = () => {
433
+ return inst.clone({
434
+ ...inst._zod.def,
435
+ dto: true,
436
+ });
437
+ };
438
+ inst.entity = () => {
439
+ let id = normalized.fields.id;
440
+ while (id && id instanceof SurrealZodOptional) {
441
+ id = id.unwrap();
442
+ }
443
+ return inst.clone({
444
+ ...inst._zod.def,
445
+ dto: false,
446
+ fields: {
447
+ ...normalized.fields,
448
+ id,
449
+ },
450
+ });
451
+ };
452
+ // @ts-expect-error - overloaded
453
+ inst.toSurql = (statement = "define", options) =>
454
+ // @ts-expect-error - overloaded
455
+ tableToSurql(inst, statement, options);
456
+ inst._zod.parse = (payload, ctx) => {
457
+ const input = payload.value;
458
+ if (!core.util.isObject(input)) {
459
+ payload.issues.push({
460
+ expected: "object",
461
+ code: "invalid_type",
462
+ input,
463
+ inst,
464
+ });
465
+ return payload;
466
+ }
467
+ payload.value = {};
468
+ const promises = [];
469
+ const fields = normalized.fields;
470
+ for (const field of normalized.fieldNames) {
471
+ const schema = fields[field];
472
+ const result = schema._zod.run({ value: input[field], issues: [] }, ctx);
473
+ if (result instanceof Promise) {
474
+ promises.push(result.then((result) => {
475
+ handleFieldResult(result, payload, field, input);
476
+ }));
477
+ }
478
+ else {
479
+ handleFieldResult(result, payload, field, input);
480
+ }
481
+ }
482
+ if (!catchall) {
483
+ return promises.length
484
+ ? Promise.all(promises).then(() => payload)
485
+ : payload;
486
+ }
487
+ return handleCatchall(promises, input, payload, ctx, normalized, inst);
488
+ };
489
+ return inst;
490
+ });
491
+ export function table(name) {
492
+ return new SurrealZodTable({
493
+ type: "any",
494
+ name,
495
+ // @ts-expect-error - id set in constructor
496
+ fields: {},
497
+ catchall: unknown(),
498
+ surreal: {
499
+ type: "table",
500
+ tableType: "any",
501
+ schemafull: false,
502
+ drop: false,
503
+ comment: undefined,
504
+ },
505
+ });
506
+ }
507
+ export const SurrealZodTableNormal = core.$constructor("SurrealZodTableNormal", (inst, def) => {
508
+ SurrealZodTable.init(inst, def);
509
+ });
510
+ export function normalTable(name) {
511
+ return table(name).normal();
512
+ }
513
+ export const SurrealZodTableRelation = core.$constructor("SurrealZodTableRelation", (inst, def) => {
514
+ SurrealZodTable.init(inst, def);
515
+ inst.from = (from) => {
516
+ return new SurrealZodTableRelation({
517
+ ...def,
518
+ fields: {
519
+ ...def.fields,
520
+ in: from instanceof SurrealZodRecordId ? from : recordId(from),
521
+ },
522
+ });
523
+ };
524
+ inst.to = (to) => {
525
+ return new SurrealZodTableRelation({
526
+ ...def,
527
+ fields: {
528
+ ...def.fields,
529
+ out: to instanceof SurrealZodRecordId ? to : recordId(to),
530
+ },
531
+ });
532
+ };
533
+ inst.in = inst.from;
534
+ inst.out = inst.to;
535
+ inst.fields = (fields) => {
536
+ return new SurrealZodTableRelation({
537
+ ...def,
538
+ fields: {
539
+ ...def.fields,
540
+ ...fields,
541
+ },
542
+ });
543
+ };
544
+ return inst;
545
+ });
546
+ export function relationTable(name) {
547
+ return table(name).relation();
548
+ }
549
+ export const SurrealZodOptional = core.$constructor("SurrealZodOptional", (inst, def) => {
550
+ // @ts-expect-error - unknown assertion error
551
+ core.$ZodOptional.init(inst, def);
552
+ SurrealZodType.init(inst, def);
553
+ inst.unwrap = () => {
554
+ return inst._zod.def.innerType;
555
+ };
556
+ });
557
+ export function optional(innerType) {
558
+ return new SurrealZodOptional({
559
+ type: "optional",
560
+ innerType,
561
+ surreal: {
562
+ type: "optional",
563
+ },
564
+ });
565
+ }
566
+ export const SurrealZodNonOptional = core.$constructor("SurrealZodNonOptional", (inst, def) => {
567
+ // @ts-expect-error - unknown assertion error
568
+ core.$ZodNonOptional.init(inst, def);
569
+ SurrealZodType.init(inst, def);
570
+ });
571
+ export function nonoptional(innerType) {
572
+ return new SurrealZodNonOptional({
573
+ type: "nonoptional",
574
+ innerType,
575
+ surreal: {
576
+ type: "nonoptional",
577
+ },
578
+ });
579
+ }
580
+ export const SurrealZodNullable = core.$constructor("SurrealZodNullable", (inst, def) => {
581
+ // @ts-expect-error - unknown assertion error
582
+ core.$ZodNullable.init(inst, def);
583
+ SurrealZodType.init(inst, def);
584
+ });
585
+ export function nullable(innerType) {
586
+ return new SurrealZodNullable({
587
+ type: "nullable",
588
+ innerType,
589
+ surreal: {
590
+ type: "nullable",
591
+ },
592
+ });
593
+ }
594
+ /////////////////////////////////////////////////
595
+ /////////////////////////////////////////////////
596
+ ////////// //////////
597
+ ////////// SurrealZodNullish //////////
598
+ ////////// //////////
599
+ /////////////////////////////////////////////////
600
+ /////////////////////////////////////////////////
601
+ export function nullish(innerType) {
602
+ return optional(nullable(innerType));
603
+ }
@@ -0,0 +1,2 @@
1
+ import type { SurrealZodTableFields } from "./schema";
2
+ export declare const optionalFields: (fields: SurrealZodTableFields) => string[];
@@ -0,0 +1,2 @@
1
+ import * as core from "zod/v4/core";
2
+ export const optionalFields = core.util.optionalKeys;
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "surreal-zod",
3
- "version": "0.0.0-alpha.1",
4
- "module": "index.ts",
3
+ "version": "0.0.0-alpha.10",
5
4
  "scripts": {
6
- "build": "tsc"
5
+ "build": "tsc",
6
+ "prepack": "bun run build"
7
7
  },
8
8
  "files": [
9
9
  "lib",
10
10
  "src"
11
11
  ],
12
+ "module": "lib/index.js",
13
+ "types": "lib/index.d.ts",
12
14
  "exports": {
13
15
  ".": {
14
16
  "bun": "./src/index.ts",
@@ -30,6 +32,6 @@
30
32
  "dependencies": {
31
33
  "@surrealdb/node": "2.3.4",
32
34
  "dedent": "^1.7.0",
33
- "surrealdb": "^2.0.0-alpha.13"
35
+ "surrealdb": "^2.0.0-alpha.14"
34
36
  }
35
37
  }
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- import * as sz from "./zod";
1
+ import * as sz from "./zod/schema";
2
2
  export { sz };
3
3
  export default sz;