@cequrebackends/cequre-ts 0.11.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/adapters/bun/bun-cron.d.ts +20 -0
  2. package/dist/adapters/bun/bun-media.d.ts +9 -0
  3. package/dist/adapters/bun/bun-redis-cache.d.ts +33 -0
  4. package/dist/adapters/bun/bun-response.d.ts +13 -0
  5. package/dist/adapters/bun/bun-server.d.ts +6 -0
  6. package/dist/adapters/bun/bun-storage.d.ts +10 -0
  7. package/dist/adapters/bun/bun-websocket.d.ts +104 -0
  8. package/dist/adapters/bun/index.d.ts +9 -0
  9. package/dist/adapters/bun/index.js +547 -0
  10. package/dist/adapters/node/index.d.ts +9 -0
  11. package/dist/adapters/node/index.js +902 -0
  12. package/dist/adapters/node/node-cron.d.ts +20 -0
  13. package/dist/adapters/node/node-media.d.ts +9 -0
  14. package/dist/adapters/node/node-redis-cache.d.ts +32 -0
  15. package/dist/adapters/node/node-response.d.ts +13 -0
  16. package/dist/adapters/node/node-server.d.ts +6 -0
  17. package/dist/adapters/node/node-storage.d.ts +10 -0
  18. package/dist/adapters/node/node-websocket.d.ts +102 -0
  19. package/dist/index-17yswtmg.js +175 -0
  20. package/dist/index-kyvy0s1x.js +2662 -0
  21. package/dist/index-mfqj7cwr.js +165 -0
  22. package/dist/index-rf1kdn5b.js +732 -0
  23. package/dist/index.d.ts +18 -0
  24. package/dist/index.js +3152 -0
  25. package/dist/interface-map.d.ts +175 -0
  26. package/dist/shared/core/adapter.d.ts +84 -0
  27. package/dist/shared/core/base-sql-adapter.d.ts +34 -0
  28. package/dist/shared/core/cache.d.ts +8 -0
  29. package/dist/shared/core/email.d.ts +53 -0
  30. package/dist/shared/core/index.d.ts +12 -0
  31. package/dist/shared/core/index.js +47 -0
  32. package/dist/shared/core/openapi.d.ts +24 -0
  33. package/dist/shared/core/plugins.d.ts +2 -0
  34. package/dist/shared/core/request.d.ts +32 -0
  35. package/dist/shared/core/sdk.d.ts +3 -0
  36. package/dist/shared/core/stream.d.ts +24 -0
  37. package/dist/shared/core/types-generator.d.ts +0 -0
  38. package/dist/shared/core/types.d.ts +304 -0
  39. package/dist/shared/core/ulid.d.ts +5 -0
  40. package/dist/shared/core/validator.d.ts +32 -0
  41. package/dist/shared/main/access-evaluator.d.ts +13 -0
  42. package/dist/shared/main/access.d.ts +34 -0
  43. package/dist/shared/main/aot.d.ts +3 -0
  44. package/dist/shared/main/hooks.d.ts +76 -0
  45. package/dist/shared/main/population.d.ts +13 -0
  46. package/dist/shared/main/router.d.ts +112 -0
  47. package/dist/shared/main/runtime.d.ts +195 -0
  48. package/dist/shared/main/sse.d.ts +87 -0
  49. package/dist/shared/utils/crypto.d.ts +23 -0
  50. package/dist/shared/utils/durable-queue.d.ts +20 -0
  51. package/dist/shared/utils/duration.d.ts +15 -0
  52. package/dist/shared/utils/error.d.ts +52 -0
  53. package/dist/shared/utils/kv/adapters/memory.d.ts +20 -0
  54. package/dist/shared/utils/kv/adapters/sqlite.d.ts +32 -0
  55. package/dist/shared/utils/kv/index.d.ts +37 -0
  56. package/dist/shared/utils/kv/types.d.ts +38 -0
  57. package/dist/shared/utils/logger.d.ts +42 -0
  58. package/dist/shared/utils/queue-manager.d.ts +64 -0
  59. package/dist/shared/utils/url.d.ts +13 -0
  60. package/package.json +63 -0
