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

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
@@ -2,350 +2,441 @@ import { BoundQuery, escapeIdent, escapeIdPart, RecordId, surql, Table, } from "
2
2
  import * as z4 from "zod/v4/core";
3
3
  import z from "zod";
4
4
  import dedent from "dedent";
5
- export function zodToSurql(options) {
6
- const table = typeof options.table === "string"
7
- ? new Table(options.table)
8
- : options.table;
9
- const schema = options.schema;
10
- if (!("_zod" in schema)) {
11
- throw new Error("Invalid schema provided, make sure you are using zod v4 as zod v3 is currently not supported.");
5
+ export function tableToSurql(table, statement, options) {
6
+ if (statement === "define") {
7
+ return defineTable(table, options);
12
8
  }
13
- const def = schema._zod.def;
14
- const shape = def.shape;
15
- const query = defineTable(options);
16
- for (const [key, value] of Object.entries(shape)) {
17
- query.append(defineField({ name: key, table, type: value, exists: options.exists }));
9
+ if (statement === "info") {
10
+ return infoTable(table);
11
+ }
12
+ if (statement === "structure") {
13
+ return structureTable(table);
14
+ }
15
+ if (statement === "remove") {
16
+ return removeTable(table, options);
18
17
  }
19
- // @ts-expect-error - extend is not a method of z4.$ZodObject
20
- return [schema.extend({ id: z.any() }), query];
18
+ throw new Error(`Invalid statement: ${statement}`);
21
19
  }
22
- function defineTable(options) {
23
- const table = typeof options.table === "string"
24
- ? new Table(options.table)
25
- : options.table;
20
+ export function removeTable(table, options) {
21
+ const name = table._zod.def.name;
22
+ const query = surql `REMOVE TABLE`;
23
+ const removeOptions = options;
24
+ if (removeOptions?.missing === "ignore") {
25
+ query.append(" IF EXISTS");
26
+ }
27
+ query.append(` ${escapeIdent(name)}`);
28
+ query.append(";");
29
+ return query;
30
+ }
31
+ export function infoTable(table) {
32
+ const name = table._zod.def.name;
33
+ const query = surql `INFO FOR TABLE`;
34
+ query.append(` ${escapeIdent(name)}`);
35
+ query.append(";");
36
+ return query;
37
+ }
38
+ export function structureTable(table) {
39
+ const name = table._zod.def.name;
40
+ const query = surql `INFO FOR TABLE`;
41
+ query.append(` ${escapeIdent(name)}`);
42
+ query.append(" STRUCTURE;");
43
+ return query;
44
+ }
45
+ export function defineTable(schema, options) {
46
+ const def = schema._zod.def;
47
+ const surreal = schema._zod.def.surreal;
48
+ const table = new Table(def.name);
26
49
  const query = surql `DEFINE TABLE`;
27
- if (options.exists === "ignore") {
50
+ if (options?.exists === "ignore") {
28
51
  query.append(" IF NOT EXISTS");
29
52
  }
30
- else if (options.exists === "overwrite") {
53
+ else if (options?.exists === "overwrite") {
31
54
  query.append(" OVERWRITE");
32
55
  }
33
56
  // Looks like passing Table instance is not supported yet
34
57
  query.append(` ${escapeIdPart(table.name)}`);
35
- if (options.drop) {
58
+ query.append(` TYPE ${surreal.tableType.toUpperCase()}`);
59
+ if (isRelationTable(schema)) {
60
+ const fromTables = schema._zod.def.fields.in._zod.def.table;
61
+ if (fromTables) {
62
+ query.append(` FROM ${fromTables.map(escapeIdent).join(" | ")}`);
63
+ }
64
+ const toTables = schema._zod.def.fields.out._zod.def.table;
65
+ if (toTables) {
66
+ query.append(` TO ${toTables.map(escapeIdent).join(" | ")}`);
67
+ }
68
+ }
69
+ if (surreal.drop) {
36
70
  query.append(" DROP");
37
71
  }
38
- if (options.schemafull) {
72
+ if (surreal.schemafull) {
39
73
  query.append(" SCHEMAFULL");
40
74
  }
41
75
  else {
42
76
  query.append(" SCHEMALESS");
43
77
  }
44
- if (options.comment) {
45
- query.append(surql ` COMMENT ${options.comment}`);
78
+ if (surreal.comment) {
79
+ query.append(surql ` COMMENT ${surreal.comment}`);
46
80
  }
47
81
  query.append(";\n");
82
+ if (options?.fields) {
83
+ for (const [fieldName, fieldSchema] of Object.entries(def.fields)) {
84
+ query.append(defineField(fieldName, table.name, fieldName === "id"
85
+ ? fieldSchema._zod.def.innerType
86
+ : fieldSchema, {
87
+ exists: options.exists,
88
+ schemafull: surreal.schemafull,
89
+ }));
90
+ }
91
+ }
48
92
  return query;
49
93
  }
50
- function defineField(options) {
51
- const name = options.name;
52
- const table = typeof options.table === "string"
53
- ? new Table(options.table)
54
- : options.table;
55
- const schema = options.type;
56
- if (!("_zod" in schema)) {
57
- throw new Error("Invalid field schema provided, make sure you are using zod v4 as zod v3 is currently not supported.");
58
- }
59
- const context = {
60
- name,
61
- table,
62
- rootSchema: schema,
63
- children: [],
64
- asserts: [],
65
- transforms: [],
66
- };
94
+ export function isNormalTable(table) {
95
+ return table._zod.def.surreal.tableType === "normal";
96
+ }
97
+ export function isRelationTable(table) {
98
+ return table._zod.def.surreal.tableType === "relation";
99
+ }
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
+ // };
67
109
  const query = surql `DEFINE FIELD`;
68
- if (options.exists === "ignore") {
110
+ if (options?.exists === "ignore") {
69
111
  query.append(" IF NOT EXISTS");
70
112
  }
71
- else if (options.exists === "overwrite") {
113
+ else if (options?.exists === "overwrite") {
72
114
  query.append(" OVERWRITE");
73
115
  }
74
- query.append(` ${name} ON TABLE ${table.name}`);
75
- const type = zodTypeToSurrealType(schema, [], context);
76
- query.append(` TYPE ${type}`);
77
- if (context.default) {
78
- query.append(context.default.always
79
- ? ` DEFAULT ALWAYS ${JSON.stringify(context.default.value)}`
80
- : ` DEFAULT ${JSON.stringify(context.default.value)}`);
81
- }
82
- if (context.transforms.length > 0) {
83
- query.append(` VALUE {\n`);
84
- for (const transform of context.transforms) {
85
- query.append(dedent.withOptions({ alignValues: true }) `
86
- //
87
- ${transform}\n`.slice(3));
88
- }
89
- query.append(`}`);
90
- }
91
- if (context.asserts.length > 0) {
92
- query.append(` ASSERT {\n`);
93
- for (const assert of context.asserts) {
94
- query.append(dedent.withOptions({ alignValues: true }) `
95
- //
96
- ${assert}\n`.slice(3));
97
- }
98
- query.append(`}`);
116
+ query.append(` ${name} ON TABLE ${escapeIdent(table)}`);
117
+ const context = {
118
+ type: new Set(),
119
+ depth: 0,
120
+ children: [],
121
+ flexible: false,
122
+ };
123
+ query.append(` TYPE ${inferSurrealType(schema, context)}`);
124
+ if (options?.schemafull && context.flexible) {
125
+ query.append(" FLEXIBLE");
99
126
  }
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}`);
133
+ // const type =
134
+ // name === "id"
135
+ // ? inferSurrealType(
136
+ // (schema as unknown as SurrealZodRecordId)._zod.def.innerType,
137
+ // [],
138
+ // context,
139
+ // )
140
+ // : inferSurrealType(schema, [], context);
141
+ // query.append(` TYPE ${type}`);
142
+ // if (context.default) {
143
+ // query.append(
144
+ // context.default.always
145
+ // ? ` DEFAULT ALWAYS ${JSON.stringify(context.default.value)}`
146
+ // : ` DEFAULT ${JSON.stringify(context.default.value)}`,
147
+ // );
148
+ // }
149
+ // if (context.transforms.length > 0) {
150
+ // query.append(` VALUE {\n`);
151
+ // for (const transform of context.transforms) {
152
+ // query.append(
153
+ // dedent.withOptions({ alignValues: true })`
154
+ // //
155
+ // ${transform}\n`.slice(3),
156
+ // );
157
+ // }
158
+ // query.append(`}`);
159
+ // }
160
+ // if (context.asserts.length > 0) {
161
+ // query.append(` ASSERT {\n`);
162
+ // for (const assert of context.asserts) {
163
+ // query.append(
164
+ // dedent.withOptions({ alignValues: true })`
165
+ // //
166
+ // ${assert}\n`.slice(3),
167
+ // );
168
+ // }
169
+ // query.append(`}`);
170
+ // }
100
171
  query.append(`;\n`);
101
172
  if (context.children.length > 0) {
102
173
  for (const { name: childName, type: childType } of context.children) {
103
- query.append(defineField({
104
- name: `${name}.${childName}`,
105
- table,
106
- type: childType,
107
- exists: options.exists,
174
+ query.append(defineField(`${escapeIdent(name)}.${escapeIdent(childName)}`, table, childType, {
175
+ exists: options?.exists,
108
176
  }));
109
177
  }
110
178
  }
111
179
  return query;
112
180
  }
113
- export function zodTypeToSurrealType(type, parents = [], context) {
181
+ export function inferSurrealType(type, context) {
114
182
  const schema = type;
115
183
  if (!("_zod" in schema)) {
116
184
  throw new Error("Invalid schema provided, make sure you are using zod v4 as zod v3 is currently not supported.");
117
185
  }
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
+ // }
118
194
  const def = schema._zod.def;
119
- const checks = getChecks(schema);
120
- parseChecks(context.name, checks, context, def.type);
195
+ const childIndent = " ".repeat(context.depth + 1);
196
+ // const checks = getChecks(schema);
197
+ // parseChecks(context.name, checks, context, def.type);
121
198
  // console.log(zodToSexpr(type));
122
- switch (def.type) {
123
- case "string":
124
- return "string";
125
- case "boolean":
126
- return "bool";
127
- case "object": {
128
- const isInArray = context.rootSchema._zod.def.type === "array";
129
- // TODO: remove any
130
- for (const [key, value] of Object.entries(def.shape)) {
131
- context.children.push({
132
- name: isInArray ? `*.${key}` : key,
133
- // TODO: remove as
134
- type: value,
135
- });
136
- }
137
- return "object";
138
- }
139
- case "number":
140
- return "number";
141
- case "null":
142
- return "NULL";
143
- // case "bigint":
144
- // return "bigint";
145
- // case "symbol":
146
- // return "symbol";
147
- case "any": {
148
- //===============================
149
- // Surreal Specific Types
150
- //===============================
151
- if ("surrealType" in def) {
152
- if (def.surrealType === "record_id") {
153
- if (def.what) {
154
- return `record<${Object.keys(def.what).join(" | ")}>`;
155
- }
156
- else {
157
- return "record";
158
- }
159
- }
160
- }
161
- return "any";
199
+ switch (def.surreal?.type ?? def.type) {
200
+ case "any":
201
+ case "unknown": {
202
+ context.type.add("any");
203
+ break;
162
204
  }
205
+ case "never":
163
206
  case "undefined": {
164
- return "NONE";
165
- }
166
- case "default": {
167
- // if (typeof def.defaultValue === "function") {
168
- // context.default = { value: def.defaultValue(), always: false };
169
- // } else {
170
- // console.log(
171
- // "default",
172
- // Object.getOwnPropertyDescriptor(def, "defaultValue").get?.toString(),
173
- // );
174
- // TODO: remove any
175
- context.default = { value: def.defaultValue, always: false };
176
- // }
177
- return zodTypeToSurrealType(
178
- // TODO: remove any
179
- def.innerType, [...parents, def.type], context);
180
- }
181
- case "nullable": {
182
- const inner = zodTypeToSurrealType(
183
- // TODO: remove any
184
- def.innerType, [...parents, def.type], context);
185
- if (parents.includes("nullable")) {
186
- return inner;
187
- }
188
- return `${inner} | NULL`;
207
+ context.type.add("none");
208
+ break;
189
209
  }
190
210
  case "optional": {
191
- const inner = zodTypeToSurrealType(
192
- // TODO: remove any
193
- def.innerType, [...parents, def.type], context);
194
- if (parents.includes("optional") || parents.includes("nonoptional")) {
195
- return inner;
196
- }
197
- return `option<${inner}>`;
211
+ inferSurrealType(type._zod.def.innerType, context);
212
+ context.type.add("none");
213
+ break;
198
214
  }
199
215
  case "nonoptional": {
200
- // just a marker for children optional to skip the option<...> wrapper
201
- return zodTypeToSurrealType(
202
- // TODO: remove any
203
- def.innerType, [...parents, def.type], context);
204
- }
205
- case "union": {
206
- // TODO: remove any
207
- return (def.options
208
- // TODO: remove any
209
- .map((option) => zodTypeToSurrealType(option, [...parents, def.type], context))
210
- .join(" | "));
211
- }
212
- case "array": {
213
- const inner = zodTypeToSurrealType(
214
- // TODO: remove any
215
- def.element, [...parents, def.type], context);
216
- return `array<${inner}>`;
217
- }
218
- case "custom": {
219
- return "any";
220
- }
221
- default: {
222
- console.log("unknown type", def.type);
223
- return "any";
216
+ inferSurrealType(type._zod.def.innerType, context);
217
+ if (context.type.size > 1 && context.type.has("none")) {
218
+ context.type.delete("none");
219
+ }
220
+ break;
224
221
  }
225
- }
226
- }
227
- function getChecks(_schema) {
228
- const schema = _schema;
229
- const checks = schema._zod.def.checks ?? [];
230
- if ("check" in schema._zod.def) {
231
- checks.unshift(schema);
232
- }
233
- return checks;
234
- }
235
- function parseChecks(name, checks, context, type) {
236
- for (const check of checks) {
237
- const { transform, assert } = parseCheck(name, check, type);
238
- if (transform) {
239
- context.transforms.push(transform);
222
+ case "null": {
223
+ context.type.add("null");
224
+ break;
240
225
  }
241
- if (assert) {
242
- context.asserts.push(assert);
226
+ case "nullable": {
227
+ inferSurrealType(type._zod.def.innerType, context);
228
+ context.type.add("null");
229
+ break;
243
230
  }
244
- }
245
- }
246
- export const checkMap = {
247
- min_length(name, value, type) {
248
- if (type === "array") {
249
- return `$value.len() >= ${value} || { THROW 'Field "${name}" must have at least ${value} ${value === 1 ? "item" : "items"}' };`;
231
+ case "boolean": {
232
+ context.type.add("bool");
233
+ break;
250
234
  }
251
- if (type === "string") {
252
- return `$value.len() >= ${value} || { THROW 'Field "${name}" must be at least ${value} ${value === 1 ? "character" : "characters"} long' };`;
235
+ case "string": {
236
+ context.type.add("string");
237
+ break;
253
238
  }
254
- throw new Error(`Invalid type: ${type}`);
255
- },
256
- max_length(name, value, type) {
257
- if (type === "array") {
258
- return `$value.len() <= ${value} || { THROW 'Field "${name}" must have at most ${value} ${value === 1 ? "item" : "items"}' };`;
239
+ case "bigint":
240
+ case "number": {
241
+ context.type.add("number");
242
+ break;
259
243
  }
260
- if (type === "string") {
261
- return `$value.len() <= ${value} || { THROW 'Field "${name}" must be at most ${value} ${value === 1 ? "character" : "characters"} long' };`;
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";
255
+ // }
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`;
263
+ // }
264
+ // type += "}";
265
+ // context.type.add(type);
266
+ // break;
267
+ // }
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 });
273
+ }
274
+ break;
262
275
  }
263
- throw new Error(`Invalid type: ${type}`);
264
- },
265
- greater_than(name, value, inclusive) {
266
- return `$value ${inclusive ? ">=" : ">"} ${value} || { THROW 'Field "${name}" must be greater than ${inclusive ? "or equal to" : ""} ${value}' };`;
267
- },
268
- less_than(name, value, inclusive) {
269
- return `$value ${inclusive ? "<=" : "<"} ${value} || { THROW 'Field "${name}" must be less than ${inclusive ? "or equal to" : ""} ${value}' };`;
270
- },
271
- length_equals(name, value, type = "string") {
272
- if (type === "array") {
273
- return `$value.len() == ${value} || { THROW 'Field "${name}" must have exactly ${value} ${value === 1 ? "item" : "items"}' };`;
276
+ case "record_id": {
277
+ const table = def.table;
278
+ if (table) {
279
+ context.type.add(`record<${table.map(escapeIdent).join(" | ")}>`);
280
+ }
281
+ else {
282
+ context.type.add("record");
283
+ }
284
+ break;
274
285
  }
275
- if (type === "string") {
276
- return `$value.len() == ${value} || { THROW 'Field "${name}" must be exactly ${value} ${value === 1 ? "character" : "characters"} long' };`;
286
+ case "table": {
287
+ throw new Error("Table type cannot be used as a field type");
277
288
  }
278
- throw new Error(`Invalid type: ${type}`);
279
- },
280
- string_format: {
281
- email: (name) => {
282
- const regex = /^[A-Za-z0-9'_+-]+(?:\.[A-Za-z0-9'_+-]+)*@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/;
283
- return `string::matches($value, ${regex}) || { THROW "Field '${name}' must be a valid email address" };`;
284
- },
285
- url: (name, def) => {
286
- return dedent `
287
- LET $url = {
288
- scheme: parse::url::scheme($value),
289
- host: parse::url::host($value),
290
- domain: parse::url::domain($value),
291
- path: parse::url::path($value),
292
- port: parse::url::port($value),
293
- query: parse::url::query($value),
294
- hash: parse::url::fragment($value),
295
- };
296
- $url.scheme || { THROW "Field '${name}' must be a valid URL" };
297
- ${def?.hostname
298
- ? `($url.host ?? "").matches(${def.hostname}) || { THROW "Field '${name}' must match hostname ${def.hostname.toString().replace(/\\/g, "\\\\")}" };`
299
- : ""}
300
- ${def?.protocol
301
- ? `($url.scheme ?? "").matches(${def.protocol}) || { THROW "Field '${name}' must match protocol ${def.protocol.toString().replace(/\\/g, "\\\\")}" };`
302
- : ""}
303
- $url.scheme + "://" + ($url.host ?? "") + (
304
- IF $url.port && (
305
- ($url.scheme == "http" && $url.port != 80) ||
306
- ($url.scheme == "https" && $url.port != 443)
307
- ) { ":" + <string>$url.port } ?? ""
308
- )
309
- + ($url.path ?? "")
310
- + (IF $url.query { "?" + $url.query } ?? "")
311
- + (IF $url.fragment { "#" + $url.fragment } ?? "");
312
- `;
313
- },
314
- },
315
- };
316
- function parseCheck(name, _check, type) {
317
- const check = _check;
318
- const def = check._zod.def;
319
- switch (def.check) {
320
- case "min_length":
321
- return { assert: checkMap.min_length(name, def.minimum, type) };
322
- case "max_length":
323
- return { assert: checkMap.max_length(name, def.maximum, type) };
324
- case "greater_than":
325
- return { assert: checkMap.greater_than(name, def.value, def.inclusive) };
326
- case "less_than":
327
- return { assert: checkMap.less_than(name, def.value, def.inclusive) };
328
- case "length_equals":
329
- return { assert: checkMap.length_equals(name, def.length, type) };
330
- case "string_format":
331
- return assertionForStringFormat(name, check);
332
- default:
333
- return { assert: `THROW 'Unknown check: ${def.check}';` };
334
289
  }
335
- }
336
- // Remove look-around, look-behind, and look-ahead as they are not supported by SurrealDB
337
- function assertionForStringFormat(name, _check) {
338
- const check = _check;
339
- const def = check._zod.def;
340
- switch (def.format) {
341
- case "email": {
342
- return { assert: checkMap.string_format.email(name) };
343
- }
344
- case "url": {
345
- const code = checkMap.string_format.url(name, def);
346
- return def.normalize ? { transform: code } : { assert: code };
347
- }
348
- default:
349
- return { assert: `THROW 'Unsupported string format: ${def.format}';` };
290
+ if (context.type.has("any")) {
291
+ return "any";
350
292
  }
293
+ return Array.from(context.type).join(" | ");
351
294
  }
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;
302
+ // }
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
+ // }
318
+ // }
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
+ // }
423
+ // }
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
+ // }
442
+ // }