@rebasepro/server 0.13.0 → 0.13.1-canary.g249daa1

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 (56) hide show
  1. package/dist/{admin_block-CcSGdH7M.js → admin_block-CLeUMG8G.js} +3 -1
  2. package/dist/admin_block-CLeUMG8G.js.map +1 -0
  3. package/dist/api/errors.d.ts +0 -6
  4. package/dist/api/openapi-generator.d.ts +14 -0
  5. package/dist/api/rest/idempotency.d.ts +41 -4
  6. package/dist/api/rest/write-validation.d.ts +12 -5
  7. package/dist/auth/index.d.ts +1 -1
  8. package/dist/auth/jwt.d.ts +12 -0
  9. package/dist/{auth-CuC9M2x6.js → auth-BANhRhN9.js} +194 -84
  10. package/dist/auth-BANhRhN9.js.map +1 -0
  11. package/dist/{backup-CVggVhR2.js → backup-BJiY_mM-.js} +2 -2
  12. package/dist/{backup-CVggVhR2.js.map → backup-BJiY_mM-.js.map} +1 -1
  13. package/dist/boot/boot.d.ts +19 -0
  14. package/dist/boot/ddl-bootstrap.d.ts +70 -0
  15. package/dist/{contract-routes-Dj8i5AiM.js → contract-routes-FSt0Nyfk.js} +2 -2
  16. package/dist/{contract-routes-Dj8i5AiM.js.map → contract-routes-FSt0Nyfk.js.map} +1 -1
  17. package/dist/cron/cron-scheduler.d.ts +8 -3
  18. package/dist/cron/define-cron.d.ts +5 -3
  19. package/dist/{cron-routes-CrQ0tK-_.js → cron-routes-D5a9v1HY.js} +2 -2
  20. package/dist/{cron-routes-CrQ0tK-_.js.map → cron-routes-D5a9v1HY.js.map} +1 -1
  21. package/dist/{cron-scheduler-B3RFt0HS.js → cron-scheduler-CsQtYLMq.js} +28 -3
  22. package/dist/cron-scheduler-CsQtYLMq.js.map +1 -0
  23. package/dist/{cron-store-BywZsyfZ.js → cron-store-ZbjfuvMb.js} +65 -46
  24. package/dist/cron-store-ZbjfuvMb.js.map +1 -0
  25. package/dist/ddl-bootstrap-Bp3mr6Td.js +183 -0
  26. package/dist/ddl-bootstrap-Bp3mr6Td.js.map +1 -0
  27. package/dist/{errors-CgkCzoj7.js → errors-BpudWAVU.js} +2 -10
  28. package/dist/errors-BpudWAVU.js.map +1 -0
  29. package/dist/functions/define-function.d.ts +1 -1
  30. package/dist/history/history-routes.d.ts +6 -0
  31. package/dist/index.es.js +835 -276
  32. package/dist/index.es.js.map +1 -1
  33. package/dist/{jwt-DD6EtpGj.js → jwt-CzeviDcB.js} +17 -2
  34. package/dist/{jwt-DD6EtpGj.js.map → jwt-CzeviDcB.js.map} +1 -1
  35. package/dist/{openapi-generator-BEwyiaAr.js → openapi-generator-VB0MUH8h.js} +340 -128
  36. package/dist/openapi-generator-VB0MUH8h.js.map +1 -0
  37. package/dist/{schema-editor-routes-CbF20Mf1.js → schema-editor-routes-BqjzvvWU.js} +4 -4
  38. package/dist/{schema-editor-routes-CbF20Mf1.js.map → schema-editor-routes-BqjzvvWU.js.map} +1 -1
  39. package/dist/{src-_qQ3RNCK.js → src-Ca6NhxKs.js} +79 -2
  40. package/dist/src-Ca6NhxKs.js.map +1 -0
  41. package/dist/{src-Cum9kox5.js → src-DVb5a0bp.js} +153 -39
  42. package/dist/src-DVb5a0bp.js.map +1 -0
  43. package/dist/storage/keys.d.ts +65 -0
  44. package/dist/utils/logging.d.ts +0 -4
  45. package/dist/utils/sql.d.ts +11 -6
  46. package/package.json +6 -6
  47. package/dist/admin_block-CcSGdH7M.js.map +0 -1
  48. package/dist/auth-CuC9M2x6.js.map +0 -1
  49. package/dist/backend-CIxN4FVm.js +0 -15
  50. package/dist/backend-CIxN4FVm.js.map +0 -1
  51. package/dist/cron-scheduler-B3RFt0HS.js.map +0 -1
  52. package/dist/cron-store-BywZsyfZ.js.map +0 -1
  53. package/dist/errors-CgkCzoj7.js.map +0 -1
  54. package/dist/openapi-generator-BEwyiaAr.js.map +0 -1
  55. package/dist/src-Cum9kox5.js.map +0 -1
  56. package/dist/src-_qQ3RNCK.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