@@ -0,0 +1,2662 @@
1
+ // @bun
2
+ import {
3
+ CequreError,
4
+ __esm,
5
+ __export,
6
+ __toCommonJS
7
+ } from "./index-17yswtmg.js";
8
+
9
+ // shared/core/openapi.ts
10
+ var exports_openapi = {};
11
+ __export(exports_openapi, {
12
+ generateScalarHTML: () => generateScalarHTML,
13
+ generateOpenAPISpec: () => generateOpenAPISpec
14
+ });
15
+ function generateScalarHTML(specUrl, title = "Cequre API") {
16
+ return `
17
+ <!DOCTYPE html>
18
+ <html>
19
+ <head>
20
+ <title>${title} - API Reference</title>
21
+ <meta charset="utf-8" />
22
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
23
+ <style>
24
+ @import url("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&family=Recursive:wght@300..1000&display=swap");
25
+ body { margin: 0; }
26
+ * { font-family: 'Plus Jakarta Sans', sans-serif !important; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <!-- Mount Scalar UI -->
31
+ <script
32
+ id="api-reference"
33
+ data-url="${specUrl}"
34
+ data-theme="purple"
35
+ data-layout="modern"
36
+ ></script>
37
+ <script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
38
+ </body>
39
+ </html>
40
+ `;
41
+ }
42
+ function fieldToSchema(field) {
43
+ let type = "string";
44
+ let format = undefined;
45
+ let items = undefined;
46
+ let properties = undefined;
47
+ switch (field.type) {
48
+ case "upload":
49
+ type = "object";
50
+ properties = {
51
+ filename: { type: "string" },
52
+ originalName: { type: "string" },
53
+ mimetype: { type: "string" },
54
+ size: { type: "number" },
55
+ url: { type: "string" }
56
+ };
57
+ break;
58
+ case "string":
59
+ case "text":
60
+ type = "string";
61
+ break;
62
+ case "email":
63
+ type = "string";
64
+ format = "email";
65
+ break;
66
+ case "password":
67
+ type = "string";
68
+ format = "password";
69
+ break;
70
+ case "boolean":
71
+ type = "boolean";
72
+ break;
73
+ case "number":
74
+ case "integer":
75
+ type = "number";
76
+ break;
77
+ case "date":
78
+ type = "string";
79
+ format = "date-time";
80
+ break;
81
+ case "object":
82
+ type = "object";
83
+ if (field.fields && field.fields.length > 0) {
84
+ properties = {};
85
+ for (const f of field.fields) {
86
+ properties[f.name] = fieldToSchema(f);
87
+ }
88
+ } else {
89
+ properties = {};
90
+ }
91
+ break;
92
+ case "array":
93
+ type = "array";
94
+ items = { type: "object", additionalProperties: true };
95
+ break;
96
+ case "json":
97
+ case "richtext":
98
+ type = "object";
99
+ properties = {};
100
+ break;
101
+ case "upload":
102
+ type = "object";
103
+ properties = {};
104
+ break;
105
+ case "select":
106
+ type = "string";
107
+ break;
108
+ case "multiselect":
109
+ type = "array";
110
+ items = { type: "string" };
111
+ break;
112
+ }
113
+ let schema = { type };
114
+ if (format)
115
+ schema.format = format;
116
+ if (type === "array" && field.type === "multiselect") {
117
+ if (field.values)
118
+ items.enum = field.values;
119
+ if (field.options)
120
+ items.enum = field.options;
121
+ } else {
122
+ if (field.values)
123
+ schema.enum = field.values;
124
+ if (field.options)
125
+ schema.enum = field.options;
126
+ }
127
+ if (properties)
128
+ schema.properties = properties;
129
+ if (items)
130
+ schema.items = items;
131
+ if (field.isArray) {
132
+ schema = { type: "array", items: schema };
133
+ }
134
+ if (field.default !== undefined)
135
+ schema.default = field.default;
136
+ if (field.optional)
137
+ schema.nullable = true;
138
+ return schema;
139
+ }
140
+ function buildCollectionSchema(collection, isInput = false, isUpdateInput = false, depth = 0, ast, isAuthCollection = false) {
141
+ const properties = {};
142
+ if (!isInput && !isUpdateInput) {
143
+ properties.id = { type: "string", format: "ulid" };
144
+ properties.createdAt = { type: "string", format: "date-time" };
145
+ properties.updatedAt = { type: "string", format: "date-time" };
146
+ }
147
+ const required = [];
148
+ for (const field of collection.fields) {
149
+ if ((field.type === "password" || field.hidden) && !isInput && !isUpdateInput)
150
+ continue;
151
+ if ((field.type === "password" || field.omit) && (isInput || isUpdateInput))
152
+ continue;
153
+ if (isAuthCollection && isUpdateInput && (field.name === "email" || field.name === "password"))
154
+ continue;
155
+ if ((field.type === "link" || field.type === "relation" || field.type === "relationship") && field.target && depth > 0 && ast && !isInput && !isUpdateInput) {
156
+ const targetCol = ast.collections.find((c) => c.slug === field.target);
157
+ if (targetCol) {
158
+ const targetSchema = buildCollectionSchema(targetCol, false, false, depth - 1, ast);
159
+ if (field.type === "link") {
160
+ properties[field.name] = { type: "array", items: targetSchema };
161
+ } else {
162
+ properties[field.name] = targetSchema;
163
+ }
164
+ } else {
165
+ properties[field.name] = fieldToSchema(field);
166
+ }
167
+ } else {
168
+ properties[field.name] = fieldToSchema(field);
169
+ }
170
+ if (field.type === "upload" && (isInput || isUpdateInput)) {
171
+ properties[field.name] = {
172
+ oneOf: [
173
+ properties[field.name],
174
+ { type: "string", format: "binary" }
175
+ ]
176
+ };
177
+ }
178
+ if (!field.optional && !isUpdateInput) {
179
+ required.push(field.name);
180
+ }
181
+ }
182
+ if (!isInput && !isUpdateInput) {
183
+ required.push("id", "createdAt", "updatedAt");
184
+ }
185
+ return {
186
+ type: "object",
187
+ properties,
188
+ ...required.length > 0 ? { required } : {}
189
+ };
190
+ }
191
+ function buildWhereSchema(collection, schema, schemaRefName, depth = 0) {
192
+ const properties = {
193
+ id: {
194
+ oneOf: [
195
+ { type: "string", format: "ulid" },
196
+ { type: "object", additionalProperties: true }
197
+ ]
198
+ },
199
+ createdAt: {
200
+ oneOf: [
201
+ { type: "string", format: "date-time" },
202
+ { type: "object", additionalProperties: true }
203
+ ]
204
+ },
205
+ updatedAt: {
206
+ oneOf: [
207
+ { type: "string", format: "date-time" },
208
+ { type: "object", additionalProperties: true }
209
+ ]
210
+ }
211
+ };
212
+ for (const field of collection.fields) {
213
+ if (field.type === "password")
214
+ continue;
215
+ if ((field.type === "link" || field.type === "relation") && field.target) {
216
+ let fieldSchema;
217
+ if (depth === 0) {
218
+ const targetCol = schema.collections.find((c) => c.slug === field.target);
219
+ if (targetCol) {
220
+ const targetSchemaRefName = targetCol.slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
221
+ fieldSchema = {
222
+ oneOf: [
223
+ { type: "string" },
224
+ { type: "object", additionalProperties: true },
225
+ buildWhereSchema(targetCol, schema, targetSchemaRefName, depth + 1)
226
+ ]
227
+ };
228
+ } else {
229
+ fieldSchema = {
230
+ oneOf: [{ type: "string" }, { type: "object", additionalProperties: true }]
231
+ };
232
+ }
233
+ } else {
234
+ fieldSchema = {
235
+ oneOf: [{ type: "string" }, { type: "object", additionalProperties: true }]
236
+ };
237
+ }
238
+ if (field.type === "link") {
239
+ properties[field.name] = { type: "array", items: fieldSchema };
240
+ } else {
241
+ properties[field.name] = fieldSchema;
242
+ }
243
+ } else {
244
+ properties[field.name] = {
245
+ oneOf: [
246
+ fieldToSchema(field),
247
+ { type: "object", additionalProperties: true }
248
+ ]
249
+ };
250
+ }
251
+ }
252
+ return {
253
+ type: "object",
254
+ properties: {
255
+ ...properties,
256
+ AND: { type: "array", items: { $ref: `#/components/schemas/${schemaRefName}WhereInput` } },
257
+ OR: { type: "array", items: { $ref: `#/components/schemas/${schemaRefName}WhereInput` } }
258
+ }
259
+ };
260
+ }
261
+ function typeboxToOpenAPISchema(schema) {
262
+ if (!schema || typeof schema !== "object")
263
+ return {};
264
+ const s = schema;
265
+ const kind = s[_TB_KIND];
266
+ switch (kind) {
267
+ case "String":
268
+ case "RegExp":
269
+ case "TemplateLiteral":
270
+ return { type: "string" };
271
+ case "Number":
272
+ return { type: "number" };
273
+ case "Integer":
274
+ return { type: "integer" };
275
+ case "Boolean":
276
+ return { type: "boolean" };
277
+ case "Null":
278
+ return { type: "null" };
279
+ case "Any":
280
+ case "Unknown":
281
+ return {};
282
+ case "Void":
283
+ return {};
284
+ case "Date":
285
+ return { type: "string", format: "date-time" };
286
+ case "File":
287
+ case "Blob":
288
+ return { type: "string", format: "binary" };
289
+ case "Literal": {
290
+ const val = s["const"];
291
+ const t = typeof val;
292
+ return t === "string" ? { type: "string", enum: [val] } : t === "number" ? { type: "number", enum: [val] } : { type: "boolean", enum: [val] };
293
+ }
294
+ case "Array":
295
+ return { type: "array", items: typeboxToOpenAPISchema(s["items"]) };
296
+ case "Tuple": {
297
+ const items = s["items"] ?? [];
298
+ return { type: "array", prefixItems: items.map(typeboxToOpenAPISchema), maxItems: items.length };
299
+ }
300
+ case "Union": {
301
+ const variants = s["anyOf"] ?? [];
302
+ return { oneOf: variants.map(typeboxToOpenAPISchema) };
303
+ }
304
+ case "Intersect": {
305
+ const variants = s["allOf"] ?? [];
306
+ return { allOf: variants.map(typeboxToOpenAPISchema) };
307
+ }
308
+ case "Object": {
309
+ const properties = s["properties"] ?? {};
310
+ const required = s["required"] ?? [];
311
+ const props = {};
312
+ for (const [key, val] of Object.entries(properties)) {
313
+ props[key] = typeboxToOpenAPISchema(val);
314
+ }
315
+ return {
316
+ type: "object",
317
+ properties: props,
318
+ ...required.length > 0 ? { required } : {}
319
+ };
320
+ }
321
+ default: {
322
+ if (s[_TB_MODIFIER] === "Optional") {
323
+ const { [_TB_MODIFIER]: _m, ...rest } = s;
324
+ return typeboxToOpenAPISchema(rest);
325
+ }
326
+ try {
327
+ const clean = JSON.parse(JSON.stringify(schema));
328
+ delete clean["$schema"];
329
+ return clean;
330
+ } catch {
331
+ return {};
332
+ }
333
+ }
334
+ }
335
+ }
336
+ function schemaHasFileField(schema) {
337
+ if (!schema || typeof schema !== "object")
338
+ return false;
339
+ const s = schema;
340
+ const kind = s[_TB_KIND];
341
+ if (kind === "File" || kind === "Blob")
342
+ return true;
343
+ if (kind === "Object") {
344
+ const props = s["properties"] ?? {};
345
+ return Object.values(props).some(schemaHasFileField);
346
+ }
347
+ return false;
348
+ }
349
+ function buildRequestBody(bodySchema) {
350
+ const oaSchema = typeboxToOpenAPISchema(bodySchema);
351
+ const contentType = schemaHasFileField(bodySchema) ? "multipart/form-data" : "application/json";
352
+ return {
353
+ required: true,
354
+ content: { [contentType]: { schema: oaSchema } }
355
+ };
356
+ }
357
+ function buildQueryParameters(querySchema) {
358
+ if (!querySchema || typeof querySchema !== "object")
359
+ return [];
360
+ const s = querySchema;
361
+ if (s[_TB_KIND] !== "Object")
362
+ return [];
363
+ const properties = s["properties"] ?? {};
364
+ const required = new Set(s["required"] ?? []);
365
+ return Object.entries(properties).map(([name, val]) => ({
366
+ name,
367
+ in: "query",
368
+ required: required.has(name),
369
+ schema: typeboxToOpenAPISchema(val)
370
+ }));
371
+ }
372
+ function buildResponsesFromSchema(responseSchema) {
373
+ if (!responseSchema || typeof responseSchema !== "object") {
374
+ return { "200": { description: "OK" } };
375
+ }
376
+ const s = responseSchema;
377
+ const kind = s[_TB_KIND];
378
+ if (kind) {
379
+ const oaSchema = typeboxToOpenAPISchema(responseSchema);
380
+ const isEmpty = Object.keys(oaSchema).length === 0;
381
+ return {
382
+ "200": {
383
+ description: "OK",
384
+ ...!isEmpty ? { content: { "application/json": { schema: oaSchema } } } : {}
385
+ }
386
+ };
387
+ }
388
+ const responses = {};
389
+ for (const [statusCode, val] of Object.entries(s)) {
390
+ if (!/^\d{3}$/.test(statusCode))
391
+ continue;
392
+ const valSchema = typeboxToOpenAPISchema(val);
393
+ const isEmpty = Object.keys(valSchema).length === 0;
394
+ responses[statusCode] = {
395
+ description: httpStatusDescription(Number(statusCode)),
396
+ ...!isEmpty ? { content: { "application/json": { schema: valSchema } } } : {}
397
+ };
398
+ }
399
+ return Object.keys(responses).length > 0 ? responses : { "200": { description: "OK" } };
400
+ }
401
+ function httpStatusDescription(code) {
402
+ const map = {
403
+ 200: "OK",
404
+ 201: "Created",
405
+ 204: "No Content",
406
+ 400: "Bad Request",
407
+ 401: "Unauthorized",
408
+ 403: "Forbidden",
409
+ 404: "Not Found",
410
+ 409: "Conflict",
411
+ 422: "Unprocessable Entity",
412
+ 429: "Too Many Requests",
413
+ 500: "Internal Server Error"
414
+ };
415
+ return map[code] ?? "Response";
416
+ }
417
+ function generateOpenAPISpec(schema, serverUrl, prefix, app, includeHidden = false) {
418
+ const paths = {};
419
+ const components = { schemas: {} };
420
+ for (const collection of schema.collections) {
421
+ if (collection.hidden && !includeHidden)
422
+ continue;
423
+ const slug = collection.slug;
424
+ const tag = slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
425
+ const schemaRefName = tag;
426
+ const isAuthCol = !!collection.auth;
427
+ components.schemas[schemaRefName] = buildCollectionSchema(collection, false, false, 0, schema, isAuthCol);
428
+ components.schemas[`${schemaRefName}CreateInput`] = buildCollectionSchema(collection, true, false, 0, schema, isAuthCol);
429
+ components.schemas[`${schemaRefName}UpdateInput`] = buildCollectionSchema(collection, false, true, 0, schema, isAuthCol);
430
+ components.schemas[`${schemaRefName}Populated`] = buildCollectionSchema(collection, false, false, 1, schema, isAuthCol);
431
+ components.schemas[`${schemaRefName}PopulatedDepth2`] = buildCollectionSchema(collection, false, false, 2, schema, isAuthCol);
432
+ components.schemas[`${schemaRefName}WhereInput`] = buildWhereSchema(collection, schema, schemaRefName);
433
+ components.schemas[`${schemaRefName}QueryInput`] = {
434
+ type: "object",
435
+ properties: {
436
+ where: { $ref: `#/components/schemas/${schemaRefName}WhereInput` },
437
+ sort: { type: "string" },
438
+ limit: { type: "number" },
439
+ page: { type: "number" },
440
+ depth: { type: "number" }
441
+ }
442
+ };
443
+ components.schemas[`Paginated${schemaRefName}`] = {
444
+ type: "object",
445
+ properties: {
446
+ docs: { type: "array", items: { $ref: `#/components/schemas/${schemaRefName}` } },
447
+ totalDocs: { type: "number" },
448
+ limit: { type: "number" },
449
+ page: { type: "number" },
450
+ totalPages: { type: "number" },
451
+ hasNextPage: { type: "boolean" },
452
+ hasPrevPage: { type: "boolean" }
453
+ },
454
+ required: ["docs", "totalDocs", "limit", "page", "totalPages", "hasNextPage", "hasPrevPage"]
455
+ };
456
+ components.schemas[`Paginated${schemaRefName}Populated`] = {
457
+ type: "object",
458
+ properties: {
459
+ docs: { type: "array", items: { $ref: `#/components/schemas/${schemaRefName}Populated` } },
460
+ totalDocs: { type: "number" },
461
+ limit: { type: "number" },
462
+ page: { type: "number" },
463
+ totalPages: { type: "number" },
464
+ hasNextPage: { type: "boolean" },
465
+ hasPrevPage: { type: "boolean" }
466
+ },
467
+ required: ["docs", "totalDocs", "limit", "page", "totalPages", "hasNextPage", "hasPrevPage"]
468
+ };
469
+ components.schemas[`Paginated${schemaRefName}PopulatedDepth2`] = {
470
+ type: "object",
471
+ properties: {
472
+ docs: { type: "array", items: { $ref: `#/components/schemas/${schemaRefName}PopulatedDepth2` } },
473
+ totalDocs: { type: "number" },
474
+ limit: { type: "number" },
475
+ page: { type: "number" },
476
+ totalPages: { type: "number" },
477
+ hasNextPage: { type: "boolean" },
478
+ hasPrevPage: { type: "boolean" }
479
+ },
480
+ required: ["docs", "totalDocs", "limit", "page", "totalPages", "hasNextPage", "hasPrevPage"]
481
+ };
482
+ const basePath = `${prefix}/${slug}`;
483
+ const itemPath = `${basePath}/{id}`;
484
+ paths[basePath] = {
485
+ get: {
486
+ tags: [tag],
487
+ summary: `List ${slug}`,
488
+ parameters: [{ name: "q", in: "query", description: "JSON stringified QueryArgs object", schema: { $ref: `#/components/schemas/${schemaRefName}QueryInput` } }],
489
+ responses: {
490
+ "200": {
491
+ description: "List of documents",
492
+ content: {
493
+ "application/json": {
494
+ schema: { $ref: `#/components/schemas/Paginated${schemaRefName}` }
495
+ }
496
+ }
497
+ }
498
+ }
499
+ },
500
+ post: {
501
+ tags: [tag],
502
+ summary: `Create ${slug}`,
503
+ requestBody: { content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}CreateInput` } } } },
504
+ responses: {
505
+ "201": {
506
+ description: "Document created",
507
+ content: {
508
+ "application/json": {
509
+ schema: { $ref: `#/components/schemas/${schemaRefName}` }
510
+ }
511
+ }
512
+ }
513
+ }
514
+ }
515
+ };
516
+ paths[itemPath] = {
517
+ get: {
518
+ tags: [tag],
519
+ summary: `Get ${slug} by ID`,
520
+ parameters: [
521
+ { name: "id", in: "path", required: true, schema: { type: "string", format: "ulid" } },
522
+ { name: "q", in: "query", description: "JSON stringified QueryArgs object", schema: { type: "object", properties: { depth: { type: "number" } } } }
523
+ ],
524
+ responses: {
525
+ "200": {
526
+ description: "Document found",
527
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } } }
528
+ }
529
+ }
530
+ },
531
+ patch: {
532
+ tags: [tag],
533
+ summary: `Update ${slug}`,
534
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string", format: "ulid" } }],
535
+ requestBody: { content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}UpdateInput` } } } },
536
+ responses: {
537
+ "200": {
538
+ description: "Document updated",
539
+ content: {
540
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } }
541
+ }
542
+ }
543
+ }
544
+ },
545
+ delete: {
546
+ tags: [tag],
547
+ summary: `Delete ${slug}`,
548
+ parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }],
549
+ responses: {
550
+ "200": { description: "Document deleted" }
551
+ }
552
+ }
553
+ };
554
+ if (collection.auth) {
555
+ const registerRequired = ["email", "password"];
556
+ const registerProperties = {
557
+ email: { type: "string", format: "email" },
558
+ password: { type: "string", format: "password" }
559
+ };
560
+ for (const field of collection.fields) {
561
+ if (field.name !== "email" && field.name !== "password") {
562
+ if (field.omit)
563
+ continue;
564
+ registerProperties[field.name] = fieldToSchema(field);
565
+ if (!field.optional)
566
+ registerRequired.push(field.name);
567
+ }
568
+ }
569
+ const authSuccessSchemaRefName = `AuthResponse`;
570
+ components.schemas[authSuccessSchemaRefName] = {
571
+ type: "object",
572
+ properties: {
573
+ user: { $ref: `#/components/schemas/${schemaRefName}` },
574
+ accessToken: { type: "string" },
575
+ refreshToken: { type: "string" },
576
+ message: { type: "string" }
577
+ }
578
+ };
579
+ const registerSchemaRefName = `AuthRegisterInput`;
580
+ components.schemas[registerSchemaRefName] = {
581
+ type: "object",
582
+ required: registerRequired,
583
+ properties: registerProperties
584
+ };
585
+ const loginSchemaRefName = `AuthLoginInput`;
586
+ if (!components.schemas[loginSchemaRefName]) {
587
+ components.schemas[loginSchemaRefName] = {
588
+ type: "object",
589
+ required: ["email", "password"],
590
+ properties: {
591
+ email: { type: "string", format: "email" },
592
+ password: { type: "string", format: "password" }
593
+ }
594
+ };
595
+ }
596
+ const refreshSchemaRefName = `AuthRefreshInput`;
597
+ if (!components.schemas[refreshSchemaRefName]) {
598
+ components.schemas[refreshSchemaRefName] = {
599
+ type: "object",
600
+ required: ["refreshToken"],
601
+ properties: { refreshToken: { type: "string" } }
602
+ };
603
+ }
604
+ const forgotSchemaRefName = `AuthForgotPasswordInput`;
605
+ if (!components.schemas[forgotSchemaRefName]) {
606
+ components.schemas[forgotSchemaRefName] = {
607
+ type: "object",
608
+ required: ["email"],
609
+ properties: { email: { type: "string", format: "email" } }
610
+ };
611
+ }
612
+ const resetSchemaRefName = `AuthResetPasswordInput`;
613
+ if (!components.schemas[resetSchemaRefName]) {
614
+ components.schemas[resetSchemaRefName] = {
615
+ type: "object",
616
+ required: ["token", "newPassword"],
617
+ properties: {
618
+ token: { type: "string" },
619
+ newPassword: { type: "string", format: "password" }
620
+ }
621
+ };
622
+ }
623
+ paths[`${basePath}/register`] = {
624
+ post: {
625
+ tags: ["Auth"],
626
+ summary: "Register",
627
+ requestBody: {
628
+ required: true,
629
+ content: {
630
+ "application/json": {
631
+ schema: { $ref: `#/components/schemas/${registerSchemaRefName}` }
632
+ }
633
+ }
634
+ },
635
+ responses: {
636
+ "201": {
637
+ description: "Registration successful",
638
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${authSuccessSchemaRefName}` } } }
639
+ }
640
+ }
641
+ }
642
+ };
643
+ paths[`${basePath}/login`] = {
644
+ post: {
645
+ tags: ["Auth"],
646
+ summary: "Login",
647
+ requestBody: {
648
+ required: true,
649
+ content: {
650
+ "application/json": {
651
+ schema: { $ref: `#/components/schemas/${loginSchemaRefName}` }
652
+ }
653
+ }
654
+ },
655
+ responses: {
656
+ "200": {
657
+ description: "Login successful",
658
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${authSuccessSchemaRefName}` } } }
659
+ }
660
+ }
661
+ }
662
+ };
663
+ paths[`${basePath}/me`] = {
664
+ get: {
665
+ tags: ["Auth"],
666
+ summary: "Get Current User",
667
+ responses: {
668
+ "200": {
669
+ description: "Current user data",
670
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } } }
671
+ }
672
+ }
673
+ }
674
+ };
675
+ paths[`${basePath}/refresh`] = {
676
+ post: {
677
+ tags: ["Auth"],
678
+ summary: "Refresh token",
679
+ requestBody: {
680
+ required: true,
681
+ content: {
682
+ "application/json": {
683
+ schema: { $ref: `#/components/schemas/${refreshSchemaRefName}` }
684
+ }
685
+ }
686
+ },
687
+ responses: {
688
+ "200": {
689
+ description: "Tokens refreshed",
690
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${authSuccessSchemaRefName}` } } }
691
+ }
692
+ }
693
+ }
694
+ };
695
+ paths[`${basePath}/logout`] = {
696
+ post: {
697
+ tags: ["Auth"],
698
+ summary: "Logout",
699
+ responses: {
700
+ "200": { description: "Logged out successfully" }
701
+ }
702
+ }
703
+ };
704
+ paths[`${basePath}/forgot-password`] = {
705
+ post: {
706
+ tags: ["Auth"],
707
+ summary: "Forgot Password",
708
+ requestBody: {
709
+ required: true,
710
+ content: {
711
+ "application/json": {
712
+ schema: { $ref: `#/components/schemas/${forgotSchemaRefName}` }
713
+ }
714
+ }
715
+ },
716
+ responses: {
717
+ "200": {
718
+ description: "Password reset email sent (if account exists)"
719
+ }
720
+ }
721
+ }
722
+ };
723
+ paths[`${basePath}/reset-password`] = {
724
+ post: {
725
+ tags: ["Auth"],
726
+ summary: "Reset Password",
727
+ requestBody: {
728
+ required: true,
729
+ content: {
730
+ "application/json": {
731
+ schema: { $ref: `#/components/schemas/${resetSchemaRefName}` }
732
+ }
733
+ }
734
+ },
735
+ responses: {
736
+ "200": {
737
+ description: "Password reset successfully"
738
+ },
739
+ "400": {
740
+ description: "Invalid or expired token"
741
+ }
742
+ }
743
+ }
744
+ };
745
+ const mfaVerifySchemaRefName = `AuthMfaVerifyInput`;
746
+ if (!components.schemas[mfaVerifySchemaRefName]) {
747
+ components.schemas[mfaVerifySchemaRefName] = {
748
+ type: "object",
749
+ required: ["mfaCode"],
750
+ properties: { mfaCode: { type: "string" } }
751
+ };
752
+ }
753
+ const mfaLoginSchemaRefName = `AuthMfaLoginInput`;
754
+ if (!components.schemas[mfaLoginSchemaRefName]) {
755
+ components.schemas[mfaLoginSchemaRefName] = {
756
+ type: "object",
757
+ required: ["mfaToken", "mfaCode"],
758
+ properties: {
759
+ mfaToken: { type: "string" },
760
+ mfaCode: { type: "string" }
761
+ }
762
+ };
763
+ }
764
+ paths[`${basePath}/mfa/enroll`] = {
765
+ post: {
766
+ tags: ["Auth"],
767
+ summary: "Enroll MFA",
768
+ responses: {
769
+ "200": {
770
+ description: "MFA enrollment started",
771
+ content: { "application/json": { schema: { type: "object", properties: { secret: { type: "string" }, uri: { type: "string" }, backupCodes: { type: "array", items: { type: "string" } } } } } }
772
+ }
773
+ }
774
+ }
775
+ };
776
+ paths[`${basePath}/mfa/verify`] = {
777
+ post: {
778
+ tags: ["Auth"],
779
+ summary: "Verify MFA Setup",
780
+ requestBody: {
781
+ required: true,
782
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${mfaVerifySchemaRefName}` } } }
783
+ },
784
+ responses: { "200": { description: "MFA verified successfully" } }
785
+ }
786
+ };
787
+ paths[`${basePath}/mfa/login`] = {
788
+ post: {
789
+ tags: ["Auth"],
790
+ summary: "Complete MFA Login",
791
+ requestBody: {
792
+ required: true,
793
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${mfaLoginSchemaRefName}` } } }
794
+ },
795
+ responses: {
796
+ "200": {
797
+ description: "Login successful",
798
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${authSuccessSchemaRefName}` } } }
799
+ }
800
+ }
801
+ }
802
+ };
803
+ paths[`${basePath}/mfa/disable`] = {
804
+ post: {
805
+ tags: ["Auth"],
806
+ summary: "Disable MFA",
807
+ requestBody: {
808
+ required: true,
809
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${mfaVerifySchemaRefName}` } } }
810
+ },
811
+ responses: { "200": { description: "MFA disabled successfully" } }
812
+ }
813
+ };
814
+ }
815
+ }
816
+ if (schema.globals) {
817
+ for (const glob of schema.globals) {
818
+ const slug = glob.slug;
819
+ const tag = "Globals";
820
+ const schemaRefName = slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("") + "Global";
821
+ components.schemas[schemaRefName] = buildCollectionSchema(glob);
822
+ components.schemas[`${schemaRefName}CreateInput`] = buildCollectionSchema(glob, true, false);
823
+ components.schemas[`${schemaRefName}UpdateInput`] = buildCollectionSchema(glob, false, true);
824
+ const basePath = `${prefix}/globals/${slug}`;
825
+ paths[basePath] = {
826
+ get: {
827
+ tags: [tag],
828
+ summary: `Get global configuration: ${slug}`,
829
+ responses: {
830
+ "200": {
831
+ description: "Global document",
832
+ content: {
833
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } }
834
+ }
835
+ }
836
+ }
837
+ },
838
+ post: {
839
+ tags: [tag],
840
+ summary: `Create or Update global configuration: ${slug}`,
841
+ requestBody: {
842
+ required: true,
843
+ content: {
844
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}CreateInput` } }
845
+ }
846
+ },
847
+ responses: {
848
+ "200": {
849
+ description: "Global document updated",
850
+ content: {
851
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } }
852
+ }
853
+ }
854
+ }
855
+ },
856
+ patch: {
857
+ tags: [tag],
858
+ summary: `Patch global configuration: ${slug}`,
859
+ requestBody: {
860
+ required: true,
861
+ content: {
862
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}UpdateInput` } }
863
+ }
864
+ },
865
+ responses: {
866
+ "200": {
867
+ description: "Global document updated",
868
+ content: {
869
+ "application/json": { schema: { $ref: `#/components/schemas/${schemaRefName}` } }
870
+ }
871
+ }
872
+ }
873
+ }
874
+ };
875
+ }
876
+ }
877
+ if (app?.routes) {
878
+ for (const route of app.routes) {
879
+ const method = route.method?.toLowerCase();
880
+ const rawPath = route.path;
881
+ if (!method || !rawPath)
882
+ continue;
883
+ const routePath = rawPath.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, "{$1}");
884
+ const options = route.options || route.hooks || {};
885
+ const detail = options.detail;
886
+ if (detail?.hide)
887
+ continue;
888
+ const routeTags = detail?.tags ?? options.tags ?? [];
889
+ if (method === "options" || paths[routePath]?.[method])
890
+ continue;
891
+ const isInternalPath = rawPath.startsWith(`${prefix}/docs`);
892
+ if (isInternalPath)
893
+ continue;
894
+ const isHiddenCollectionPath = schema.collections.some((c) => c.hidden && rawPath.startsWith(`${prefix}/${c.slug}`));
895
+ if (isHiddenCollectionPath)
896
+ continue;
897
+ const operationTags = routeTags.length ? routeTags : ["Custom"];
898
+ const operation = {
899
+ ...detail ?? {},
900
+ tags: operationTags
901
+ };
902
+ const bodySchema = options.body;
903
+ const querySchema = options.query;
904
+ const responseSchema = options.response;
905
+ if (bodySchema && !operation.requestBody && ["post", "put", "patch"].includes(method)) {
906
+ operation.requestBody = buildRequestBody(bodySchema);
907
+ }
908
+ if (querySchema) {
909
+ const generatedParams = buildQueryParameters(querySchema);
910
+ if (generatedParams.length > 0) {
911
+ operation.parameters = [
912
+ ...generatedParams,
913
+ ...operation.parameters ?? []
914
+ ];
915
+ }
916
+ }
917
+ if (responseSchema && !operation.responses) {
918
+ operation.responses = buildResponsesFromSchema(responseSchema);
919
+ } else if (!operation.responses) {
920
+ operation.responses = { "200": { description: "OK" } };
921
+ }
922
+ if (route.options?.stream || route.hooks?.stream) {
923
+ operation.responses = operation.responses || {};
924
+ operation.responses["200"] = operation.responses["200"] || { description: "OK" };
925
+ operation.responses["200"].content = operation.responses["200"].content || {};
926
+ operation.responses["200"].content["text/event-stream"] = { schema: { type: "string" } };
927
+ }
928
+ paths[routePath] ??= {};
929
+ paths[routePath][method] = operation;
930
+ }
931
+ }
932
+ return {
933
+ openapi: "3.0.3",
934
+ info: { title: "Cequre API", version: "1.0.0" },
935
+ servers: [{ url: serverUrl }],
936
+ paths,
937
+ components
938
+ };
939
+ }
940
+ var _TB_KIND, _TB_MODIFIER;
941
+ var init_openapi = __esm(() => {
942
+ _TB_KIND = Symbol.for("TypeBox.Kind");
943
+ _TB_MODIFIER = Symbol.for("TypeBox.Modifier");
944
+ });
945
+
946
+ // shared/core/sdk.ts
947
+ var exports_sdk = {};
948
+ __export(exports_sdk, {
949
+ generateTypeScriptClientSDK: () => generateTypeScriptClientSDK,
950
+ generateSharedModels: () => generateSharedModels,
951
+ generateBackendSchemas: () => generateBackendSchemas
952
+ });
953
+ function generateSharedModels(schema, routes) {
954
+ const openApiSpec = generateOpenAPISpec(schema, "http://localhost:3000", "/api", { routes }, true);
955
+ const models = [];
956
+ const schemas = openApiSpec.components?.schemas || {};
957
+ function resolveType(s) {
958
+ if (!s)
959
+ return "any";
960
+ if (s.$ref) {
961
+ return s.$ref.split("/").pop();
962
+ }
963
+ switch (s.type) {
964
+ case "string":
965
+ if (s.format === "binary")
966
+ return "string | File | Blob";
967
+ if (s.format === "date-time")
968
+ return "string | Date";
969
+ return s.enum ? s.enum.map((e) => `"${e}"`).join(" | ") : "string";
970
+ case "number":
971
+ case "integer":
972
+ return s.enum ? s.enum.join(" | ") : "number";
973
+ case "boolean":
974
+ return s.enum ? s.enum.join(" | ") : "boolean";
975
+ case "array":
976
+ return `Array<${resolveType(s.items)}>`;
977
+ case "object":
978
+ if (s.properties) {
979
+ const props = Object.entries(s.properties).map(([key, val]) => {
980
+ const isRequired = s.required?.includes(key);
981
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
982
+ return ` ${safeKey}${isRequired ? "" : "?"}: ${resolveType(val)};`;
983
+ }).join(`
984
+ `);
985
+ return `{
986
+ ${props}
987
+ }`;
988
+ }
989
+ return "Record<string, any>";
990
+ default:
991
+ if (s.enum) {
992
+ return s.enum.map((e) => `"${e}"`).join(" | ");
993
+ }
994
+ if (s.anyOf) {
995
+ return s.anyOf.map((s2) => resolveType(s2)).join(" | ");
996
+ }
997
+ if (s.allOf) {
998
+ return s.allOf.map((s2) => resolveType(s2)).join(" & ");
999
+ }
1000
+ if (s.oneOf) {
1001
+ return s.oneOf.map((s2) => resolveType(s2)).join(" | ");
1002
+ }
1003
+ return "any";
1004
+ }
1005
+ }
1006
+ for (const [schemaName, schemaDef] of Object.entries(schemas)) {
1007
+ const resolved = resolveType(schemaDef);
1008
+ if (resolved.startsWith(`{
1009
+ `)) {
1010
+ models.push(`export interface ${schemaName} ${resolved}`);
1011
+ } else {
1012
+ models.push(`export type ${schemaName} = ${resolved};`);
1013
+ }
1014
+ }
1015
+ return models.join(`
1016
+
1017
+ `) + `
1018
+
1019
+ `;
1020
+ }
1021
+ function generateTypeScriptClientSDK(schema, routes) {
1022
+ const openApiSpec = generateOpenAPISpec(schema, "http://localhost:3000", "/api", { routes });
1023
+ const modelsString = generateSharedModels(schema, routes);
1024
+ const models = [modelsString.trim()];
1025
+ function resolveType(s) {
1026
+ if (!s)
1027
+ return "any";
1028
+ if (s.$ref) {
1029
+ return s.$ref.split("/").pop();
1030
+ }
1031
+ switch (s.type) {
1032
+ case "string":
1033
+ if (s.format === "binary")
1034
+ return "string | File | Blob";
1035
+ return s.enum ? s.enum.map((e) => `"${e}"`).join(" | ") : "string";
1036
+ case "number":
1037
+ case "integer":
1038
+ return s.enum ? s.enum.join(" | ") : "number";
1039
+ case "boolean":
1040
+ return s.enum ? s.enum.join(" | ") : "boolean";
1041
+ case "array":
1042
+ return `Array<${resolveType(s.items)}>`;
1043
+ case "object":
1044
+ if (s.properties) {
1045
+ const props = Object.entries(s.properties).map(([key, val]) => {
1046
+ const isRequired = s.required?.includes(key);
1047
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
1048
+ return ` ${safeKey}${isRequired ? "" : "?"}: ${resolveType(val)};`;
1049
+ }).join(`
1050
+ `);
1051
+ return `{
1052
+ ${props}
1053
+ }`;
1054
+ }
1055
+ return "Record<string, any>";
1056
+ default:
1057
+ if (s.enum) {
1058
+ return s.enum.map((e) => `"${e}"`).join(" | ");
1059
+ }
1060
+ if (s.anyOf) {
1061
+ return s.anyOf.map((s2) => resolveType(s2)).join(" | ");
1062
+ }
1063
+ if (s.allOf) {
1064
+ return s.allOf.map((s2) => resolveType(s2)).join(" & ");
1065
+ }
1066
+ if (s.oneOf) {
1067
+ return s.oneOf.map((s2) => resolveType(s2)).join(" | ");
1068
+ }
1069
+ return "any";
1070
+ }
1071
+ }
1072
+ function getTypeForPath(method, path) {
1073
+ const op = openApiSpec.paths?.[path]?.[method];
1074
+ if (!op)
1075
+ return "any";
1076
+ const successRes = op.responses?.["200"] || op.responses?.["201"];
1077
+ if (successRes?.content?.["application/json"]?.schema) {
1078
+ return resolveType(successRes.content["application/json"].schema);
1079
+ } else if (successRes && !successRes.content) {
1080
+ return "void";
1081
+ }
1082
+ return "any";
1083
+ }
1084
+ function getReqForPath(method, path) {
1085
+ const op = openApiSpec.paths?.[path]?.[method];
1086
+ if (!op)
1087
+ return "any";
1088
+ if (op.requestBody?.content?.["application/json"]?.schema) {
1089
+ return resolveType(op.requestBody.content["application/json"].schema);
1090
+ } else if (op.requestBody?.content?.["multipart/form-data"]?.schema) {
1091
+ return "FormData | any";
1092
+ }
1093
+ return "any";
1094
+ }
1095
+ function getQueryTypeForPath(path) {
1096
+ const op = openApiSpec.paths?.[path]?.get;
1097
+ if (!op || !op.parameters)
1098
+ return "any";
1099
+ const qParam = op.parameters.find((p) => p.name === "q" && p.in === "query");
1100
+ if (qParam?.schema) {
1101
+ return resolveType(qParam.schema);
1102
+ }
1103
+ return "any";
1104
+ }
1105
+ const root = { methods: [], children: {} };
1106
+ let hasRoutes = false;
1107
+ const prefix = schema.core?.api?.prefix || "/api";
1108
+ for (const col of schema.collections || []) {
1109
+ if (col.hidden)
1110
+ continue;
1111
+ const className = col.slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
1112
+ const getListRes = getTypeForPath("get", `${prefix}/${col.slug}`);
1113
+ const getListQuery = getQueryTypeForPath(`${prefix}/${col.slug}`);
1114
+ const getSingleRes = getTypeForPath("get", `${prefix}/${col.slug}/{id}`);
1115
+ const postReq = getReqForPath("post", `${prefix}/${col.slug}`);
1116
+ const postRes = getTypeForPath("post", `${prefix}/${col.slug}`);
1117
+ const patchReq = getReqForPath("patch", `${prefix}/${col.slug}/{id}`);
1118
+ const patchRes = getTypeForPath("patch", `${prefix}/${col.slug}/{id}`);
1119
+ const deleteRes = getTypeForPath("delete", `${prefix}/${col.slug}/{id}`);
1120
+ const publicFields = col.fields.filter((f) => f.type !== "password" && !f.hidden);
1121
+ const schemaObjStr = JSON.stringify(publicFields.reduce((acc, f) => {
1122
+ acc[f.name] = {
1123
+ type: f.type,
1124
+ required: !f.optional,
1125
+ ...f.values ? { values: f.values } : {},
1126
+ ...f.target ? { target: f.target } : {}
1127
+ };
1128
+ return acc;
1129
+ }, {}));
1130
+ const methods = [
1131
+ `schema: ${schemaObjStr} as const,`,
1132
+ `get: ((params?: { id?: string; query?: ${getListQuery} }) => {
1133
+ if (params?.id) return parent.request<any>('GET', \`${prefix}/${col.slug}/\${params.id}\${params?.query ? '?' + new URLSearchParams({ q: JSON.stringify(params.query) }).toString() : ''}\`);
1134
+ return parent.request<any>('GET', \`${prefix}/${col.slug}\${params?.query ? '?' + new URLSearchParams({ q: JSON.stringify(params.query) }).toString() : ''}\`);
1135
+ }) as {
1136
+ (params: { id: string, query: { depth: 2 } }): Promise<SDKResponse<${className}PopulatedDepth2>>;
1137
+ (params: { id: string, query: { depth: 1 } }): Promise<SDKResponse<${className}Populated>>;
1138
+ (params: { id: string, query?: { depth?: number } }): Promise<SDKResponse<${getSingleRes}>>;
1139
+ (params: { query: ${getListQuery} & { depth: 2 } }): Promise<SDKResponse<Paginated${className}PopulatedDepth2>>;
1140
+ (params: { query: ${getListQuery} & { depth: 1 } }): Promise<SDKResponse<Paginated${className}Populated>>;
1141
+ (params?: { query?: ${getListQuery} }): Promise<SDKResponse<${getListRes}>>;
1142
+ },`,
1143
+ `post: (data: ${postReq}) => parent.request<${postRes}>('POST', \`${prefix}/${col.slug}\`, data),`,
1144
+ `patch: (id: string, data: ${patchReq}) => parent.request<${patchRes}>('PATCH', \`${prefix}/${col.slug}/\${id}\`, data),`,
1145
+ `delete: (id: string) => parent.request<${deleteRes}>('DELETE', \`${prefix}/${col.slug}/\${id}\`),`,
1146
+ `subscribeSSE: async (onEvent: (event: { type: 'created' | 'updated' | 'deleted'; data: ${className} }) => void) => await parent.subscribeSSE(\`${prefix}/${col.slug}/_subscribe\`, onEvent),`,
1147
+ `subscribeWS: async (onEvent: (event: { type: 'created' | 'updated' | 'deleted'; data: ${className} }) => void) => await parent.subscribeWS(\`${prefix}/${col.slug}/_subscribe\`, onEvent),`
1148
+ ];
1149
+ if (!root.children[col.slug]) {
1150
+ root.children[col.slug] = { methods: [], children: {} };
1151
+ }
1152
+ root.children[col.slug].methods.push(...methods);
1153
+ hasRoutes = true;
1154
+ if (col.auth && !root.children["auth"]) {
1155
+ root.children["auth"] = { methods: [], children: {} };
1156
+ const meRes = getTypeForPath("get", `${prefix}/${col.slug}/me`);
1157
+ root.children[col.slug].methods.push(`me: () => parent.request<${meRes}>('GET', \`${prefix}/${col.slug}/me\`),`);
1158
+ }
1159
+ }
1160
+ for (const glob of schema.globals || []) {
1161
+ const className = glob.slug.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("") + "Global";
1162
+ const getRes = getTypeForPath("get", `${prefix}/globals/${glob.slug}`);
1163
+ const patchReq = getReqForPath("patch", `${prefix}/globals/${glob.slug}`);
1164
+ const patchRes = getTypeForPath("patch", `${prefix}/globals/${glob.slug}`);
1165
+ const methods = [
1166
+ `get: () => parent.request<${getRes}>('GET', \`${prefix}/globals/${glob.slug}\`),`,
1167
+ `patch: (data: ${patchReq}) => parent.request<${patchRes}>('PATCH', \`${prefix}/globals/${glob.slug}\`, data),`
1168
+ ];
1169
+ if (!root.children["globals"]) {
1170
+ root.children["globals"] = { methods: [], children: {} };
1171
+ }
1172
+ if (!root.children["globals"].children[glob.slug]) {
1173
+ root.children["globals"].children[glob.slug] = { methods: [], children: {} };
1174
+ }
1175
+ root.children["globals"].children[glob.slug].methods.push(...methods);
1176
+ hasRoutes = true;
1177
+ }
1178
+ const standardPaths = new Set;
1179
+ for (const col of schema.collections || []) {
1180
+ standardPaths.add(`get:${prefix}/${col.slug}`);
1181
+ standardPaths.add(`get:${prefix}/${col.slug}/{id}`);
1182
+ standardPaths.add(`post:${prefix}/${col.slug}`);
1183
+ standardPaths.add(`patch:${prefix}/${col.slug}/{id}`);
1184
+ standardPaths.add(`delete:${prefix}/${col.slug}/{id}`);
1185
+ if (col.auth) {
1186
+ standardPaths.add(`get:${prefix}/${col.slug}/me`);
1187
+ }
1188
+ }
1189
+ for (const glob of schema.globals || []) {
1190
+ standardPaths.add(`get:${prefix}/globals/${glob.slug}`);
1191
+ standardPaths.add(`patch:${prefix}/globals/${glob.slug}`);
1192
+ standardPaths.add(`post:${prefix}/globals/${glob.slug}`);
1193
+ }
1194
+ for (const [rawPath, pathItem] of Object.entries(openApiSpec.paths || {})) {
1195
+ for (const [method, operationRaw] of Object.entries(pathItem)) {
1196
+ const operation = operationRaw;
1197
+ if (standardPaths.has(`${method.toLowerCase()}:${rawPath}`))
1198
+ continue;
1199
+ if (rawPath.startsWith(`${prefix}/docs`) || method === "options")
1200
+ continue;
1201
+ let reqType = "any";
1202
+ let reqOptional = true;
1203
+ if (operation.requestBody) {
1204
+ reqOptional = !operation.requestBody.required;
1205
+ }
1206
+ if (operation.requestBody?.content?.["application/json"]?.schema) {
1207
+ reqType = resolveType(operation.requestBody.content["application/json"].schema);
1208
+ } else if (operation.requestBody?.content?.["multipart/form-data"]?.schema) {
1209
+ reqType = "FormData | any";
1210
+ }
1211
+ let resType = "any";
1212
+ let isStream = false;
1213
+ let isMediaStream = false;
1214
+ const successRes = operation.responses?.["200"] || operation.responses?.["201"];
1215
+ if (operation.responses?.["206"]) {
1216
+ isMediaStream = true;
1217
+ }
1218
+ if (successRes?.content?.["text/event-stream"]) {
1219
+ isStream = true;
1220
+ }
1221
+ if (successRes?.content?.["application/json"]?.schema) {
1222
+ resType = resolveType(successRes.content["application/json"].schema);
1223
+ } else if (successRes && !successRes.content) {
1224
+ resType = "Blob | any";
1225
+ }
1226
+ let p = rawPath;
1227
+ if (p.startsWith(prefix))
1228
+ p = p.slice(prefix.length);
1229
+ if (p.startsWith("/"))
1230
+ p = p.slice(1);
1231
+ const segments = p.split("/").filter(Boolean);
1232
+ let isAuthRoute = false;
1233
+ if (segments.length === 2 && segments[1] !== undefined && ["login", "register", "refresh", "logout", "forgot-password", "reset-password"].includes(segments[1])) {
1234
+ const isAuthCollectionRoute = schema.collections?.some((c) => c.slug === segments[0] && c.auth);
1235
+ if (isAuthCollectionRoute) {
1236
+ segments[0] = "auth";
1237
+ isAuthRoute = true;
1238
+ segments[1] = segments[1].replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1239
+ }
1240
+ } else if (segments.length === 3 && segments[1] === "mfa" && segments[2] !== undefined && ["enroll", "verify", "login", "disable"].includes(segments[2])) {
1241
+ const isAuthCollectionRoute = schema.collections?.some((c) => c.slug === segments[0] && c.auth);
1242
+ if (isAuthCollectionRoute) {
1243
+ segments[0] = "auth";
1244
+ segments[1] = `mfa${segments[2].charAt(0).toUpperCase() + segments[2].slice(1)}`;
1245
+ segments.pop();
1246
+ isAuthRoute = true;
1247
+ }
1248
+ }
1249
+ let current = root;
1250
+ for (let i = 0;i < segments.length; i++) {
1251
+ const seg = segments[i];
1252
+ if (seg === undefined)
1253
+ continue;
1254
+ if (seg.startsWith("{") && seg.endsWith("}")) {
1255
+ const paramName = seg.slice(1, -1).replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1256
+ if (!current.paramChild) {
1257
+ current.paramChild = { paramName, node: { methods: [], children: {} } };
1258
+ }
1259
+ current = current.paramChild.node;
1260
+ if (i === segments.length - 1) {
1261
+ let argsStr = "";
1262
+ let reqArgs = [`'${method.toUpperCase()}'`, `\`${rawPath.replace(/{/g, "${")}\``];
1263
+ if (["post", "put", "patch"].includes(method.toLowerCase())) {
1264
+ argsStr = `data${reqOptional ? "?" : ""}: ${reqType}`;
1265
+ reqArgs.push("data");
1266
+ }
1267
+ const methodTitle = method.toLowerCase();
1268
+ let methodStr = "";
1269
+ if (isMediaStream) {
1270
+ methodStr = `toUrl: (queryParams?: Record<string, any>) => parent.buildUrl(\`${rawPath.replace(/{/g, "${")}\`, queryParams),`;
1271
+ } else if (isStream) {
1272
+ methodStr = `${methodTitle}: (${argsStr}) => parent.streamRequest('${method.toUpperCase()}', \`${rawPath.replace(/{/g, "${")}\`${reqArgs.length > 2 ? ", data" : ""}),`;
1273
+ } else {
1274
+ methodStr = `${methodTitle}: (${argsStr}) => parent.request<${resType}>(${reqArgs.join(", ")}),`;
1275
+ }
1276
+ if (current.methods.some((m) => m.trim().startsWith(`${methodTitle}:`))) {
1277
+ console.warn(`[sdk] Duplicate route: ${method.toUpperCase()} ${rawPath} \u2014 already registered, skipping`);
1278
+ } else {
1279
+ current.methods.push(methodStr);
1280
+ hasRoutes = true;
1281
+ }
1282
+ }
1283
+ } else {
1284
+ if (i === segments.length - 1 && (isAuthRoute || current.children[seg])) {
1285
+ let argsStr = "";
1286
+ let reqArgs = [`'${method.toUpperCase()}'`, `\`${rawPath.replace(/{/g, "${")}\``];
1287
+ if (["post", "put", "patch"].includes(method.toLowerCase())) {
1288
+ argsStr = `data${reqOptional ? "?" : ""}: ${reqType}`;
1289
+ reqArgs.push("data");
1290
+ }
1291
+ const camelSeg = seg.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1292
+ const methodTitle = isAuthRoute ? camelSeg : `${method.toLowerCase()}${camelSeg.charAt(0).toUpperCase() + camelSeg.slice(1)}`;
1293
+ let methodStr = "";
1294
+ if (isMediaStream) {
1295
+ methodStr = `toUrl: (queryParams?: Record<string, any>) => parent.buildUrl(\`${rawPath.replace(/{/g, "${")}\`, queryParams),`;
1296
+ } else if (isStream) {
1297
+ methodStr = `${methodTitle}: (${argsStr}) => parent.streamRequest('${method.toUpperCase()}', \`${rawPath.replace(/{/g, "${")}\`${reqArgs.length > 2 ? ", data" : ""}),`;
1298
+ } else {
1299
+ methodStr = `${methodTitle}: (${argsStr}) => parent.request<${resType}>(${reqArgs.join(", ")}),`;
1300
+ }
1301
+ if (current.methods.some((m) => m.trim().startsWith(`${methodTitle}:`))) {
1302
+ console.warn(`[sdk] Duplicate route: ${method.toUpperCase()} ${rawPath} \u2014 already registered, skipping`);
1303
+ } else {
1304
+ current.methods.push(methodStr);
1305
+ hasRoutes = true;
1306
+ }
1307
+ break;
1308
+ } else if (i === segments.length - 1) {
1309
+ let argsStr = "";
1310
+ let reqArgs = [`'${method.toUpperCase()}'`, `\`${rawPath.replace(/{/g, "${")}\``];
1311
+ if (["post", "put", "patch"].includes(method.toLowerCase())) {
1312
+ argsStr = `data${reqOptional ? "?" : ""}: ${reqType}`;
1313
+ reqArgs.push("data");
1314
+ }
1315
+ const cleanSeg2 = seg.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1316
+ if (!current.children[cleanSeg2]) {
1317
+ current.children[cleanSeg2] = { methods: [], children: {} };
1318
+ }
1319
+ const targetNode = current.children[cleanSeg2];
1320
+ const methodTitle = i === 0 ? method.toLowerCase() : isAuthRoute ? cleanSeg2 : `${method.toLowerCase()}${cleanSeg2.charAt(0).toUpperCase() + cleanSeg2.slice(1)}`;
1321
+ let methodStr = "";
1322
+ if (isMediaStream) {
1323
+ methodStr = `toUrl: (queryParams?: Record<string, any>) => parent.buildUrl(\`${rawPath.replace(/{/g, "${")}\`, queryParams),`;
1324
+ } else if (isStream) {
1325
+ methodStr = `${methodTitle}: (${argsStr}) => parent.streamRequest('${method.toUpperCase()}', \`${rawPath.replace(/{/g, "${")}\`${reqArgs.length > 2 ? ", data" : ""}),`;
1326
+ } else {
1327
+ methodStr = `${methodTitle}: (${argsStr}) => parent.request<${resType}>(${reqArgs.join(", ")}),`;
1328
+ }
1329
+ if (targetNode.methods.some((m) => m.trim().startsWith(`${methodTitle}:`))) {
1330
+ console.warn(`[sdk] Duplicate route: ${method.toUpperCase()} ${rawPath} \u2014 already registered, skipping`);
1331
+ } else {
1332
+ targetNode.methods.push(methodStr);
1333
+ hasRoutes = true;
1334
+ }
1335
+ break;
1336
+ }
1337
+ const cleanSeg = seg.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1338
+ if (!current.children[cleanSeg]) {
1339
+ current.children[cleanSeg] = { methods: [], children: {} };
1340
+ }
1341
+ current = current.children[cleanSeg];
1342
+ }
1343
+ }
1344
+ }
1345
+ }
1346
+ function stringifyNode(node, indentLevel) {
1347
+ const indent = " ".repeat(indentLevel);
1348
+ const hasMethods = node.methods.length > 0;
1349
+ const hasChildren = Object.keys(node.children).length > 0;
1350
+ const hasParam = !!node.paramChild;
1351
+ if (!hasMethods && !hasChildren && !hasParam)
1352
+ return "{}";
1353
+ let childrenStr = "";
1354
+ if (hasMethods || hasChildren) {
1355
+ childrenStr += `{
1356
+ `;
1357
+ if (hasMethods) {
1358
+ childrenStr += node.methods.map((m) => `${indent} ${m}`).join(`
1359
+ `) + `
1360
+ `;
1361
+ }
1362
+ for (const [key, child] of Object.entries(node.children)) {
1363
+ childrenStr += `${indent} ${key}: ${stringifyNode(child, indentLevel + 1).trim()},
1364
+ `;
1365
+ }
1366
+ childrenStr += `${indent}}`;
1367
+ }
1368
+ if (hasParam) {
1369
+ const paramName = node.paramChild.paramName;
1370
+ const paramChildStr = stringifyNode(node.paramChild.node, indentLevel + 1).trim();
1371
+ const methodName = `by${paramName.charAt(0).toUpperCase() + paramName.slice(1)}`;
1372
+ const paramMethod = `${indent} ${methodName}: (${paramName}: string) => (${paramChildStr})`;
1373
+ let combinedStr = `{
1374
+ `;
1375
+ if (hasMethods) {
1376
+ combinedStr += node.methods.map((m) => `${indent} ${m}`).join(`
1377
+ `) + `
1378
+ `;
1379
+ }
1380
+ for (const [key, child] of Object.entries(node.children)) {
1381
+ combinedStr += `${indent} ${key}: ${stringifyNode(child, indentLevel + 1).trim()},
1382
+ `;
1383
+ }
1384
+ combinedStr += `${paramMethod}
1385
+ ${indent}}`;
1386
+ return combinedStr;
1387
+ }
1388
+ return childrenStr;
1389
+ }
1390
+ let treeBody = ` private _cache: Record<string, any> = {};
1391
+
1392
+ `;
1393
+ for (const [key, child] of Object.entries(root.children)) {
1394
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
1395
+ const initName = `_init_${key.replace(/[^a-zA-Z0-9_]/g, "_")}`;
1396
+ treeBody += ` private ${initName}() {
1397
+ const parent = this;
1398
+ return ${stringifyNode(child, 2).trim()};
1399
+ }
1400
+
1401
+ `;
1402
+ treeBody += ` get ${safeKey}(): ReturnType<typeof this.${initName}> {
1403
+ if (!this._cache['${key}']) {
1404
+ this._cache['${key}'] = this.${initName}();
1405
+ }
1406
+ return this._cache['${key}'];
1407
+ }
1408
+
1409
+ `;
1410
+ }
1411
+ const engine = `
1412
+ // ==========================================
1413
+ // Cequre Core Engine
1414
+ // ==========================================
1415
+ export interface CequreClientConfig {
1416
+ baseUrl?: string;
1417
+ token?: string;
1418
+ getToken?: () => string | null | Promise<string | null>;
1419
+ credentials?: "omit" | "same-origin" | "include";
1420
+ onResponse?: (response: SDKResponse<any>) => void | Promise<void>;
1421
+ }
1422
+
1423
+ export interface CequreError {
1424
+ code: string;
1425
+ message: string;
1426
+ statusCode: number;
1427
+ timestamp: string;
1428
+ path?: string;
1429
+ stack?: string;
1430
+ }
1431
+
1432
+ export interface SDKResponse<T> {
1433
+ data: T | null;
1434
+ error: CequreError | null;
1435
+ status: number;
1436
+ url: string;
1437
+ headers: Headers | Record<string, string>;
1438
+ }
1439
+
1440
+ export class CequreCore {
1441
+ private config: CequreClientConfig;
1442
+
1443
+ constructor(config: CequreClientConfig = {}) {
1444
+
1445
+ this.config = { ...config, baseUrl: (config.baseUrl || "").replace(/\\/$/, "") };
1446
+ }
1447
+
1448
+ withToken(token: string): CequreClient {
1449
+ const cloned = Object.create(Object.getPrototypeOf(this));
1450
+ cloned.config = { ...this.config, token };
1451
+ cloned._cache = {};
1452
+ return cloned;
1453
+ }
1454
+
1455
+ async buildUrl(path: string, queryParams: Record<string, any> = {}): Promise<string> {
1456
+ let url = \`\${this.config.baseUrl}\${path}\`;
1457
+ const token = await this.resolveToken();
1458
+ const params = new URLSearchParams();
1459
+ for (const [key, val] of Object.entries(queryParams)) {
1460
+ if (val !== undefined && val !== null) {
1461
+ params.append(key, String(val));
1462
+ }
1463
+ }
1464
+ if (token) params.set("token", token);
1465
+ const qs = params.toString();
1466
+ if (qs) url += (url.includes('?') ? '&' : '?') + qs;
1467
+ return url;
1468
+ }
1469
+
1470
+ private async resolveToken(): Promise<string | null> {
1471
+ if (this.config.token) return this.config.token;
1472
+ if (this.config.getToken) {
1473
+ const t = await this.config.getToken();
1474
+ if (t) return t;
1475
+ }
1476
+ return null;
1477
+ }
1478
+
1479
+ private async getAuthHeader(): Promise<string | null> {
1480
+ const t = await this.resolveToken();
1481
+ return t ? \`Bearer \${t}\` : null;
1482
+ }
1483
+
1484
+ private async fetchWithRetry(url: string, options: RequestInit, maxRetries = 2): Promise<Response> {
1485
+ let lastError: unknown;
1486
+ const method = (options.method || 'GET').toUpperCase();
1487
+ const isIdempotent = method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
1488
+ const retries = isIdempotent ? maxRetries : 0;
1489
+
1490
+ for (let attempt = 0; attempt <= retries; attempt++) {
1491
+ try {
1492
+ const res = await fetch(url, options);
1493
+ if (res.status >= 500 && res.status !== 501 && attempt < retries) {
1494
+ await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));
1495
+ continue;
1496
+ }
1497
+ return res;
1498
+ } catch (err: any) {
1499
+ if (err?.name === 'AbortError') throw err;
1500
+ lastError = err;
1501
+ if (attempt < retries) {
1502
+ await new Promise(r => setTimeout(r, 1000 * 2 ** attempt));
1503
+ continue;
1504
+ }
1505
+ }
1506
+ }
1507
+ throw lastError;
1508
+ }
1509
+
1510
+ async request<T>(
1511
+ method: string,
1512
+ path: string,
1513
+ data?: any,
1514
+ customHeaders: Record<string, string> = {},
1515
+ signal?: AbortSignal
1516
+ ): Promise<SDKResponse<T>> {
1517
+ const headers: Record<string, string> = {
1518
+ 'X-Cequre-CSRF': '1',
1519
+ ...customHeaders
1520
+ };
1521
+ const auth = await this.getAuthHeader();
1522
+ if (auth) headers['Authorization'] = auth;
1523
+
1524
+ const options: RequestInit = {
1525
+ method,
1526
+ headers,
1527
+ credentials: this.config.credentials,
1528
+ signal
1529
+ };
1530
+
1531
+ if (data) {
1532
+ const hasFiles = this.containsFiles(data);
1533
+ if (hasFiles) {
1534
+ const formData = new FormData();
1535
+ for (const key of Object.keys(data)) {
1536
+ const val = data[key];
1537
+ if (val == null) continue;
1538
+ if (val instanceof File || val instanceof Blob) {
1539
+ formData.append(key, val);
1540
+ } else if (typeof val === 'boolean') {
1541
+ formData.append(key, val ? 'true' : 'false');
1542
+ } else if (typeof val === 'object') {
1543
+ formData.append(key, JSON.stringify(val));
1544
+ } else {
1545
+ formData.append(key, String(val));
1546
+ }
1547
+ }
1548
+ options.body = formData;
1549
+ } else {
1550
+ headers['Content-Type'] = 'application/json';
1551
+ options.body = JSON.stringify(data);
1552
+ }
1553
+ }
1554
+
1555
+ try {
1556
+ const res = await this.fetchWithRetry(\`\${this.config.baseUrl}\${path}\`, options);
1557
+
1558
+ if (!res.ok) {
1559
+ let errData;
1560
+ const errText = await res.text();
1561
+ try {
1562
+ errData = JSON.parse(errText);
1563
+ } catch {
1564
+ errData = {
1565
+ code: "SERVER_ERROR",
1566
+ message: errText || res.statusText || "Unknown server error",
1567
+ statusCode: res.status,
1568
+ timestamp: new Date().toISOString(),
1569
+ path
1570
+ };
1571
+ }
1572
+
1573
+ const errorObject: CequreError = {
1574
+ code: errData?.code || "UNKNOWN_ERROR",
1575
+ message: errData?.message || (typeof errData?.error === "string" ? errData.error : "An unexpected error occurred"),
1576
+ statusCode: errData?.statusCode || res.status,
1577
+ timestamp: errData?.timestamp || new Date().toISOString(),
1578
+ path: errData?.path || path,
1579
+ stack: errData?.stack
1580
+ };
1581
+
1582
+ const result = { data: null, error: errorObject, status: res.status, url: res.url || path, headers: res.headers };
1583
+ if (this.config.onResponse) await this.config.onResponse(result);
1584
+ return result;
1585
+ }
1586
+
1587
+ if (res.status === 204 || res.headers.get('content-length') === '0') {
1588
+ const result = { data: undefined as unknown as T, error: null, status: res.status, url: res.url || path, headers: res.headers };
1589
+ if (this.config.onResponse) await this.config.onResponse(result);
1590
+ return result;
1591
+ }
1592
+
1593
+ const contentType = res.headers.get('content-type') || '';
1594
+ if (!contentType.includes('application/json') && !contentType.includes('text/')) {
1595
+ const blob = await res.blob();
1596
+ const result = { data: blob as unknown as T, error: null, status: res.status, url: res.url || path, headers: res.headers };
1597
+ if (this.config.onResponse) await this.config.onResponse(result);
1598
+ return result;
1599
+ }
1600
+
1601
+ const text = await res.text();
1602
+
1603
+ if (!text) {
1604
+ const result = { data: undefined as unknown as T, error: null, status: res.status, url: res.url || path, headers: res.headers };
1605
+ if (this.config.onResponse) await this.config.onResponse(result);
1606
+ return result;
1607
+ }
1608
+
1609
+ try {
1610
+ const result = { data: JSON.parse(text) as T, error: null, status: res.status, url: res.url || path, headers: res.headers };
1611
+ if (this.config.onResponse) await this.config.onResponse(result);
1612
+ return result;
1613
+ } catch {
1614
+ const result = { data: text as unknown as T, error: null, status: res.status, url: res.url || path, headers: res.headers };
1615
+ if (this.config.onResponse) await this.config.onResponse(result);
1616
+ return result;
1617
+ }
1618
+ } catch (error: any) {
1619
+ if (error?.name === 'AbortError') throw error;
1620
+ if (error instanceof Response) throw error;
1621
+ const networkError: CequreError = {
1622
+ code: "NETWORK_ERROR",
1623
+ message: error?.message || "Failed to connect to the server",
1624
+ statusCode: 0,
1625
+ timestamp: new Date().toISOString(),
1626
+ path
1627
+ };
1628
+ const result = { data: null as any, error: networkError, status: 0, url: path, headers: {} };
1629
+ if (this.config.onResponse) await this.config.onResponse(result);
1630
+ return result;
1631
+ }
1632
+ }
1633
+
1634
+ private containsFiles(data: any): boolean {
1635
+ if (!data || typeof data !== 'object') return false;
1636
+ for (const key of Object.keys(data)) {
1637
+ if (data[key] instanceof File || data[key] instanceof Blob) return true;
1638
+ }
1639
+ return false;
1640
+ }
1641
+
1642
+ async uploadLargeFile(file: File, options?: { chunkSize?: number, onProgress?: (percent: number) => void }): Promise<SDKResponse<any>> {
1643
+ const chunkSize = options?.chunkSize || 5 * 1024 * 1024; // 5MB default
1644
+ const totalParts = Math.ceil(file.size / chunkSize);
1645
+ // Determine the API prefix (default to /api if not dynamically available, though server maps it globally)
1646
+ const prefix = "/api"; // The standard Cequre prefix
1647
+
1648
+ // 1. Init
1649
+ const initRes = await this.request<any>("POST", \`\${prefix}/uploads/multipart/init\`, {
1650
+ filename: file.name,
1651
+ mimetype: file.type,
1652
+ size: file.size
1653
+ });
1654
+
1655
+ if (initRes.error) return initRes;
1656
+ const { uploadId, parts } = initRes.data;
1657
+
1658
+ const completedParts = [];
1659
+ const isDirect = Array.isArray(parts) && parts.length > 0;
1660
+
1661
+ for (let i = 0; i < totalParts; i++) {
1662
+ const partNumber = i + 1;
1663
+ const start = i * chunkSize;
1664
+ const end = Math.min(start + chunkSize, file.size);
1665
+ const chunk = (file as any).slice(start, end);
1666
+
1667
+ if (isDirect) {
1668
+ // Direct-to-cloud using presigned URL
1669
+ const presignedUrl = parts.find((p: any) => p.partNumber === partNumber)?.url;
1670
+ if (!presignedUrl) {
1671
+ return { data: null, error: { code: "UPLOAD_ERROR", message: "Missing presigned URL for part", statusCode: 500, timestamp: new Date().toISOString() }, status: 500, url: "", headers: new Headers() };
1672
+ }
1673
+
1674
+ try {
1675
+ const res = await fetch(presignedUrl, {
1676
+ method: "PUT",
1677
+ body: chunk,
1678
+ headers: { "Content-Type": file.type }
1679
+ });
1680
+ if (!res.ok) throw new Error(\`Failed to upload part \${partNumber}\`);
1681
+
1682
+ let etag = res.headers.get("ETag") || res.headers.get("etag");
1683
+ if (etag) etag = etag.replace(/"/g, ""); // AWS wraps ETags in quotes
1684
+ completedParts.push({ partNumber, etag });
1685
+ } catch (err: any) {
1686
+ return { data: null, error: { code: "UPLOAD_ERROR", message: err.message, statusCode: 500, timestamp: new Date().toISOString() }, status: 500, url: "", headers: new Headers() };
1687
+ }
1688
+
1689
+ } else {
1690
+ // Fallback to Cequre Server
1691
+ const res = await this.request<any>("POST", \`\${prefix}/uploads/multipart/chunk?uploadId=\${uploadId}&partNumber=\${partNumber}\`, chunk, {
1692
+ "Content-Type": "application/octet-stream"
1693
+ });
1694
+ if (res.error) return res;
1695
+ let etag = res.data.etag;
1696
+ completedParts.push({ partNumber, etag });
1697
+ }
1698
+
1699
+ if (options?.onProgress) {
1700
+ options.onProgress(Math.round(((i + 1) / totalParts) * 100));
1701
+ }
1702
+ }
1703
+
1704
+ // 3. Complete
1705
+ return this.request<any>("POST", \`\${prefix}/uploads/multipart/complete\`, {
1706
+ uploadId,
1707
+ filename: file.name,
1708
+ parts: completedParts
1709
+ });
1710
+ }
1711
+
1712
+ async *streamRequest(method: string, path: string, data?: any, options?: RequestInit): AsyncGenerator<string, void, unknown> {
1713
+ const headers = new Headers(options?.headers);
1714
+ const token = await this.resolveToken();
1715
+ if (token) {
1716
+ headers.set('Authorization', \`Bearer \${token}\`);
1717
+ }
1718
+
1719
+ const fetchOptions: RequestInit = {
1720
+ ...options,
1721
+ method,
1722
+ headers
1723
+ };
1724
+
1725
+ if (data !== undefined) {
1726
+ if (this.containsFiles(data)) {
1727
+ const formData = new FormData();
1728
+ for (const key of Object.keys(data)) {
1729
+ const val = data[key];
1730
+ if (val == null) continue;
1731
+ if (val instanceof File || val instanceof Blob) {
1732
+ formData.append(key, val);
1733
+ } else if (typeof val === 'boolean') {
1734
+ formData.append(key, val ? 'true' : 'false');
1735
+ } else if (typeof val === 'object') {
1736
+ formData.append(key, JSON.stringify(val));
1737
+ } else {
1738
+ formData.append(key, String(val));
1739
+ }
1740
+ }
1741
+ fetchOptions.body = formData;
1742
+ } else {
1743
+ headers.set('Content-Type', 'application/json');
1744
+ fetchOptions.body = JSON.stringify(data);
1745
+ }
1746
+ }
1747
+
1748
+ const res = await this.fetchWithRetry(\`\${this.config.baseUrl}\${path}\`, fetchOptions);
1749
+
1750
+ if (!res.ok) {
1751
+ let errText = await res.text().catch(() => "");
1752
+ throw new Error(\`Stream request failed with status \${res.status}: \${errText}\`);
1753
+ }
1754
+
1755
+ if (!res.body) return;
1756
+
1757
+ const reader = res.body.getReader();
1758
+ const decoder = new TextDecoder();
1759
+
1760
+ try {
1761
+ while (true) {
1762
+ const { done, value } = await reader.read();
1763
+ if (done) break;
1764
+ if (value) {
1765
+ yield decoder.decode(value, { stream: true });
1766
+ }
1767
+ }
1768
+ } finally {
1769
+ reader.cancel().catch(() => {});
1770
+ reader.releaseLock();
1771
+ }
1772
+ }
1773
+
1774
+ /**
1775
+ * @security
1776
+ * Note: EventSource does not support custom headers. The authentication token
1777
+ * is appended to the URL query string. This may be logged by proxies or the server.
1778
+ */
1779
+ async subscribeSSE(path: string, onEvent: (event: any) => void): Promise<() => void> {
1780
+ let url = \`\${this.config.baseUrl}\${path}\`;
1781
+ const token = await this.resolveToken();
1782
+ if (token) {
1783
+ url += (url.includes('?') ? '&' : '?') + \`token=\${encodeURIComponent(token)}\`;
1784
+ }
1785
+
1786
+ const es = new (globalThis as any).EventSource(url);
1787
+ let retryCount = 0;
1788
+ const maxRetries = 5;
1789
+
1790
+ es.onmessage = (e: { data: string }) => {
1791
+ retryCount = 0;
1792
+ try {
1793
+ onEvent(JSON.parse(e.data));
1794
+ } catch (err) {}
1795
+ };
1796
+
1797
+ es.onerror = (_e: any) => {
1798
+ retryCount++;
1799
+ if (retryCount >= maxRetries) {
1800
+ es.close();
1801
+ es.onmessage = null;
1802
+ es.onerror = null;
1803
+ }
1804
+ };
1805
+
1806
+ return () => {
1807
+ es.close();
1808
+ es.onmessage = null;
1809
+ es.onerror = null;
1810
+ };
1811
+ }
1812
+
1813
+ async subscribeWS(path: string, onEvent: (event: any) => void): Promise<() => void> {
1814
+ const wsUrl = (this.config.baseUrl || "").replace(/^http/, 'ws') + path;
1815
+ const token = await this.resolveToken();
1816
+ let ws: WebSocket;
1817
+ let retryCount = 0;
1818
+ const maxRetries = 5;
1819
+ let manualClose = false;
1820
+
1821
+ const connect = () => {
1822
+ ws = new WebSocket(wsUrl);
1823
+ ws.onopen = () => {
1824
+ retryCount = 0;
1825
+ if (token) ws.send(JSON.stringify({ type: 'auth', token }));
1826
+ };
1827
+ ws.onmessage = (e: { data: string }) => {
1828
+ try { onEvent(JSON.parse(e.data)); } catch (err) {}
1829
+ };
1830
+ ws.onerror = () => { /* consumer observes via onclose */ };
1831
+ ws.onclose = () => {
1832
+ if (manualClose) return;
1833
+ if (retryCount < maxRetries) {
1834
+ retryCount++;
1835
+ setTimeout(connect, Math.min(1000 * 2 ** retryCount, 30000));
1836
+ }
1837
+ };
1838
+ };
1839
+
1840
+ connect();
1841
+
1842
+ return () => {
1843
+ manualClose = true;
1844
+ ws?.close();
1845
+ if (ws) {
1846
+ ws.onopen = null;
1847
+ ws.onmessage = null;
1848
+ ws.onerror = null;
1849
+ ws.onclose = null;
1850
+ }
1851
+ };
1852
+ }
1853
+ }
1854
+ `;
1855
+ const exampleUsage = `/**
1856
+ * ==========================================
1857
+ * CEQURE SDK - EXAMPLE USAGE
1858
+ * ==========================================
1859
+ * This SDK is auto-generated and has zero dependencies.
1860
+ * You can drop this file directly into your frontend, or use it in an AI Agent script.
1861
+ *
1862
+ * --- BROWSER / GLOBAL USAGE ---
1863
+ * import { createCequreClient } from './cequre-sdk';
1864
+ *
1865
+ * export const api = createCequreClient({
1866
+ * baseUrl: "http://localhost:3000",
1867
+ * getToken: () => localStorage.getItem("token") // auto-injects auth
1868
+ * });
1869
+ *
1870
+ * // Authentication (for auth-enabled collections)
1871
+ * // const { data: session } = await api.auth.login({ email: "alice@example.com", password: "password123" });
1872
+ * // localStorage.setItem("token", session.accessToken);
1873
+ *
1874
+ * // Create a record
1875
+ * const { data: newUser, error, status } = await api.users.post({ name: "Alice", email: "alice@example.com" });
1876
+ * if (error) {
1877
+ * console.error("Failed to create user:", error.message);
1878
+ * } else {
1879
+ * console.log("Created user:", newUser);
1880
+ * }
1881
+ *
1882
+ * // Subscribe to realtime updates
1883
+ * const unsubscribe = api.users.subscribeWS((event) => console.log("User updated!", event.data));
1884
+ *
1885
+ * // Consume a custom streaming endpoint
1886
+ * // const stream = await api.aiChat.get();
1887
+ * // for await (const chunk of stream) {
1888
+ * // console.log(chunk);
1889
+ * // }
1890
+ *
1891
+ * --- SERVER / SSR USAGE (Remix, Next.js, etc.) ---
1892
+ * // Use .withToken() to create a safe clone for a specific user request
1893
+ * const serverApi = api.withToken(requestToken);
1894
+ * const { data, error } = await serverApi.users.get();
1895
+ * ==========================================
1896
+ */
1897
+ `;
1898
+ const finalOutput = `// cequre-sdk.ts
1899
+ // This file is auto-generated by Cequre. Do NOT edit it manually.
1900
+
1901
+ ${exampleUsage}
1902
+ // ==========================================
1903
+ // Generated Types
1904
+ // ==========================================
1905
+ ${models.join(`
1906
+
1907
+ `)}
1908
+
1909
+ ${engine}
1910
+
1911
+ export class CequreClient extends CequreCore {
1912
+
1913
+ ${treeBody}
1914
+ }
1915
+
1916
+ /**
1917
+ * Creates a new CequreClient instance.
1918
+ */
1919
+ export function createCequreClient(config: CequreClientConfig = {}) {
1920
+ return new CequreClient(config);
1921
+ }
1922
+ `;
1923
+ return finalOutput;
1924
+ }
1925
+ function generateBackendSchemas(schema, routes) {
1926
+ const { generateOpenAPISpec: generateOpenAPISpec2 } = (init_openapi(), __toCommonJS(exports_openapi));
1927
+ const openApiSpec = generateOpenAPISpec2(schema, "http://localhost:3000", "/api", { routes });
1928
+ const schemas = openApiSpec.components?.schemas || {};
1929
+ const models = [];
1930
+ function resolveTypeBox(s, currentSchemaName) {
1931
+ if (!s)
1932
+ return "t.Any()";
1933
+ if (s.$ref) {
1934
+ const refName = `${s.$ref.split("/").pop()}Schema`;
1935
+ if (refName === currentSchemaName) {
1936
+ return "t.Any()";
1937
+ }
1938
+ if (currentSchemaName.endsWith("WhereInputSchema") && refName.endsWith("WhereInputSchema")) {
1939
+ return "t.Any()";
1940
+ }
1941
+ return refName;
1942
+ }
1943
+ switch (s.type) {
1944
+ case "string":
1945
+ if (s.enum)
1946
+ return s.enum.length === 1 ? `t.Literal("${s.enum[0]}")` : `t.Union([${s.enum.map((e) => `t.Literal("${e}")`).join(", ")}])`;
1947
+ if (s.format === "date-time")
1948
+ return `t.Union([t.String({ format: "date-time" }), t.Date()])`;
1949
+ if (s.format)
1950
+ return `t.String({ format: "${s.format}" })`;
1951
+ return "t.String()";
1952
+ case "number":
1953
+ case "integer":
1954
+ return s.enum ? s.enum.length === 1 ? `t.Literal(${s.enum[0]})` : `t.Union([${s.enum.map((e) => `t.Literal(${e})`).join(", ")}])` : "t.Number()";
1955
+ case "boolean":
1956
+ return s.enum ? s.enum.length === 1 ? `t.Literal(${s.enum[0]})` : `t.Union([${s.enum.map((e) => `t.Literal(${e})`).join(", ")}])` : "t.Boolean()";
1957
+ case "array":
1958
+ return `t.Array(${resolveTypeBox(s.items, currentSchemaName)})`;
1959
+ case "object":
1960
+ if (s.properties) {
1961
+ const props = Object.entries(s.properties).map(([key, val]) => {
1962
+ const isRequired = s.required?.includes(key);
1963
+ const safeKey = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : `"${key}"`;
1964
+ let tb = resolveTypeBox(val, currentSchemaName);
1965
+ if (!isRequired) {
1966
+ tb = `t.Optional(${tb})`;
1967
+ }
1968
+ return ` ${safeKey}: ${tb}`;
1969
+ }).join(`,
1970
+ `);
1971
+ return `t.Object({
1972
+ ${props}
1973
+ })`;
1974
+ }
1975
+ return "t.Record(t.String(), t.Any())";
1976
+ default:
1977
+ if (s.enum) {
1978
+ return `t.Union([${s.enum.map((e) => typeof e === "string" ? `t.Literal("${e}")` : `t.Literal(${e})`).join(", ")}])`;
1979
+ }
1980
+ if (s.anyOf) {
1981
+ return `t.Union([${s.anyOf.map((s2) => resolveTypeBox(s2, currentSchemaName)).join(", ")}])`;
1982
+ }
1983
+ if (s.allOf) {
1984
+ return `t.Intersect([${s.allOf.map((s2) => resolveTypeBox(s2, currentSchemaName)).join(", ")}])`;
1985
+ }
1986
+ if (s.oneOf) {
1987
+ return `t.Union([${s.oneOf.map((s2) => resolveTypeBox(s2, currentSchemaName)).join(", ")}])`;
1988
+ }
1989
+ return "t.Any()";
1990
+ }
1991
+ }
1992
+ for (const [schemaName, schemaDef] of Object.entries(schemas)) {
1993
+ models.push(`export const ${schemaName}Schema = ${resolveTypeBox(schemaDef, `${schemaName}Schema`)};`);
1994
+ }
1995
+ const finalOutput = `// cequre-schemas.ts
1996
+ // This file is auto-generated by Cequre. Do NOT edit it manually.
1997
+
1998
+ import { t } from "@cequrebackends/cequre-ts";
1999
+
2000
+ // ==========================================
2001
+ // Generated TypeBox Schemas
2002
+ // ==========================================
2003
+ ${models.join(`
2004
+
2005
+ `)}
2006
+ `;
2007
+ return finalOutput;
2008
+ }
2009
+ var init_sdk = __esm(() => {
2010
+ init_openapi();
2011
+ });
2012
+ // shared/core/base-sql-adapter.ts
2013
+ class BaseSqlAdapter {
2014
+ _createdDbName = null;
2015
+ _numericFields = new Map;
2016
+ _jsonFields = new Map;
2017
+ _relationshipTargets = new Map;
2018
+ _searchableFields = new Map;
2019
+ _knownFields = new Map;
2020
+ get createdDbName() {
2021
+ return this._createdDbName;
2022
+ }
2023
+ getStartupInfo() {
2024
+ return { createdResourceName: this._createdDbName };
2025
+ }
2026
+ configureCollections(collections) {
2027
+ for (const col of collections) {
2028
+ const nums = new Set;
2029
+ const jsons = new Set;
2030
+ const searchable = new Set;
2031
+ const known = new Set(["id"]);
2032
+ for (const f of col.fields) {
2033
+ known.add(f.name);
2034
+ if (f.type === "number" || f.type === "integer") {
2035
+ nums.add(f.name);
2036
+ }
2037
+ if (f.type === "json" || f.type === "richtext" || f.type === "array" || f.type === "upload" || f.type === "object") {
2038
+ jsons.add(f.name);
2039
+ }
2040
+ if (f.type === "relationship" && f.target) {
2041
+ this._relationshipTargets.set(`${col.slug}.${f.name}`, f.target);
2042
+ }
2043
+ }
2044
+ known.add("createdAt");
2045
+ known.add("updatedAt");
2046
+ if (nums.size > 0)
2047
+ this._numericFields.set(col.slug, nums);
2048
+ if (jsons.size > 0)
2049
+ this._jsonFields.set(col.slug, jsons);
2050
+ if (searchable.size > 0)
2051
+ this._searchableFields.set(col.slug, searchable);
2052
+ this._knownFields.set(col.slug, known);
2053
+ }
2054
+ }
2055
+ registerCollections(collections) {
2056
+ this.configureCollections(collections);
2057
+ }
2058
+ parseConstraintError(error) {
2059
+ const message = error instanceof Error ? error.message : String(error);
2060
+ const rawMessage = message;
2061
+ const pgFkMatch = message.match(/violates foreign key constraint [""]([^""]+)[""]/i) || message.match(/[""]([^""]+)[""] is not present in table [""]([^""]+)[""]/i);
2062
+ if (pgFkMatch) {
2063
+ const constraintMatch = message.match(/[""]([^""]+)[""]/);
2064
+ const tableMatch = message.match(/on table [""]([^""]+)[""]/i);
2065
+ const keyMatch = message.match(/Key \(([^)]+)\)=\(([^)]+)\)/i);
2066
+ return {
2067
+ type: "foreign_key",
2068
+ table: tableMatch?.[1],
2069
+ constraint: constraintMatch?.[1],
2070
+ referencedTable: pgFkMatch[2] || constraintMatch?.[1]?.replace(/_[a-z_]+_fkey$/, ""),
2071
+ column: keyMatch?.[1],
2072
+ rawMessage
2073
+ };
2074
+ }
2075
+ const pgUniqueMatch = message.match(/duplicate key.*unique constraint [""]([^""]+)[""]/i);
2076
+ if (pgUniqueMatch) {
2077
+ const tableMatch = message.match(/on table [""]([^""]+)[""]/i);
2078
+ const keyMatch = message.match(/Key \(([^)]+)\)=\(([^)]+)\)/i);
2079
+ return {
2080
+ type: "unique",
2081
+ table: tableMatch?.[1],
2082
+ constraint: pgUniqueMatch[1],
2083
+ column: keyMatch?.[1],
2084
+ rawMessage
2085
+ };
2086
+ }
2087
+ if (message.includes("UNIQUE constraint failed")) {
2088
+ const parts = message.split(":");
2089
+ return { type: "unique", rawMessage, constraint: parts[1]?.trim() };
2090
+ }
2091
+ const pgNotNullMatch = message.match(/null value in column [""]([^""]+)[""].*not-null constraint/i);
2092
+ if (pgNotNullMatch) {
2093
+ const tableMatch = message.match(/on table [""]([^""]+)[""]/i);
2094
+ return {
2095
+ type: "not_null",
2096
+ table: tableMatch?.[1],
2097
+ column: pgNotNullMatch[1],
2098
+ rawMessage
2099
+ };
2100
+ }
2101
+ const pgCheckMatch = message.match(/violates check constraint [""]([^""]+)[""]/i);
2102
+ if (pgCheckMatch) {
2103
+ const tableMatch = message.match(/for relation [""]([^""]+)[""]/i);
2104
+ return {
2105
+ type: "check",
2106
+ table: tableMatch?.[1],
2107
+ constraint: pgCheckMatch[1],
2108
+ rawMessage
2109
+ };
2110
+ }
2111
+ return null;
2112
+ }
2113
+ }
2114
+ function sqlIdentifier(identifier) {
2115
+ return `"${identifier.replace(/"/g, '""')}"`;
2116
+ }
2117
+ function sqlLiteral(value) {
2118
+ return `'${value.replace(/'/g, "''")}'`;
2119
+ }
2120
+ // shared/core/cache.ts
2121
+ class MemoryCacheStore {
2122
+ cache = new Map;
2123
+ async get(key) {
2124
+ const item = this.cache.get(key);
2125
+ if (!item)
2126
+ return null;
2127
+ if (item.expiry !== null && Date.now() > item.expiry) {
2128
+ this.cache.delete(key);
2129
+ return null;
2130
+ }
2131
+ return item.value;
2132
+ }
2133
+ async set(key, value, ttlSeconds) {
2134
+ const expiry = ttlSeconds ? Date.now() + ttlSeconds * 1000 : null;
2135
+ this.cache.set(key, { value, expiry });
2136
+ }
2137
+ async del(...keys) {
2138
+ let count = 0;
2139
+ for (const key of keys) {
2140
+ if (this.cache.delete(key)) {
2141
+ count++;
2142
+ }
2143
+ }
2144
+ return count;
2145
+ }
2146
+ close() {
2147
+ this.cache.clear();
2148
+ }
2149
+ }
2150
+ // shared/core/email.ts
2151
+ class CequreMailer {
2152
+ transport;
2153
+ appName;
2154
+ resetTokenExpiryMinutes;
2155
+ from;
2156
+ templates;
2157
+ constructor(config, transport) {
2158
+ this.appName = config?.appName ?? process.env.SMTP_APP_NAME ?? "Cequre App";
2159
+ this.resetTokenExpiryMinutes = config?.resetTokenExpiryMinutes ?? 60;
2160
+ this.from = config?.from ?? process.env.SMTP_FROM ?? `"Cequre App" <no-reply@cequre.dev>`;
2161
+ this.templates = config?.templates;
2162
+ const resolvedTransport = transport ?? config?.transport;
2163
+ if (!resolvedTransport) {
2164
+ throw CequreError.NotConfigured("Email transport is required. Install an email provider plugin and pass email.transport.");
2165
+ }
2166
+ this.transport = resolvedTransport;
2167
+ }
2168
+ async send(options) {
2169
+ if (!options.to) {
2170
+ throw CequreError.BadRequest("Email 'to' address is required");
2171
+ }
2172
+ return this.transport.send({ from: this.from, ...options });
2173
+ }
2174
+ async verify() {
2175
+ return this.transport.verify();
2176
+ }
2177
+ async sendWelcome(to, opts = {}) {
2178
+ const appName = opts.appName ?? this.appName;
2179
+ const custom = this.templates?.welcome?.({ to, appName });
2180
+ await this.send({
2181
+ to,
2182
+ subject: custom?.subject ?? `Welcome to ${appName}`,
2183
+ html: custom?.html ?? welcomeTemplate({ to, appName }),
2184
+ text: custom?.text ?? `Welcome to ${appName}! Your account has been created successfully.`
2185
+ });
2186
+ }
2187
+ async sendForgotPassword(to, resetUrl, opts = {}) {
2188
+ const appName = opts.appName ?? this.appName;
2189
+ const expiresInMinutes = opts.expiresInMinutes ?? this.resetTokenExpiryMinutes;
2190
+ const custom = this.templates?.forgotPassword?.({ to, resetUrl, appName, expiresInMinutes });
2191
+ await this.send({
2192
+ to,
2193
+ subject: custom?.subject ?? `Reset your ${appName} password`,
2194
+ html: custom?.html ?? forgotPasswordTemplate({ to, resetUrl, appName, expiresInMinutes }),
2195
+ text: custom?.text ?? `You requested a password reset for ${appName}.
2196
+
2197
+ Click the link below to reset your password (valid for ${expiresInMinutes} minutes):
2198
+
2199
+ ${resetUrl}
2200
+
2201
+ If you didn't request a reset, you can safely ignore this email.`
2202
+ });
2203
+ }
2204
+ async sendPasswordResetSuccess(to, opts = {}) {
2205
+ const appName = opts.appName ?? this.appName;
2206
+ const custom = this.templates?.passwordResetSuccess?.({ to, appName });
2207
+ await this.send({
2208
+ to,
2209
+ subject: custom?.subject ?? `Your ${appName} password has been reset`,
2210
+ html: custom?.html ?? passwordResetSuccessTemplate({ to, appName }),
2211
+ text: custom?.text ?? `Your ${appName} password has been successfully reset. If you did not perform this action, contact support immediately.`
2212
+ });
2213
+ }
2214
+ async sendVerificationEmail(to, verifyUrl, opts = {}) {
2215
+ const appName = opts.appName ?? this.appName;
2216
+ const custom = this.templates?.verifyEmail?.({ to, verifyUrl, appName });
2217
+ await this.send({
2218
+ to,
2219
+ subject: custom?.subject ?? `Verify your ${appName} email`,
2220
+ html: custom?.html ?? verifyEmailTemplate({ to, verifyUrl, appName }),
2221
+ text: custom?.text ?? `Welcome to ${appName}! Please verify your email address by clicking the link below:
2222
+
2223
+ ${verifyUrl}
2224
+
2225
+ If you didn't create an account, you can safely ignore this email.`
2226
+ });
2227
+ }
2228
+ }
2229
+ function createMailer(config) {
2230
+ const transport = config?.transport;
2231
+ if (!transport)
2232
+ return;
2233
+ return new CequreMailer(config, transport);
2234
+ }
2235
+ function baseTemplate(content, appName) {
2236
+ return `<!DOCTYPE html>
2237
+ <html lang="en">
2238
+ <head>
2239
+ <meta charset="UTF-8" />
2240
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
2241
+ <style>
2242
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f4f4f7; margin: 0; padding: 0; }
2243
+ .wrapper { max-width: 600px; margin: 40px auto; background: #ffffff; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
2244
+ .header { background: #6d28d9; padding: 32px 40px; }
2245
+ .header h1 { color: #ffffff; margin: 0; font-size: 22px; font-weight: 600; }
2246
+ .body { padding: 32px 40px; color: #374151; line-height: 1.6; }
2247
+ .body p { margin: 0 0 16px; }
2248
+ .btn { display: inline-block; padding: 12px 28px; background: #6d28d9; color: #ffffff !important; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 15px; margin: 8px 0 20px; }
2249
+ .footer { padding: 20px 40px; background: #f9fafb; color: #9ca3af; font-size: 12px; line-height: 1.5; border-top: 1px solid #e5e7eb; }
2250
+ .muted { color: #6b7280; font-size: 13px; }
2251
+ </style>
2252
+ </head>
2253
+ <body>
2254
+ <div class="wrapper">
2255
+ <div class="header"><h1>${appName}</h1></div>
2256
+ <div class="body">${content}</div>
2257
+ <div class="footer">You received this email because an action was performed on your ${appName} account.<br/>If you didn't initiate this, please ignore this email or contact support.</div>
2258
+ </div>
2259
+ </body>
2260
+ </html>`;
2261
+ }
2262
+ function welcomeTemplate({ to, appName }) {
2263
+ return baseTemplate(`<p>Hi <strong>${to}</strong>,</p>
2264
+ <p>Welcome to <strong>${appName}</strong>! Your account has been created successfully.</p>
2265
+ <p>You can now log in and start using the platform.</p>`, appName);
2266
+ }
2267
+ function forgotPasswordTemplate({
2268
+ to,
2269
+ resetUrl,
2270
+ appName,
2271
+ expiresInMinutes
2272
+ }) {
2273
+ return baseTemplate(`<p>Hi,</p>
2274
+ <p>We received a request to reset the password for the account associated with <strong>${to}</strong>.</p>
2275
+ <p>Click the button below to reset your password. This link will expire in <strong>${expiresInMinutes} minutes</strong>.</p>
2276
+ <a href="${resetUrl}" class="btn">Reset Password</a>
2277
+ <p class="muted">Or copy this link into your browser:<br/><a href="${resetUrl}">${resetUrl}</a></p>
2278
+ <p class="muted">If you didn't request a password reset, you can safely ignore this email \u2014 your password will not change.</p>`, appName);
2279
+ }
2280
+ function passwordResetSuccessTemplate({ appName }) {
2281
+ return baseTemplate(`<p>Your password for <strong>${appName}</strong> has been successfully reset.</p>
2282
+ <p>If you performed this action, no further steps are needed.</p>
2283
+ <p class="muted">If you did not reset your password, please contact support immediately and secure your account.</p>`, appName);
2284
+ }
2285
+ function verifyEmailTemplate({ to, verifyUrl, appName }) {
2286
+ return baseTemplate(`<p>Hi <strong>${to}</strong>,</p>
2287
+ <p>Welcome to <strong>${appName}</strong>! Please verify your email address by clicking the button below.</p>
2288
+ <a href="${verifyUrl}" class="btn">Verify Email</a>
2289
+ <p class="muted">Or copy this link into your browser:<br/><a href="${verifyUrl}">${verifyUrl}</a></p>
2290
+ <p class="muted">If you didn't create an account, you can safely ignore this email.</p>`, appName);
2291
+ }
2292
+
2293
+ // shared/core/index.ts
2294
+ init_openapi();
2295
+
2296
+ // shared/core/plugins.ts
2297
+ function sortPlugins(plugins) {
2298
+ const pluginMap = new Map;
2299
+ for (const p of plugins)
2300
+ pluginMap.set(p.name, p);
2301
+ const graph = new Map;
2302
+ const inDegree = new Map;
2303
+ for (const p of plugins) {
2304
+ if (!graph.has(p.name))
2305
+ graph.set(p.name, new Set);
2306
+ if (!inDegree.has(p.name))
2307
+ inDegree.set(p.name, 0);
2308
+ }
2309
+ for (const p of plugins) {
2310
+ if (p.runsBefore) {
2311
+ for (const target of p.runsBefore) {
2312
+ if (!pluginMap.has(target))
2313
+ continue;
2314
+ if (!graph.has(target))
2315
+ graph.set(target, new Set);
2316
+ if (!inDegree.has(target))
2317
+ inDegree.set(target, 0);
2318
+ if (!graph.get(p.name).has(target)) {
2319
+ graph.get(p.name).add(target);
2320
+ inDegree.set(target, inDegree.get(target) + 1);
2321
+ }
2322
+ }
2323
+ }
2324
+ if (p.runsAfter) {
2325
+ for (const target of p.runsAfter) {
2326
+ if (!pluginMap.has(target))
2327
+ continue;
2328
+ if (!graph.has(target))
2329
+ graph.set(target, new Set);
2330
+ if (!inDegree.has(p.name))
2331
+ inDegree.set(p.name, 0);
2332
+ if (!graph.get(target).has(p.name)) {
2333
+ graph.get(target).add(p.name);
2334
+ inDegree.set(p.name, inDegree.get(p.name) + 1);
2335
+ }
2336
+ }
2337
+ }
2338
+ }
2339
+ const queue = [];
2340
+ for (const [name, deg] of inDegree.entries()) {
2341
+ if (deg === 0 && pluginMap.has(name)) {
2342
+ queue.push(name);
2343
+ }
2344
+ }
2345
+ const sorted = [];
2346
+ while (queue.length > 0) {
2347
+ const curr = queue.shift();
2348
+ sorted.push(pluginMap.get(curr));
2349
+ if (graph.has(curr)) {
2350
+ for (const neighbor of graph.get(curr)) {
2351
+ const deg = inDegree.get(neighbor) - 1;
2352
+ inDegree.set(neighbor, deg);
2353
+ if (deg === 0) {
2354
+ queue.push(neighbor);
2355
+ }
2356
+ }
2357
+ }
2358
+ }
2359
+ if (sorted.length !== plugins.length) {
2360
+ throw new Error("Circular dependency detected in Cequre plugins topological ordering.");
2361
+ }
2362
+ return sorted;
2363
+ }
2364
+ // shared/core/request.ts
2365
+ function parseQuery(searchParams) {
2366
+ const query = {};
2367
+ for (const [key, value] of searchParams) {
2368
+ if (key.includes("[")) {
2369
+ const parts = key.split(/\[|\]/).filter(Boolean);
2370
+ let current = query;
2371
+ for (let i = 0;i < parts.length - 1; i++) {
2372
+ const part = parts[i];
2373
+ if (part === undefined)
2374
+ continue;
2375
+ const nextPart = parts[i + 1];
2376
+ if (current[part] === undefined) {
2377
+ current[part] = nextPart !== undefined && /^\d+$/.test(nextPart) ? [] : {};
2378
+ }
2379
+ current = current[part];
2380
+ }
2381
+ const last = parts[parts.length - 1];
2382
+ if (last === undefined)
2383
+ continue;
2384
+ if (current[last] !== undefined) {
2385
+ if (Array.isArray(current[last])) {
2386
+ current[last].push(value);
2387
+ } else {
2388
+ current[last] = [current[last], value];
2389
+ }
2390
+ } else {
2391
+ current[last] = value;
2392
+ }
2393
+ } else {
2394
+ const existing = query[key];
2395
+ if (existing === undefined) {
2396
+ query[key] = value;
2397
+ } else if (Array.isArray(existing)) {
2398
+ existing.push(value);
2399
+ } else {
2400
+ query[key] = [existing, value];
2401
+ }
2402
+ }
2403
+ }
2404
+ return query;
2405
+ }
2406
+ function createRouteContext(request, params = {}, state, user = null, body, cequre) {
2407
+ const url = new URL(request.url);
2408
+ return {
2409
+ request,
2410
+ url,
2411
+ params,
2412
+ query: parseQuery(url.searchParams),
2413
+ body,
2414
+ cequre,
2415
+ user,
2416
+ state,
2417
+ async stream(chunk, options) {
2418
+ if (!this._streamInitiated) {
2419
+ this._streamInitiated = true;
2420
+ const { readable, writable } = new TransformStream;
2421
+ this._streamWriter = writable.getWriter();
2422
+ if (this._streamResolver) {
2423
+ const headers = new Headers;
2424
+ if (options?.event) {
2425
+ headers.set("Content-Type", "text/event-stream");
2426
+ headers.set("Cache-Control", "no-cache");
2427
+ headers.set("Connection", "keep-alive");
2428
+ } else {
2429
+ headers.set("Content-Type", "text/plain");
2430
+ }
2431
+ this._streamResolver(new Response(readable, { headers }));
2432
+ }
2433
+ }
2434
+ if (!this._streamWriter)
2435
+ return;
2436
+ const encoder = new TextEncoder;
2437
+ let data = chunk;
2438
+ if (options?.event) {
2439
+ if (typeof data !== "string" && !(data instanceof Uint8Array)) {
2440
+ data = JSON.stringify(data);
2441
+ }
2442
+ data = `event: ${options.event}
2443
+ data: ${data}
2444
+
2445
+ `;
2446
+ } else if (typeof data !== "string" && !(data instanceof Uint8Array)) {
2447
+ data = JSON.stringify(data) + `
2448
+ `;
2449
+ }
2450
+ if (this.request.signal.aborted) {
2451
+ throw new Error("ClientDisconnected: stream aborted by client");
2452
+ }
2453
+ if (typeof data === "string") {
2454
+ data = encoder.encode(data);
2455
+ }
2456
+ try {
2457
+ await this._streamWriter.write(data);
2458
+ } catch (err) {
2459
+ throw new Error("ClientDisconnected: stream write failed");
2460
+ }
2461
+ if (options?.delay) {
2462
+ await new Promise((r) => setTimeout(r, options.delay));
2463
+ }
2464
+ }
2465
+ };
2466
+ }
2467
+
2468
+ // shared/core/index.ts
2469
+ init_sdk();
2470
+
2471
+ // shared/core/validator.ts
2472
+ import { Type, FormatRegistry } from "@sinclair/typebox";
2473
+ import { TypeCompiler } from "@sinclair/typebox/compiler";
2474
+ FormatRegistry.Set("date-time", (value) => !isNaN(Date.parse(value)));
2475
+ FormatRegistry.Set("email", (value) => /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i.test(value));
2476
+ FormatRegistry.Set("ulid", (value) => /^[0-9A-HJKMNP-TV-Z]{26}$/i.test(value));
2477
+ var SYSTEM_FIELDS = new Set(["id", "createdAt", "updatedAt"]);
2478
+ function buildCollectionCreateSchema(collection) {
2479
+ const properties = {};
2480
+ const required = [];
2481
+ for (const field of collection.fields) {
2482
+ if (SYSTEM_FIELDS.has(field.name))
2483
+ continue;
2484
+ if (field.type === "password" || field.omit || field.type === "link")
2485
+ continue;
2486
+ const schema = fieldToTypeBox(field);
2487
+ properties[field.name] = schema;
2488
+ if (!field.optional && field.default === undefined) {
2489
+ required.push(field.name);
2490
+ }
2491
+ }
2492
+ return Type.Object(properties, { additionalProperties: false, required });
2493
+ }
2494
+ function buildCollectionUpdateSchema(collection) {
2495
+ const properties = {};
2496
+ for (const field of collection.fields) {
2497
+ if (SYSTEM_FIELDS.has(field.name))
2498
+ continue;
2499
+ if (field.type === "password" || field.omit || field.type === "link")
2500
+ continue;
2501
+ const schema = fieldToTypeBox(field);
2502
+ properties[field.name] = Type.Optional(schema);
2503
+ }
2504
+ return Type.Object(properties, { additionalProperties: false });
2505
+ }
2506
+ function fieldToTypeBox(field) {
2507
+ let schema;
2508
+ switch (field.type) {
2509
+ case "string":
2510
+ case "text":
2511
+ case "textarea":
2512
+ case "email":
2513
+ case "password":
2514
+ schema = Type.String();
2515
+ break;
2516
+ case "boolean":
2517
+ schema = Type.Boolean();
2518
+ break;
2519
+ case "number":
2520
+ case "integer":
2521
+ schema = Type.Number();
2522
+ break;
2523
+ case "date":
2524
+ schema = Type.String({ format: "date-time" });
2525
+ break;
2526
+ case "json":
2527
+ case "richtext":
2528
+ schema = Type.Unknown();
2529
+ break;
2530
+ case "select":
2531
+ if (field.values && field.values.length > 0) {
2532
+ schema = Type.Union(field.values.map((v) => Type.Literal(v)));
2533
+ } else {
2534
+ schema = Type.String();
2535
+ }
2536
+ break;
2537
+ case "multiselect":
2538
+ if (field.values && field.values.length > 0) {
2539
+ schema = Type.Array(Type.Union(field.values.map((v) => Type.Literal(v))));
2540
+ } else {
2541
+ schema = Type.Array(Type.String());
2542
+ }
2543
+ break;
2544
+ case "enum":
2545
+ if (field.values && field.values.length > 0) {
2546
+ schema = Type.Union(field.values.map((v) => Type.Literal(v)));
2547
+ } else {
2548
+ schema = Type.String();
2549
+ }
2550
+ break;
2551
+ case "relationship":
2552
+ schema = Type.String();
2553
+ break;
2554
+ case "upload":
2555
+ schema = Type.Any();
2556
+ break;
2557
+ case "array":
2558
+ schema = Type.Array(Type.Unknown());
2559
+ break;
2560
+ case "object":
2561
+ if (field.fields && field.fields.length > 0) {
2562
+ const props = {};
2563
+ for (const f of field.fields) {
2564
+ props[f.name] = fieldToTypeBox(f);
2565
+ }
2566
+ schema = Type.Object(props, { additionalProperties: false });
2567
+ } else {
2568
+ schema = Type.Record(Type.String(), Type.Unknown());
2569
+ }
2570
+ break;
2571
+ default:
2572
+ schema = Type.Unknown();
2573
+ }
2574
+ if (field.isArray) {
2575
+ schema = Type.Array(schema);
2576
+ }
2577
+ if (field.optional || field.default !== undefined) {
2578
+ schema = Type.Optional(schema);
2579
+ }
2580
+ return schema;
2581
+ }
2582
+ var compiledCreateCache = new Map;
2583
+ var compiledUpdateCache = new Map;
2584
+ function validateCreate(collection, data) {
2585
+ if (!collection.fields || collection.fields.length === 0) {
2586
+ return data;
2587
+ }
2588
+ console.log("Validating CREATE for", collection.slug, "FormatRegistry has date-time:", FormatRegistry.Has("date-time"));
2589
+ let check = compiledCreateCache.get(collection.slug);
2590
+ if (!check) {
2591
+ const schema = buildCollectionCreateSchema(collection);
2592
+ if (collection.slug === "kitchenSinks")
2593
+ console.log("SCHEMA:", JSON.stringify(schema, null, 2));
2594
+ check = TypeCompiler.Compile(schema);
2595
+ compiledCreateCache.set(collection.slug, check);
2596
+ }
2597
+ if (!check.Check(data)) {
2598
+ const firstError = check.Errors(data).First();
2599
+ const path = firstError?.path ? ` at ${firstError.path}` : "";
2600
+ const message = firstError?.message ? `Validation error${path}: ${firstError.message}` : `Validation error${path}`;
2601
+ throw CequreError.BadRequest(message);
2602
+ }
2603
+ return data;
2604
+ }
2605
+ function validateUpdate(collection, data) {
2606
+ if (!collection.fields || collection.fields.length === 0) {
2607
+ return data;
2608
+ }
2609
+ let check = compiledUpdateCache.get(collection.slug);
2610
+ if (!check) {
2611
+ const schema = buildCollectionUpdateSchema(collection);
2612
+ check = TypeCompiler.Compile(schema);
2613
+ compiledUpdateCache.set(collection.slug, check);
2614
+ }
2615
+ if (!check.Check(data)) {
2616
+ const firstError = check.Errors(data).First();
2617
+ const path = firstError?.path ? ` at ${firstError.path}` : "";
2618
+ const message = firstError?.message ? `Validation error${path}: ${firstError.message}` : `Validation error${path}`;
2619
+ throw CequreError.BadRequest(message);
2620
+ }
2621
+ return data;
2622
+ }
2623
+ // shared/core/stream.ts
2624
+ class MemoryStreamStore {
2625
+ streams = new Map;
2626
+ maxCapacity;
2627
+ constructor(maxCapacity = 1000) {
2628
+ this.maxCapacity = maxCapacity;
2629
+ }
2630
+ async publish(collection, action, data) {
2631
+ if (!this.streams.has(collection)) {
2632
+ this.streams.set(collection, []);
2633
+ }
2634
+ const stream = this.streams.get(collection);
2635
+ const timestamp = Date.now();
2636
+ const id = `${timestamp}-${Math.random().toString(36).substring(2, 7)}`;
2637
+ const event = {
2638
+ id,
2639
+ collection,
2640
+ action,
2641
+ data,
2642
+ timestamp
2643
+ };
2644
+ stream.push(event);
2645
+ if (stream.length > this.maxCapacity) {
2646
+ stream.shift();
2647
+ }
2648
+ return id;
2649
+ }
2650
+ async read(collection, lastEventId) {
2651
+ const stream = this.streams.get(collection) || [];
2652
+ if (!lastEventId) {
2653
+ return stream;
2654
+ }
2655
+ const index = stream.findIndex((e) => e.id === lastEventId);
2656
+ if (index === -1) {
2657
+ return stream;
2658
+ }
2659
+ return stream.slice(index + 1);
2660
+ }
2661
+ }
2662
+ export { MemoryStreamStore, buildCollectionCreateSchema, buildCollectionUpdateSchema, validateCreate, validateUpdate, generateScalarHTML, generateOpenAPISpec, init_openapi, MemoryCacheStore, CequreMailer, createMailer, generateSharedModels, generateTypeScriptClientSDK, generateBackendSchemas, exports_sdk, init_sdk, BaseSqlAdapter, sqlIdentifier, sqlLiteral, sortPlugins, parseQuery, createRouteContext };