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

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/lib/surql.js CHANGED
@@ -1,7 +1,9 @@
1
- import { BoundQuery, escapeIdent, escapeIdPart, RecordId, surql, Table, } from "surrealdb";
2
- import * as z4 from "zod/v4/core";
3
- import z from "zod";
1
+ import { BoundQuery, escapeIdent, escapeIdPart, surql, Table, toSurqlString as baseToSurqlString, } from "surrealdb";
2
+ import * as core from "zod/v4/core";
3
+ import * as _schema_ from "./zod/schema";
4
+ import * as _core_ from "./zod/core";
4
5
  import dedent from "dedent";
6
+ const OPEN_ISSUE_FOR_SUPPORT = " If you need this, please open an issue on the repository so we can look into your use case and possible implementation. https://github.com/msanchezdev/surreal-zod/issues";
5
7
  export function tableToSurql(table, statement, options) {
6
8
  if (statement === "define") {
7
9
  return defineTable(table, options);
@@ -81,31 +83,45 @@ export function defineTable(schema, options) {
81
83
  query.append(";\n");
82
84
  if (options?.fields) {
83
85
  for (const [fieldName, fieldSchema] of Object.entries(def.fields)) {
86
+ let defaultValue;
87
+ let comment;
88
+ let readonly;
89
+ let assert;
90
+ let value;
91
+ if (fieldSchema instanceof _schema_.ZodSurrealField) {
92
+ defaultValue = fieldSchema._zod.def.surreal.field?.default;
93
+ comment = fieldSchema._zod.def.surreal.field?.comment;
94
+ readonly = fieldSchema._zod.def.surreal.field?.readonly;
95
+ assert = fieldSchema._zod.def.surreal.field?.assert;
96
+ value = fieldSchema._zod.def.surreal.field?.value;
97
+ }
84
98
  query.append(defineField(fieldName, table.name, fieldName === "id"
85
99
  ? fieldSchema._zod.def.innerType
86
100
  : fieldSchema, {
87
101
  exists: options.exists,
88
102
  schemafull: surreal.schemafull,
103
+ default: defaultValue,
104
+ comment,
105
+ readonly,
106
+ assert,
107
+ value,
108
+ // @ts-expect-error - @internal
109
+ // We need to add the table schema to the fullParents set to avoid
110
+ // infinite recursion. Child fields keep a separate context from
111
+ // the table or other fields.
112
+ // The resting point for fullParents is in the inferSurrealType function,
113
+ // on the switch case for the "lazy" type.
114
+ fullParents: new Set([schema]),
89
115
  }));
90
116
  }
91
117
  }
92
118
  return query;
93
119
  }
94
- export function isNormalTable(table) {
95
- return table._zod.def.surreal.tableType === "normal";
96
- }
97
120
  export function isRelationTable(table) {
98
- return table._zod.def.surreal.tableType === "relation";
121
+ return (table instanceof _schema_.ZodSurrealTable &&
122
+ table._zod.def.surreal.tableType === "relation");
99
123
  }
100
- function defineField(name, table, schema, options) {
101
- // const context: ZodSurrealTypeContext = {
102
- // name,
103
- // table,
104
- // rootSchema: schema,
105
- // children: [],
106
- // asserts: [],
107
- // transforms: [],
108
- // };
124
+ export function defineField(name, table, schema, options) {
109
125
  const query = surql `DEFINE FIELD`;
110
126
  if (options?.exists === "ignore") {
111
127
  query.append(" IF NOT EXISTS");
@@ -117,23 +133,37 @@ function defineField(name, table, schema, options) {
117
133
  const context = {
118
134
  type: new Set(),
119
135
  depth: 0,
136
+ parents: new Set(),
137
+ // @ts-expect-error - @internal
138
+ fullParents: options?.fullParents ?? new Set(),
120
139
  children: [],
121
140
  flexible: false,
122
141
  };
123
- query.append(` TYPE ${inferSurrealType(schema, context)}`);
142
+ query.append(` TYPE ${inferSurrealType(schema, context).type}`);
124
143
  if (options?.schemafull && context.flexible) {
125
144
  query.append(" FLEXIBLE");
126
145
  }
127
- // if (options.exists === "ignore") {
128
- // query.append(" IF NOT EXISTS");
129
- // } else if (options.exists === "overwrite") {
130
- // query.append(" OVERWRITE");
131
- // }
132
- // query.append(` ${name} ON TABLE ${table.name}`);
146
+ if (options?.readonly) {
147
+ query.append(" READONLY");
148
+ }
149
+ if (options?.default) {
150
+ query.append(options.default.always
151
+ ? ` DEFAULT ALWAYS ${formatQuery(inlineQueryParameters(options.default.value))}`
152
+ : ` DEFAULT ${formatQuery(inlineQueryParameters(options.default.value))}`);
153
+ }
154
+ if (options?.value) {
155
+ query.append(` VALUE ${inlineQueryParameters(options.value)}`);
156
+ }
157
+ if (options?.assert) {
158
+ query.append(` ASSERT ${inlineQueryParameters(options.assert)}`);
159
+ }
160
+ if (options?.comment) {
161
+ query.append(` COMMENT ${JSON.stringify(options.comment)}`);
162
+ }
133
163
  // const type =
134
164
  // name === "id"
135
165
  // ? inferSurrealType(
136
- // (schema as unknown as SurrealZodRecordId)._zod.def.innerType,
166
+ // (schema as unknown as ZodSurrealRecordId)._zod.def.innerType,
137
167
  // [],
138
168
  // context,
139
169
  // )
@@ -170,273 +200,639 @@ function defineField(name, table, schema, options) {
170
200
  // }
171
201
  query.append(`;\n`);
172
202
  if (context.children.length > 0) {
203
+ context.fullParents.add(schema);
173
204
  for (const { name: childName, type: childType } of context.children) {
174
- query.append(defineField(`${escapeIdent(name)}.${escapeIdent(childName)}`, table, childType, {
205
+ query.append(defineField(`${escapeIdent(name)}.${childName === "*" ? childName : escapeIdent(childName)}`, table, childType, {
175
206
  exists: options?.exists,
207
+ // @ts-expect-error - @internal
208
+ fullParents: context.fullParents,
176
209
  }));
177
210
  }
178
211
  }
179
212
  return query;
180
213
  }
214
+ function createContext(override) {
215
+ return {
216
+ type: new Set(),
217
+ depth: 0,
218
+ parents: new Set(),
219
+ fullParents: new Set(),
220
+ children: [],
221
+ flexible: false,
222
+ ...override,
223
+ };
224
+ }
181
225
  export function inferSurrealType(type, context) {
226
+ context ??= createContext();
227
+ function enter(type, ctx) {
228
+ context ??= createContext();
229
+ if (context.parents.has(type)) {
230
+ console.warn("Recursive type detected", type._zod.def.type);
231
+ context.type.add("any");
232
+ // throw new Error("Recursive type detected");
233
+ return { type: "any", context };
234
+ }
235
+ const newContext = ctx
236
+ ? {
237
+ children: [],
238
+ flexible: false,
239
+ type: new Set(),
240
+ depth: context.depth + 1,
241
+ ...(typeof ctx === "object" ? ctx : {}),
242
+ parents: context.parents,
243
+ fullParents: context.fullParents,
244
+ }
245
+ : context;
246
+ context.parents.add(type);
247
+ context.fullParents.add(type);
248
+ const result = inferSurrealType(type, newContext);
249
+ context.fullParents.delete(type);
250
+ context.parents.delete(type);
251
+ return result;
252
+ }
182
253
  const schema = type;
183
254
  if (!("_zod" in schema)) {
184
- throw new Error("Invalid schema provided, make sure you are using zod v4 as zod v3 is currently not supported.");
255
+ throw new Error("Invalid schema provided, make sure you are using zod v4+");
185
256
  }
186
- // if ("surreal" in schema._zod.def) {
187
- // return schema._zod.def.surreal.type;
188
- // } else {
189
- // throw new Error(
190
- // // @ts-expect-error - zod core not supported
191
- // `Invalid surreal schema provided. Received ${schema._zod.def.type}`,
192
- // );
193
- // }
257
+ // console.log(schema);
194
258
  const def = schema._zod.def;
195
- const childIndent = " ".repeat(context.depth + 1);
196
259
  // const checks = getChecks(schema);
197
260
  // parseChecks(context.name, checks, context, def.type);
198
261
  // console.log(zodToSexpr(type));
199
- switch (def.surreal?.type ?? def.type) {
200
- case "any":
201
- case "unknown": {
202
- context.type.add("any");
203
- break;
204
- }
205
- case "never":
206
- case "undefined": {
207
- context.type.add("none");
208
- break;
209
- }
210
- case "optional": {
211
- inferSurrealType(type._zod.def.innerType, context);
212
- context.type.add("none");
213
- break;
214
- }
215
- case "nonoptional": {
216
- inferSurrealType(type._zod.def.innerType, context);
217
- if (context.type.size > 1 && context.type.has("none")) {
218
- context.type.delete("none");
262
+ // console.log(def);
263
+ if (def.surreal.type) {
264
+ context.type.add(def.surreal.type);
265
+ }
266
+ else
267
+ switch ( /* isSurrealZodSchemaDef(def) ? */def.type /*: def.type*/) {
268
+ // case "record_id": {
269
+ // const table = (def as ZodSurrealdRecordId["_zod"]["def"]).table;
270
+ // if (table) {
271
+ // context.type.add(`record<${table.map(escapeIdent).join(" | ")}>`);
272
+ // } else {
273
+ // context.type.add("record");
274
+ // }
275
+ // break;
276
+ // }
277
+ // case "uuid": {
278
+ // context.type.add("uuid");
279
+ // break;
280
+ // }
281
+ // case "table": {
282
+ // throw new Error(
283
+ // `Table type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`,
284
+ // );
285
+ // }
286
+ // case "void":
287
+ // case "never":
288
+ // case "undefined": {
289
+ // context.type.add("none");
290
+ // break;
291
+ // }
292
+ case "optional": {
293
+ enter(def.innerType);
294
+ context.type.add("none");
295
+ break;
219
296
  }
220
- break;
221
- }
222
- case "null": {
223
- context.type.add("null");
224
- break;
225
- }
226
- case "nullable": {
227
- inferSurrealType(type._zod.def.innerType, context);
228
- context.type.add("null");
229
- break;
230
- }
231
- case "boolean": {
232
- context.type.add("bool");
233
- break;
234
- }
235
- case "string": {
236
- context.type.add("string");
237
- break;
238
- }
239
- case "bigint":
240
- case "number": {
241
- context.type.add("number");
242
- break;
243
- }
244
- case "object": {
245
- const _schema = schema;
246
- const shape = _schema._zod.def.shape;
247
- const catchall = _schema._zod.def.catchall;
248
- const isStrict = catchall?._zod.traits.has("$ZodNever");
249
- const isLoose = catchall?._zod.traits.has("$ZodUnknown");
250
- // buggy syntax
251
- // if (isStrict) {
252
- // let type = "{";
253
- // if (Object.keys(shape).length > 0) {
254
- // type += "\n";
297
+ case "nonoptional": {
298
+ enter(def.innerType);
299
+ if (context.type.size > 1 && context.type.has("none")) {
300
+ context.type.delete("none");
301
+ }
302
+ break;
303
+ }
304
+ case "nullable": {
305
+ enter(def.innerType);
306
+ context.type.add("null");
307
+ break;
308
+ }
309
+ // case "boolean": {
310
+ // context.type.add("bool");
311
+ // break;
312
+ // }
313
+ // case "string": {
314
+ // // Needs override, this will not work with original zod types
315
+ // // if (isStringFormat(def)) {
316
+ // // switch (def.format) {
317
+ // // case "uuid":
318
+ // // case "guid":
319
+ // // context.type.add("uuid");
320
+ // // break TYPE_CHECK;
321
+ // // }
322
+ // // }
323
+ // context.type.add("string");
324
+ // break;
325
+ // }
326
+ // case "bigint": {
327
+ // if (!isBigIntFormat(def as core.$ZodBigIntFormatDef)) {
328
+ // context.type.add("int");
329
+ // break;
255
330
  // }
256
- // for (const [key, value] of Object.entries(shape)) {
257
- // const childContext: ZodSurrealTypeContext = {
258
- // type: new Set(),
259
- // depth: context.depth + 1,
260
- // children: [],
261
- // };
262
- // type += `${childIndent}${escapeIdent(key)}: ${inferSurrealType(value, childContext)},\n`;
331
+ // switch ((def as core.$ZodBigIntFormatDef).format) {
332
+ // case "int64":
333
+ // case "uint64":
334
+ // context.type.add("int");
335
+ // break;
336
+ // default:
337
+ // throw new Error(
338
+ // `Unsupported bigint format: ${(def as core.$ZodBigIntFormatDef).format}`,
339
+ // );
263
340
  // }
264
- // type += "}";
265
- // context.type.add(type);
266
341
  // break;
267
342
  // }
268
- context.type.add("object");
269
- if (isLoose)
270
- context.flexible = true;
271
- for (const [key, value] of Object.entries(shape)) {
272
- context.children.push({ name: key, type: value });
343
+ // case "number": {
344
+ // if (!isNumberFormat(def as core.$ZodNumberDef)) {
345
+ // context.type.add("number");
346
+ // break;
347
+ // }
348
+ // switch ((def as core.$ZodNumberFormatDef).format) {
349
+ // case "uint32":
350
+ // case "safeint":
351
+ // case "int32":
352
+ // context.type.add("int");
353
+ // break;
354
+ // case "float64":
355
+ // case "float32":
356
+ // context.type.add("float");
357
+ // break;
358
+ // default:
359
+ // throw new Error(
360
+ // `Unsupported number format: ${(def as core.$ZodNumberFormatDef).format}. ${OPEN_ISSUE_FOR_SUPPORT}`,
361
+ // );
362
+ // }
363
+ // break;
364
+ // }
365
+ // case "date": {
366
+ // context.type.add("datetime");
367
+ // break;
368
+ // }
369
+ case "object": {
370
+ const shape = def.shape;
371
+ const catchall = def.catchall;
372
+ const isStrict = catchall?._zod.traits.has("$ZodNever");
373
+ const isLoose = catchall?._zod.traits.has("$ZodUnknown");
374
+ // buggy syntax
375
+ // if (isStrict) {
376
+ // let type = "{";
377
+ // if (Object.keys(shape).length > 0) {
378
+ // type += "\n";
379
+ // }
380
+ // for (const [key, value] of Object.entries(shape)) {
381
+ // const childContext: ZodSurrealTypeContext = {
382
+ // type: new Set(),
383
+ // depth: context.depth + 1,
384
+ // children: [],
385
+ // };
386
+ // type += `${childIndent}${escapeIdent(key)}: ${inferSurrealType(value, childContext).inner},\n`;
387
+ // }
388
+ // type += "}";
389
+ // context.type.add(type);
390
+ // break;
391
+ // }
392
+ context.type.add("object");
393
+ if (isLoose)
394
+ context.flexible = true;
395
+ for (const [key, value] of Object.entries(shape)) {
396
+ context.children.push({ name: key, type: value });
397
+ }
398
+ break;
273
399
  }
274
- break;
275
- }
276
- case "record_id": {
277
- const table = def.table;
278
- if (table) {
279
- context.type.add(`record<${table.map(escapeIdent).join(" | ")}>`);
400
+ case "array": {
401
+ const { type: element } = enter(def.element, true);
402
+ if (element === "any") {
403
+ context.type.add("array");
404
+ break;
405
+ }
406
+ context.type.add(`array<${element}>`);
407
+ break;
280
408
  }
281
- else {
282
- context.type.add("record");
409
+ case "set": {
410
+ const { type: element } = enter(def.valueType, true);
411
+ if (element === "any") {
412
+ context.type.add("array");
413
+ break;
414
+ }
415
+ context.type.add(`array<${element}>`);
416
+ break;
417
+ }
418
+ case "enum": {
419
+ const values = def.entries;
420
+ for (const key in values) {
421
+ const value = values[key];
422
+ context.type.add(toSurqlString(value));
423
+ }
424
+ break;
425
+ }
426
+ case "union": {
427
+ for (const option of def.options) {
428
+ // context.type.add(inferSurrealType(option, context).inner);
429
+ enter(option);
430
+ }
431
+ break;
432
+ }
433
+ // case "intersection": {
434
+ // // TODO: Find a way to handle intersections
435
+ // // Maybe a new function where we build a new object schema (or primitive one)
436
+ // // And keep track of all the types that are used in the intersection
437
+ // //
438
+ // // const left = def.left;
439
+ // // const right = def.right;
440
+ // // inferSurrealType(left, context);
441
+ // // inferSurrealType(right, context);
442
+ // // inferSurrealType(z.never(), context);
443
+ // // context.type.add("any");
444
+ // context.type.add("any");
445
+ // break;
446
+ // }
447
+ case "tuple": {
448
+ if (def.rest) {
449
+ context.type.add("array");
450
+ break;
451
+ }
452
+ const types = new Set();
453
+ for (const item of def.items) {
454
+ types.add(enter(item, true).type);
455
+ }
456
+ context.type.add(`[${Array.from(types).join(", ")}]`);
457
+ break;
458
+ }
459
+ // case "record": {
460
+ // context.type.add("object");
461
+ // // Currently there is no way to restrict the key type of an object, so we just use *
462
+ // // context.children.push({ name: "*", type: def.keyType });
463
+ // // All commented out code is because of this. Check is only done in JS side.
464
+ // enter((def as core.$ZodRecordDef).keyType, true);
465
+ // /* const isPartial =
466
+ // def.keyType._zod.values === undefined &&
467
+ // !keyContext.type.has("string") &&
468
+ // !keyContext.type.has("number") &&
469
+ // !keyContext.type.has("int") &&
470
+ // !keyContext.type.has("float") &&
471
+ // !keyContext.type.has("decimal"); */
472
+ // context.children.push({
473
+ // name: "*",
474
+ // type: /* isPartial ? z.optional(def.valueType) : */ (
475
+ // def as core.$ZodRecordDef
476
+ // ).valueType,
477
+ // });
478
+ // break;
479
+ // }
480
+ // case "map": {
481
+ // context.type.add("object");
482
+ // // Currently there is no way to restrict the key type of an object, so we just use *
483
+ // // Surreal doesnt have a map type, so we use object instead. We cant really support non PropertyKey values
484
+ // // unless we serialize the keys to strings.
485
+ // const { context: keyContext } = enter(
486
+ // (def as core.$ZodMapDef).keyType,
487
+ // true,
488
+ // );
489
+ // for (const key of keyContext.type) {
490
+ // if (!["string", "number", "float", "int", "decimal"].includes(key)) {
491
+ // throw new Error(`Unsupported key type: ${key}`);
492
+ // }
493
+ // }
494
+ // context.children.push({
495
+ // name: "*",
496
+ // type: (def as core.$ZodMapDef).valueType,
497
+ // });
498
+ // break;
499
+ // }
500
+ case "literal": {
501
+ for (const value of def.values) {
502
+ context.type.add(toSurqlString(value));
503
+ }
504
+ break;
505
+ }
506
+ case "file": {
507
+ throw new Error(`File type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`);
508
+ }
509
+ case "transform": {
510
+ break;
511
+ }
512
+ case "readonly":
513
+ case "catch":
514
+ case "prefault":
515
+ case "default": {
516
+ enter(def.innerType);
517
+ break;
518
+ }
519
+ case "success": {
520
+ context.type.add("string");
521
+ break;
522
+ }
523
+ case "nan": {
524
+ context.type.add("number");
525
+ break;
526
+ }
527
+ case "pipe": {
528
+ enter(def.in);
529
+ enter(def.out);
530
+ break;
531
+ }
532
+ case "template_literal": {
533
+ context.type.add("string");
534
+ break;
535
+ }
536
+ case "lazy": {
537
+ const innerType = def.getter();
538
+ // All that cascading to get here, we dont want to keep complex types if
539
+ // recursive.
540
+ if (context.fullParents.has(type) || context.parents.has(type)) {
541
+ context.type.add("any");
542
+ }
543
+ else {
544
+ enter(innerType);
545
+ }
546
+ break;
547
+ }
548
+ case "promise": {
549
+ // We will not support promises for now, this can be uncommented after
550
+ // support is added
551
+ // const { inner: innerType } =
552
+ // enter(def.innerType);
553
+ // context.type.add(innerType);
554
+ throw new Error(`Promise type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`);
555
+ }
556
+ case "function": {
557
+ throw new Error(`Function type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`);
558
+ }
559
+ case "custom": {
560
+ throw new Error(`Custom type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`);
561
+ }
562
+ case "symbol": {
563
+ throw new Error(`Symbol type cannot be used as a field type.${OPEN_ISSUE_FOR_SUPPORT}`);
283
564
  }
284
- break;
285
- }
286
- case "table": {
287
- throw new Error("Table type cannot be used as a field type");
288
565
  }
289
- }
290
- if (context.type.has("any")) {
291
- return "any";
292
- }
293
- return Array.from(context.type).join(" | ");
566
+ const inner = context.type.has("any") || context.type.size === 0
567
+ ? "any"
568
+ : Array.from(context.type).join(" | ");
569
+ return { type: inner, context };
294
570
  }
295
- // function getChecks(_schema: z4.$ZodType | SurrealZodType) {
296
- // const schema = _schema as z4.$ZodTypes | SurrealZodTypes;
297
- // const checks = schema._zod.def.checks ?? [];
298
- // if ("check" in schema._zod.def) {
299
- // checks.unshift(schema as z4.$ZodCheck);
300
- // }
301
- // return checks;
571
+ // function isSurrealZodSchemaDef(
572
+ // def: core.$ZodTypeDef | ZodSurrealTypeDef,
573
+ // ): def is ZodSurrealTypeDef {
574
+ // return "surreal" in def && def.surreal.type !== undefined;
302
575
  // }
303
- // function parseChecks(
304
- // name: string,
305
- // checks: z4.$ZodCheck[],
306
- // context: ZodSurrealTypeContext,
307
- // type: ZodTypeName | SurrealZodTypeName,
308
- // ) {
309
- // for (const check of checks) {
310
- // const { transform, assert } = parseCheck(name, check, type);
311
- // if (transform) {
312
- // context.transforms.push(transform);
313
- // }
314
- // if (assert) {
315
- // context.asserts.push(assert);
316
- // }
317
- // }
576
+ // function isStringFormat(
577
+ // def: core.$ZodStringDef,
578
+ // ): def is core.$ZodStringFormatTypes["_zod"]["def"] {
579
+ // return def.type === "string" && "format" in def;
318
580
  // }
319
- // export const checkMap = {
320
- // never(name: string) {
321
- // return `THROW 'Field "${name}" must never be present'`;
322
- // },
323
- // min_length(name: string, value: number, type: ZodTypeName) {
324
- // if (type === "array") {
325
- // return `$value.len() >= ${value} || { THROW 'Field "${name}" must have at least ${value} ${value === 1 ? "item" : "items"}' };`;
326
- // }
327
- // if (type === "string") {
328
- // return `$value.len() >= ${value} || { THROW 'Field "${name}" must be at least ${value} ${value === 1 ? "character" : "characters"} long' };`;
329
- // }
330
- // throw new Error(`Invalid type: ${type}`);
331
- // },
332
- // max_length(name: string, value: number, type: ZodTypeName) {
333
- // if (type === "array") {
334
- // return `$value.len() <= ${value} || { THROW 'Field "${name}" must have at most ${value} ${value === 1 ? "item" : "items"}' };`;
335
- // }
336
- // if (type === "string") {
337
- // return `$value.len() <= ${value} || { THROW 'Field "${name}" must be at most ${value} ${value === 1 ? "character" : "characters"} long' };`;
338
- // }
339
- // throw new Error(`Invalid type: ${type}`);
340
- // },
341
- // greater_than(name: string, value: z4.util.Numeric, inclusive: boolean) {
342
- // return `$value ${inclusive ? ">=" : ">"} ${value} || { THROW 'Field "${name}" must be greater than ${inclusive ? "or equal to" : ""} ${value}' };`;
343
- // },
344
- // less_than(name: string, value: z4.util.Numeric, inclusive: boolean) {
345
- // return `$value ${inclusive ? "<=" : "<"} ${value} || { THROW 'Field "${name}" must be less than ${inclusive ? "or equal to" : ""} ${value}' };`;
346
- // },
347
- // length_equals(name: string, value: number, type: ZodTypeName = "string") {
348
- // if (type === "array") {
349
- // return `$value.len() == ${value} || { THROW 'Field "${name}" must have exactly ${value} ${value === 1 ? "item" : "items"}' };`;
350
- // }
351
- // if (type === "string") {
352
- // return `$value.len() == ${value} || { THROW 'Field "${name}" must be exactly ${value} ${value === 1 ? "character" : "characters"} long' };`;
353
- // }
354
- // throw new Error(`Invalid type: ${type}`);
355
- // },
356
- // string_format: {
357
- // email: (name: string) => {
358
- // const regex =
359
- // /^[A-Za-z0-9'_+-]+(?:\.[A-Za-z0-9'_+-]+)*@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/;
360
- // return `string::matches($value, ${regex}) || { THROW "Field '${name}' must be a valid email address" };`;
361
- // },
362
- // url: (
363
- // name: string,
364
- // def?: Pick<z4.$ZodCheckURLParams, "hostname" | "protocol" | "normalize">,
365
- // ) => {
366
- // return dedent`
367
- // LET $url = {
368
- // scheme: parse::url::scheme($value),
369
- // host: parse::url::host($value),
370
- // domain: parse::url::domain($value),
371
- // path: parse::url::path($value),
372
- // port: parse::url::port($value),
373
- // query: parse::url::query($value),
374
- // hash: parse::url::fragment($value),
375
- // };
376
- // $url.scheme || { THROW "Field '${name}' must be a valid URL" };
377
- // ${
378
- // def?.hostname
379
- // ? `($url.host ?? "").matches(${def.hostname}) || { THROW "Field '${name}' must match hostname ${def.hostname.toString().replace(/\\/g, "\\\\")}" };`
380
- // : ""
381
- // }
382
- // ${
383
- // def?.protocol
384
- // ? `($url.scheme ?? "").matches(${def.protocol}) || { THROW "Field '${name}' must match protocol ${def.protocol.toString().replace(/\\/g, "\\\\")}" };`
385
- // : ""
386
- // }
387
- // $url.scheme + "://" + ($url.host ?? "") + (
388
- // IF $url.port && (
389
- // ($url.scheme == "http" && $url.port != 80) ||
390
- // ($url.scheme == "https" && $url.port != 443)
391
- // ) { ":" + <string>$url.port } ?? ""
392
- // )
393
- // + ($url.path ?? "")
394
- // + (IF $url.query { "?" + $url.query } ?? "")
395
- // + (IF $url.fragment { "#" + $url.fragment } ?? "");
396
- // `;
397
- // },
398
- // },
399
- // };
400
- // function parseCheck(
401
- // name: string,
402
- // _check: z4.$ZodCheck,
403
- // type: ZodTypeName,
404
- // ): { transform?: string; assert?: string } {
405
- // const check = _check as z4.$ZodChecks;
406
- // const def = check._zod.def;
407
- // switch (def.check) {
408
- // case "min_length":
409
- // return { assert: checkMap.min_length(name, def.minimum, type) };
410
- // case "max_length":
411
- // return { assert: checkMap.max_length(name, def.maximum, type) };
412
- // case "greater_than":
413
- // return { assert: checkMap.greater_than(name, def.value, def.inclusive) };
414
- // case "less_than":
415
- // return { assert: checkMap.less_than(name, def.value, def.inclusive) };
416
- // case "length_equals":
417
- // return { assert: checkMap.length_equals(name, def.length, type) };
418
- // case "string_format":
419
- // return assertionForStringFormat(name, check);
420
- // default:
421
- // return { assert: `THROW 'Unknown check: ${def.check}';` };
422
- // }
581
+ // function isBigIntFormat(
582
+ // def: core.$ZodBigIntDef,
583
+ // ): def is core.$ZodBigIntFormatDef {
584
+ // return def.type === "bigint" && "format" in def;
423
585
  // }
424
- // // Remove look-around, look-behind, and look-ahead as they are not supported by SurrealDB
425
- // function assertionForStringFormat(
426
- // name: string,
427
- // _check: z4.$ZodCheck,
428
- // ): { transform?: string; assert?: string } {
429
- // const check = _check as z4.$ZodStringFormatChecks;
430
- // const def = check._zod.def;
431
- // switch (def.format) {
432
- // case "email": {
433
- // return { assert: checkMap.string_format.email(name) };
434
- // }
435
- // case "url": {
436
- // const code = checkMap.string_format.url(name, def);
437
- // return def.normalize ? { transform: code } : { assert: code };
438
- // }
439
- // default:
440
- // return { assert: `THROW 'Unsupported string format: ${def.format}';` };
441
- // }
586
+ // function isNumberFormat(
587
+ // def: core.$ZodNumberDef,
588
+ // ): def is core.$ZodNumberFormatDef {
589
+ // return def.type === "number" && "format" in def;
442
590
  // }
591
+ // // function getChecks(_schema: z4.$ZodType | SurrealZodType) {
592
+ // // const schema = _schema as z4.$ZodTypes | SurrealZodTypes;
593
+ // // const checks = schema._zod.def.checks ?? [];
594
+ // // if ("check" in schema._zod.def) {
595
+ // // checks.unshift(schema as z4.$ZodCheck);
596
+ // // }
597
+ // // return checks;
598
+ // // }
599
+ // // function parseChecks(
600
+ // // name: string,
601
+ // // checks: z4.$ZodCheck[],
602
+ // // context: ZodSurrealTypeContext,
603
+ // // type: ZodTypeName | SurrealZodTypeName,
604
+ // // ) {
605
+ // // for (const check of checks) {
606
+ // // const { transform, assert } = parseCheck(name, check, type);
607
+ // // if (transform) {
608
+ // // context.transforms.push(transform);
609
+ // // }
610
+ // // if (assert) {
611
+ // // context.asserts.push(assert);
612
+ // // }
613
+ // // }
614
+ // // }
615
+ // // export const checkMap = {
616
+ // // never(name: string) {
617
+ // // return `THROW 'Field "${name}" must never be present'`;
618
+ // // },
619
+ // // min_length(name: string, value: number, type: ZodTypeName) {
620
+ // // if (type === "array") {
621
+ // // return `$value.len() >= ${value} || { THROW 'Field "${name}" must have at least ${value} ${value === 1 ? "item" : "items"}' };`;
622
+ // // }
623
+ // // if (type === "string") {
624
+ // // return `$value.len() >= ${value} || { THROW 'Field "${name}" must be at least ${value} ${value === 1 ? "character" : "characters"} long' };`;
625
+ // // }
626
+ // // throw new Error(`Invalid type: ${type}`);
627
+ // // },
628
+ // // max_length(name: string, value: number, type: ZodTypeName) {
629
+ // // if (type === "array") {
630
+ // // return `$value.len() <= ${value} || { THROW 'Field "${name}" must have at most ${value} ${value === 1 ? "item" : "items"}' };`;
631
+ // // }
632
+ // // if (type === "string") {
633
+ // // return `$value.len() <= ${value} || { THROW 'Field "${name}" must be at most ${value} ${value === 1 ? "character" : "characters"} long' };`;
634
+ // // }
635
+ // // throw new Error(`Invalid type: ${type}`);
636
+ // // },
637
+ // // greater_than(name: string, value: z4.util.Numeric, inclusive: boolean) {
638
+ // // return `$value ${inclusive ? ">=" : ">"} ${value} || { THROW 'Field "${name}" must be greater than ${inclusive ? "or equal to" : ""} ${value}' };`;
639
+ // // },
640
+ // // less_than(name: string, value: z4.util.Numeric, inclusive: boolean) {
641
+ // // return `$value ${inclusive ? "<=" : "<"} ${value} || { THROW 'Field "${name}" must be less than ${inclusive ? "or equal to" : ""} ${value}' };`;
642
+ // // },
643
+ // // length_equals(name: string, value: number, type: ZodTypeName = "string") {
644
+ // // if (type === "array") {
645
+ // // return `$value.len() == ${value} || { THROW 'Field "${name}" must have exactly ${value} ${value === 1 ? "item" : "items"}' };`;
646
+ // // }
647
+ // // if (type === "string") {
648
+ // // return `$value.len() == ${value} || { THROW 'Field "${name}" must be exactly ${value} ${value === 1 ? "character" : "characters"} long' };`;
649
+ // // }
650
+ // // throw new Error(`Invalid type: ${type}`);
651
+ // // },
652
+ // // string_format: {
653
+ // // email: (name: string) => {
654
+ // // const regex =
655
+ // // /^[A-Za-z0-9'_+-]+(?:\.[A-Za-z0-9'_+-]+)*@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/;
656
+ // // return `string::matches($value, ${regex}) || { THROW "Field '${name}' must be a valid email address" };`;
657
+ // // },
658
+ // // url: (
659
+ // // name: string,
660
+ // // def?: Pick<z4.$ZodCheckURLParams, "hostname" | "protocol" | "normalize">,
661
+ // // ) => {
662
+ // // return dedent`
663
+ // // LET $url = {
664
+ // // scheme: parse::url::scheme($value),
665
+ // // host: parse::url::host($value),
666
+ // // domain: parse::url::domain($value),
667
+ // // path: parse::url::path($value),
668
+ // // port: parse::url::port($value),
669
+ // // query: parse::url::query($value),
670
+ // // hash: parse::url::fragment($value),
671
+ // // };
672
+ // // $url.scheme || { THROW "Field '${name}' must be a valid URL" };
673
+ // // ${
674
+ // // def?.hostname
675
+ // // ? `($url.host ?? "").matches(${def.hostname}) || { THROW "Field '${name}' must match hostname ${def.hostname.toString().replace(/\\/g, "\\\\")}" };`
676
+ // // : ""
677
+ // // }
678
+ // // ${
679
+ // // def?.protocol
680
+ // // ? `($url.scheme ?? "").matches(${def.protocol}) || { THROW "Field '${name}' must match protocol ${def.protocol.toString().replace(/\\/g, "\\\\")}" };`
681
+ // // : ""
682
+ // // }
683
+ // // $url.scheme + "://" + ($url.host ?? "") + (
684
+ // // IF $url.port && (
685
+ // // ($url.scheme == "http" && $url.port != 80) ||
686
+ // // ($url.scheme == "https" && $url.port != 443)
687
+ // // ) { ":" + <string>$url.port } ?? ""
688
+ // // )
689
+ // // + ($url.path ?? "")
690
+ // // + (IF $url.query { "?" + $url.query } ?? "")
691
+ // // + (IF $url.fragment { "#" + $url.fragment } ?? "");
692
+ // // `;
693
+ // // },
694
+ // // },
695
+ // // };
696
+ // // function parseCheck(
697
+ // // name: string,
698
+ // // _check: z4.$ZodCheck,
699
+ // // type: ZodTypeName,
700
+ // // ): { transform?: string; assert?: string } {
701
+ // // const check = _check as z4.$ZodChecks;
702
+ // // const def = check._zod.def;
703
+ // // switch (def.check) {
704
+ // // case "min_length":
705
+ // // return { assert: checkMap.min_length(name, def.minimum, type) };
706
+ // // case "max_length":
707
+ // // return { assert: checkMap.max_length(name, def.maximum, type) };
708
+ // // case "greater_than":
709
+ // // return { assert: checkMap.greater_than(name, def.value, def.inclusive) };
710
+ // // case "less_than":
711
+ // // return { assert: checkMap.less_than(name, def.value, def.inclusive) };
712
+ // // case "length_equals":
713
+ // // return { assert: checkMap.length_equals(name, def.length, type) };
714
+ // // case "string_format":
715
+ // // return assertionForStringFormat(name, check);
716
+ // // default:
717
+ // // return { assert: `THROW 'Unknown check: ${def.check}';` };
718
+ // // }
719
+ // // }
720
+ // // // Remove look-around, look-behind, and look-ahead as they are not supported by SurrealDB
721
+ // // function assertionForStringFormat(
722
+ // // name: string,
723
+ // // _check: z4.$ZodCheck,
724
+ // // ): { transform?: string; assert?: string } {
725
+ // // const check = _check as z4.$ZodStringFormatChecks;
726
+ // // const def = check._zod.def;
727
+ // // switch (def.format) {
728
+ // // case "email": {
729
+ // // return { assert: checkMap.string_format.email(name) };
730
+ // // }
731
+ // // case "url": {
732
+ // // const code = checkMap.string_format.url(name, def);
733
+ // // return def.normalize ? { transform: code } : { assert: code };
734
+ // // }
735
+ // // default:
736
+ // // return { assert: `THROW 'Unsupported string format: ${def.format}';` };
737
+ // // }
738
+ // // }
739
+ const SurrealQLStatements = [
740
+ "ACCESS",
741
+ "ALTER",
742
+ "BEGIN",
743
+ "BREAK",
744
+ "CANCEL",
745
+ "COMMIT",
746
+ "CONTINUE",
747
+ "CREATE",
748
+ "DEFINE",
749
+ "DELETE",
750
+ "FOR",
751
+ "IF",
752
+ "INFO",
753
+ "INSERT",
754
+ "KILL",
755
+ "LET",
756
+ "LIVE",
757
+ "REBUILD",
758
+ "RELATE",
759
+ "REMOVE",
760
+ "RETURN",
761
+ "SELECT",
762
+ "SHOW",
763
+ "SLEEP",
764
+ "THROW",
765
+ "UPDATE",
766
+ "UPSERT",
767
+ "USE",
768
+ "EXPLAIN",
769
+ "FETCH",
770
+ ];
771
+ function hasMultipleStatements(query) {
772
+ let inString = false;
773
+ let escaping = false;
774
+ let statements = 0;
775
+ let buffer = "";
776
+ for (let i = 0; i < query.length; i++) {
777
+ const ch = query[i];
778
+ const nextBuffer = `${buffer}${ch}`;
779
+ if (ch === "\n")
780
+ return true;
781
+ if (inString) {
782
+ if (escaping) {
783
+ escaping = false;
784
+ continue;
785
+ }
786
+ if (ch === "\\") {
787
+ escaping = true;
788
+ continue;
789
+ }
790
+ if (ch === inString) {
791
+ inString = false;
792
+ continue;
793
+ }
794
+ continue;
795
+ }
796
+ if (ch === '"' || ch === "'") {
797
+ inString = ch;
798
+ continue;
799
+ }
800
+ if (ch === " ") {
801
+ if (SurrealQLStatements.includes(buffer.toUpperCase())) {
802
+ return true;
803
+ }
804
+ continue;
805
+ }
806
+ if (ch === ";" ||
807
+ // Single line Comments
808
+ nextBuffer === "--" ||
809
+ nextBuffer === "//" ||
810
+ ch === "#") {
811
+ statements++;
812
+ buffer = "";
813
+ continue;
814
+ }
815
+ buffer = nextBuffer;
816
+ if (statements > 1)
817
+ return true;
818
+ }
819
+ if (statements > 0 && (buffer || inString || escaping))
820
+ return true;
821
+ return statements > 1;
822
+ }
823
+ export function formatQuery(query) {
824
+ if (hasMultipleStatements(query)) {
825
+ return `{\n ${dedent.withOptions({ alignValues: true }) ` ${query.trimStart()}`}\n}`;
826
+ }
827
+ return query.trim();
828
+ }
829
+ export function inlineQueryParameters(query) {
830
+ let value = query.query;
831
+ for (const [paramName, paramValue] of Object.entries(query.bindings ?? {})) {
832
+ value = value.replace(new RegExp(`\\$${paramName}\\b`, "g"), toSurqlString(paramValue));
833
+ }
834
+ return value;
835
+ }
836
+ function toSurqlString(value) {
837
+ return baseToSurqlString(value).replace(/^s"/g, '"');
838
+ }