- import { m as resolveCollectionRelations } from "./src-Cum9kox5.js";
5
- import "./src-_qQ3RNCK.js";
4
+ import { h as resolveCollectionRelations } from "./src-DVb5a0bp.js";
5
+ import "./src-Ca6NhxKs.js";
6
6
  //#region ../types/src/types/relations.ts
7
7
  /** @group Models */
8
8
  function isToMany(relation) {
@@ -13,6 +13,129 @@ function isToMany(relation) {
13
13
  function generateOpenApiSpec(collections, options = {}) {
14
14
  const basePath = options.basePath ?? "/api";
15
15
  const requireAuth = options.requireAuth ?? true;
16
+ const defaultLimit = options.listLimits?.defaultLimit ?? 50;
17
+ const maxLimit = options.listLimits?.maxLimit ?? 1e3;
18
+ /**
19
+ * The query parameters every list endpoint honours.
20
+ *
21
+ * Written once because it was written twice: the root listing named eight
22
+ * and the subcollection listing named four, though both go through the same
23
+ * `parseQueryOptions` and the same fetch. Four capabilities were therefore
24
+ * unreachable from a generated client on nested routes, and `or`/`and` were
25
+ * undocumented on both.
26
+ */
27
+ const listQueryParameters = () => [
28
+ {
29
+ name: "limit",
30
+ in: "query",
31
+ schema: {
32
+ type: "integer",
33
+ default: defaultLimit,
34
+ maximum: maxLimit
35
+ },
36
+ description: "Maximum number of records to return"
37
+ },
38
+ {
39
+ name: "offset",
40
+ in: "query",
41
+ schema: {
42
+ type: "integer",
43
+ default: 0
44
+ },
45
+ description: "Number of records to skip"
46
+ },
47
+ {
48
+ name: "page",
49
+ in: "query",
50
+ schema: {
51
+ type: "integer",
52
+ minimum: 1
53
+ },
54
+ description: "Page number (alternative to offset). Calculates offset as (page-1)*limit"
55
+ },
56
+ {
57
+ name: "orderBy",
58
+ in: "query",
59
+ schema: { type: "string" },
60
+ description: "Sort field and direction. Accepts `field:asc` or `field:desc`, or a JSON array `[{\"field\":\"name\",\"direction\":\"asc\"}]`",
61
+ example: "created_at:desc"
62
+ },
63
+ {
64
+ name: "where",
65
+ in: "query",
66
+ schema: { type: "string" },
67
+ description: "JSON object filter, mapping each field to a `[operator, value]` tuple. Combines with the per-field `?field=op.value` parameters below; on the same field, the per-field parameter wins.",
68
+ example: "{\"status\":[\"==\",\"active\"]}"
69
+ },
70
+ {
71
+ name: "or",
72
+ in: "query",
73
+ schema: { type: "string" },
74
+ description: "Disjunction of conditions, AND-ed with `where` and `searchString`.",
75
+ example: "(status.eq.draft,status.eq.review)"
76
+ },
77
+ {
78
+ name: "and",
79
+ in: "query",
80
+ schema: { type: "string" },
81
+ description: "Conjunction of conditions, AND-ed with `where` and `searchString`. Ignored when `or` is also present.",
82
+ example: "(views.gte.10,status.eq.draft)"
83
+ },
84
+ {
85
+ name: "include",
86
+ in: "query",
87
+ schema: { type: "string" },
88
+ description: "Comma-separated list of relations to include (eager-load). Use `*` for all relations.",
89
+ example: "author,tags"
90
+ },
91
+ {
92
+ name: "fields",
93
+ in: "query",
94
+ schema: { type: "string" },
95
+ description: "Comma-separated list of fields to return (field selection)",
96
+ example: "id,name,created_at"
97
+ },
98
+ {
99
+ name: "searchString",
100
+ in: "query",
101
+ schema: { type: "string" },
102
+ description: "Text search. By default a case-insensitive substring match OR-ed across the collection's top-level string properties. A collection declaring a `search` block gets ranked full-text matching over the fields it names, and rows carry a `_score`."
103
+ },
104
+ {
105
+ name: "vector_search",
106
+ in: "query",
107
+ schema: { type: "string" },
108
+ description: "Name of the `vector` property to run a nearest-neighbour search against. Requires `vector`.",
109
+ example: "embedding"
110
+ },
111
+ {
112
+ name: "vector",
113
+ in: "query",
114
+ schema: { type: "string" },
115
+ description: "The query embedding, as a JSON array of numbers. Its length must match the property's declared `dimensions`.",
116
+ example: "[0.12,-0.04,0.98]"
117
+ },
118
+ {
119
+ name: "vector_distance",
120
+ in: "query",
121
+ schema: {
122
+ type: "string",
123
+ enum: [
124
+ "cosine",
125
+ "l2",
126
+ "inner_product"
127
+ ],
128
+ default: "cosine"
129
+ },
130
+ description: "Distance function used for ordering."
131
+ },
132
+ {
133
+ name: "vector_threshold",
134
+ in: "query",
135
+ schema: { type: "number" },
136
+ description: "Drop rows farther than this distance. Rows are returned closest-first with a `_distance` field."
137
+ }
138
+ ];
16
139
  const spec = {
17
140
  openapi: "3.0.3",
18
141
  info: {
@@ -94,77 +217,14 @@ function generateOpenApiSpec(collections, options = {}) {
94
217
  });
95
218
  schemas[schemaName] = buildCollectionSchema(collection);
96
219
  schemas[`${schemaName}Input`] = buildCollectionInputSchema(collection);
220
+ schemas[`${schemaName}Update`] = buildCollectionUpdateSchema(collection);
97
221
  const dataPath = `/data/${slug}`;
98
222
  paths[dataPath] = {
99
223
  get: {
100
224
  tags: [collection.name],
101
225
  summary: `List ${collection.name}`,
102
226
  operationId: `list${schemaName}`,
103
- parameters: [
104
- {
105
- name: "limit",
106
- in: "query",
107
- schema: {
108
- type: "integer",
109
- default: 20,
110
- maximum: 100
111
- },
112
- description: "Maximum number of records to return"
113
- },
114
- {
115
- name: "offset",
116
- in: "query",
117
- schema: {
118
- type: "integer",
119
- default: 0
120
- },
121
- description: "Number of records to skip"
122
- },
123
- {
124
- name: "page",
125
- in: "query",
126
- schema: {
127
- type: "integer",
128
- minimum: 1
129
- },
130
- description: "Page number (alternative to offset). Calculates offset as (page-1)*limit"
131
- },
132
- {
133
- name: "orderBy",
134
- in: "query",
135
- schema: { type: "string" },
136
- description: "Sort field and direction. Accepts `field:asc` or `field:desc`, or a JSON array `[{\"field\":\"name\",\"direction\":\"asc\"}]`",
137
- example: "created_at:desc"
138
- },
139
- {
140
- name: "where",
141
- in: "query",
142
- schema: { type: "string" },
143
- description: "JSON object filter, mapping each field to a `[operator, value]` tuple. Combines with the per-field `?field=op.value` parameters below; on the same field, the per-field parameter wins.",
144
- example: "{\"status\":[\"==\",\"active\"]}"
145
- },
146
- {
147
- name: "include",
148
- in: "query",
149
- schema: { type: "string" },
150
- description: "Comma-separated list of relations to include (eager-load). Use `*` for all relations.",
151
- example: "author,tags"
152
- },
153
- {
154
- name: "fields",
155
- in: "query",
156
- schema: { type: "string" },
157
- description: "Comma-separated list of fields to return (field selection)",
158
- example: "id,name,created_at"
159
- },
160
- {
161
- name: "searchString",
162
- in: "query",
163
- schema: { type: "string" },
164
- description: "Full-text search query"
165
- },
166
- ...buildFilterParameters(collection)
167
- ],
227
+ parameters: [...listQueryParameters(), ...buildFilterParameters(collection)],
168
228
  responses: {
169
229
  200: {
170
230
  description: "Paginated list of entities",
@@ -199,6 +259,155 @@ function generateOpenApiSpec(collections, options = {}) {
199
259
  }
200
260
  }
201
261
  };
262
+ const idempotencyHeader = {
263
+ name: "Idempotency-Key",
264
+ in: "header",
265
+ required: false,
266
+ schema: { type: "string" },
267
+ description: "Names this write so a retry is recognised instead of repeated. Without it a client that lost the response cannot distinguish a replay from a second genuine batch, and the whole batch is written twice."
268
+ };
269
+ const bulkErrors = {
270
+ 400: {
271
+ description: "Malformed body, an unknown field, or more rows than the per-batch limit",
272
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
273
+ },
274
+ 409: {
275
+ description: "A request with the same Idempotency-Key is still in flight",
276
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
277
+ },
278
+ ...errorResponses(requireAuth)
279
+ };
280
+ paths[`/data/${slug}/bulk`] = {
281
+ post: {
282
+ tags: [collection.name],
283
+ summary: `Create many ${collection.name} in one transaction`,
284
+ description: "All-or-nothing: if any row is rejected none of them land, and the error names the offending index. Every row still runs callbacks, relations and row-level security. Capped server-side because one batch holds its locks for its whole duration.",
285
+ operationId: `createMany${schemaName}`,
286
+ parameters: [idempotencyHeader],
287
+ requestBody: {
288
+ required: true,
289
+ content: { "application/json": { schema: {
290
+ type: "object",
291
+ required: ["rows"],
292
+ properties: {
293
+ rows: {
294
+ type: "array",
295
+ items: { $ref: `#/components/schemas/${schemaName}Input` }
296
+ },
297
+ upsert: {
298
+ type: "boolean",
299
+ description: "Write each row as INSERT ... ON CONFLICT DO UPDATE on the primary key."
300
+ }
301
+ }
302
+ } } }
303
+ },
304
+ responses: {
305
+ 200: {
306
+ description: "The written rows, in the order given",
307
+ content: { "application/json": { schema: {
308
+ type: "object",
309
+ properties: {
310
+ data: {
311
+ type: "array",
312
+ items: { $ref: `#/components/schemas/${schemaName}` }
313
+ },
314
+ meta: {
315
+ type: "object",
316
+ properties: { written: { type: "integer" } }
317
+ }
318
+ }
319
+ } } }
320
+ },
321
+ ...bulkErrors
322
+ }
323
+ },
324
+ patch: {
325
+ tags: [collection.name],
326
+ summary: `Update many ${collection.name} in one transaction`,
327
+ description: "Each entry names its row and the fields to change. `{ id, data }` rather than flat rows carrying their own key, because on a table keyed on something other than `id` a flat row cannot say whether a column is the address or a value to write. An id matching no row fails the batch.",
328
+ operationId: `updateMany${schemaName}`,
329
+ parameters: [idempotencyHeader],
330
+ requestBody: {
331
+ required: true,
332
+ content: { "application/json": { schema: {
333
+ type: "object",
334
+ required: ["updates"],
335
+ properties: { updates: {
336
+ type: "array",
337
+ items: {
338
+ type: "object",
339
+ required: ["id", "data"],
340
+ properties: {
341
+ id: {
342
+ type: "string",
343
+ description: "The row to update"
344
+ },
345
+ data: { $ref: `#/components/schemas/${schemaName}Update` }
346
+ }
347
+ }
348
+ } }
349
+ } } }
350
+ },
351
+ responses: {
352
+ 200: {
353
+ description: "The updated rows, in the order given",
354
+ content: { "application/json": { schema: {
355
+ type: "object",
356
+ properties: {
357
+ data: {
358
+ type: "array",
359
+ items: { $ref: `#/components/schemas/${schemaName}` }
360
+ },
361
+ meta: {
362
+ type: "object",
363
+ properties: { written: { type: "integer" } }
364
+ }
365
+ }
366
+ } } }
367
+ },
368
+ 404: {
369
+ description: "One of the ids matches no row; nothing was written",
370
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
371
+ },
372
+ ...bulkErrors
373
+ }
374
+ }
375
+ };
376
+ paths[`/data/${slug}/bulk/delete`] = { post: {
377
+ tags: [collection.name],
378
+ summary: `Delete many ${collection.name} in one transaction`,
379
+ description: "A POST, not `DELETE /bulk` with a body. Bodies on DELETE are permitted but widely dropped by proxies and CDNs, and several generators ignore `requestBody` on a DELETE operation — a generated client would send the request with no ids at all. Takes ids rather than a filter: a mistyped condition that empties a table cannot be reviewed at the call site the way an explicit list can. `beforeDelete`/`afterDelete` fire per row.",
380
+ operationId: `deleteMany${schemaName}`,
381
+ parameters: [idempotencyHeader],
382
+ requestBody: {
383
+ required: true,
384
+ content: { "application/json": { schema: {
385
+ type: "object",
386
+ required: ["ids"],
387
+ properties: { ids: {
388
+ type: "array",
389
+ items: { oneOf: [{ type: "string" }, { type: "integer" }] }
390
+ } }
391
+ } } }
392
+ },
393
+ responses: {
394
+ 200: {
395
+ description: "How many rows were deleted",
396
+ content: { "application/json": { schema: {
397
+ type: "object",
398
+ properties: { meta: {
399
+ type: "object",
400
+ properties: { deleted: { type: "integer" } }
401
+ } }
402
+ } } }
403
+ },
404
+ 404: {
405
+ description: "One of the ids matches no row; nothing was deleted",
406
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
407
+ },
408
+ ...bulkErrors
409
+ }
410
+ } };
202
411
  const entityPath = `/data/${slug}/{id}`;
203
412
  paths[entityPath] = {
204
413
  get: {
@@ -230,32 +439,11 @@ function generateOpenApiSpec(collections, options = {}) {
230
439
  ...errorResponses(requireAuth)
231
440
  }
232
441
  },
442
+ patch: updateOperation(collection, schemaName, requireAuth),
233
443
  put: {
234
- tags: [collection.name],
235
- summary: `Update ${collection.singularName || collection.name}`,
236
- operationId: `update${schemaName}`,
237
- parameters: [{
238
- name: "id",
239
- in: "path",
240
- required: true,
241
- schema: { type: "string" },
242
- description: "Entity ID"
243
- }],
244
- requestBody: {
245
- required: true,
246
- content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaName}Input` } } }
247
- },
248
- responses: {
249
- 200: {
250
- description: "Updated entity",
251
- content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaName}` } } }
252
- },
253
- 404: {
254
- description: "Entity not found",
255
- content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
256
- },
257
- ...errorResponses(requireAuth)
258
- }
444
+ ...updateOperation(collection, schemaName, requireAuth),
445
+ operationId: `update${schemaName}ViaPut`,
446
+ deprecated: true
259
447
  },
260
448
  delete: {
261
449
  tags: [collection.name],
@@ -292,41 +480,13 @@ function generateOpenApiSpec(collections, options = {}) {
292
480
  tags: [collection.name],
293
481
  summary: `List ${relationName} for ${withIndefiniteArticle(collection.singularName || collection.name)}`,
294
482
  operationId: `list${schemaName}${toPascalCase(relationName)}`,
295
- parameters: [
296
- {
297
- name: "parentId",
298
- in: "path",
299
- required: true,
300
- schema: { type: "string" },
301
- description: `${collection.singularName || collection.name} ID`
302
- },
303
- {
304
- name: "limit",
305
- in: "query",
306
- schema: {
307
- type: "integer",
308
- default: 20
309
- }
310
- },
311
- {
312
- name: "offset",
313
- in: "query",
314
- schema: {
315
- type: "integer",
316
- default: 0
317
- }
318
- },
319
- {
320
- name: "orderBy",
321
- in: "query",
322
- schema: { type: "string" }
323
- },
324
- {
325
- name: "searchString",
326
- in: "query",
327
- schema: { type: "string" }
328
- }
329
- ],
483
+ parameters: [{
484
+ name: "parentId",
485
+ in: "path",
486
+ required: true,
487
+ schema: { type: "string" },
488
+ description: `${collection.singularName || collection.name} ID`
489
+ }, ...listQueryParameters()],
330
490
  responses: {
331
491
  200: {
332
492
  description: `List of related ${relationName}`,
@@ -370,6 +530,58 @@ function buildCollectionSchema(collection) {
370
530
  };
371
531
  }
372
532
  /**
533
+ * The PATCH/PUT operation for `/data/{slug}/{id}`.
534
+ *
535
+ * Split out because both verbs serve it and they must not drift: the update
536
+ * body is a **partial**, and describing it with the create schema was the bug
537
+ * this replaces. `<Name>Input` marks every `validation.required` property as
538
+ * required — correct for POST, wrong for an update, where omitting a field
539
+ * means "leave it alone" rather than "I forgot it". A client generated from
540
+ * that spec demanded fields the server does not, and a spec-validating gateway
541
+ * would have rejected partial updates the server accepts.
542
+ */
543
+ function updateOperation(collection, schemaName, requireAuth) {
544
+ return {
545
+ tags: [collection.name],
546
+ summary: `Update ${collection.singularName || collection.name}`,
547
+ description: "Partial update: only the properties present in the body are written; the rest are left unchanged.",
548
+ operationId: `update${schemaName}`,
549
+ parameters: [{
550
+ name: "id",
551
+ in: "path",
552
+ required: true,
553
+ schema: { type: "string" },
554
+ description: "Entity ID"
555
+ }],
556
+ requestBody: {
557
+ required: true,
558
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaName}Update` } } }
559
+ },
560
+ responses: {
561
+ 200: {
562
+ description: "Updated entity",
563
+ content: { "application/json": { schema: { $ref: `#/components/schemas/${schemaName}` } } }
564
+ },
565
+ 404: {
566
+ description: "Entity not found",
567
+ content: { "application/json": { schema: { $ref: "#/components/schemas/ErrorResponse" } } }
568
+ },
569
+ ...errorResponses(requireAuth)
570
+ }
571
+ };
572
+ }
573
+ /**
574
+ * The update body: the create schema with `required` dropped.
575
+ *
576
+ * Derived rather than rebuilt so the two cannot describe different columns —
577
+ * the only difference between creating and updating is which fields you must
578
+ * supply, and that is exactly the one thing removed here.
579
+ */
580
+ function buildCollectionUpdateSchema(collection) {
581
+ const { required: _required, ...rest } = buildCollectionInputSchema(collection);
582
+ return rest;
583
+ }
584
+ /**
373
585
  * Build an input schema (for POST/PUT) — excludes auto-generated fields.
374
586
  */
375
587
  function buildCollectionInputSchema(collection) {
@@ -594,4 +806,4 @@ function toPascalCase(str) {
594
806
  //#endregion
595
807
  export { generateOpenApiSpec };
596
808
 
597
- //# sourceMappingURL=openapi-generator-BEwyiaAr.js.map
809
+ //# sourceMappingURL=openapi-generator-VB0MUH8h.js.map