spine-framework 0.3.113 → 0.3.114

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.
@@ -1,3 +1,4 @@
1
+ // ─── CHUNK_START: ADMIN_DATA_OVERVIEW ──────────────────────────────────────────────
1
2
  /**
2
3
  * @module admin-data
3
4
  * @audience both
@@ -46,8 +47,38 @@ import { sanitizeRecordData } from './_shared/permissions'
46
47
  import { adminDb } from './_shared/db'
47
48
  import { resolveTypeId } from './_shared/resolve-ids'
48
49
  import { fireCreateTriggers, fireUpdateTriggers, fireDeleteTriggers } from './_shared/trigger-engine'
50
+ // ─── CHUNK_END: ADMIN_DATA_OVERVIEW ──────────────────────────────────────────────
49
51
 
50
- // ─── CONSTANTS ────────────────────────────────────────────────────────────────
52
+ // ─── CHUNK_START: ADMIN_DATA_MODULE_HEADER ──────────────────────────────────────────────
53
+ /**
54
+ * @chunk-id ADMIN_DATA_MODULE_HEADER_1_0_0
55
+ * @version 1.0.0
56
+ * @hash TBD
57
+ * @macro Module Header
58
+ * @micro Module-level documentation with routing table and invariants
59
+ * @inputs none
60
+ * @outputs none
61
+ * @depends-on [none]
62
+ * @depended-by [all chunks]
63
+ * @side-effects [none]
64
+ * @tags admin, module, routing, invariants
65
+ */
66
+ // ─── CHUNK_END: ADMIN_DATA_MODULE_HEADER ────────────────────────────────────────────────
67
+
68
+ // ─── CHUNK_START: ADMIN_DATA_IMPORTS_AND_CONSTANTS ──────────────────────────────────────────────
69
+ /**
70
+ * @chunk-id ADMIN_DATA_IMPORTS_AND_CONSTANTS_1_0_0
71
+ * @version 1.0.0
72
+ * @hash TBD
73
+ * @macro Imports and Constants
74
+ * @micro Module imports and constant definitions
75
+ * @inputs none
76
+ * @outputs none
77
+ * @depends-on [none]
78
+ * @depended-by [all handlers]
79
+ * @side-effects [none]
80
+ * @tags admin, imports, constants
81
+ */
51
82
 
52
83
  const PERMISSIONS_ALL = {
53
84
  record_permissions: { all: ['create', 'read', 'update', 'delete'] },
@@ -70,8 +101,23 @@ const ENTITY_TO_KIND: Record<string, string> = {
70
101
  watchers: 'watcher',
71
102
  item_progress: 'item_progress'
72
103
  }
104
+ // ─── CHUNK_END: ADMIN_DATA_IMPORTS_AND_CONSTANTS ────────────────────────────────────────────────
73
105
 
74
- // ─── HANDLERS ─────────────────────────────────────────────────────────────────
106
+ // ─── CHUNK_START: ADMIN_DATA_HANDLERS_SECTION ──────────────────────────────────────────────
107
+ /**
108
+ * @chunk-id ADMIN_DATA_HANDLERS_SECTION_1_0_0
109
+ * @version 1.0.0
110
+ * @hash TBD
111
+ * @macro Handlers Section Header
112
+ * @micro Section marker for CRUD handler functions
113
+ * @inputs none
114
+ * @outputs none
115
+ * @depends-on [none]
116
+ * @depended-by [none]
117
+ * @side-effects [none]
118
+ * @tags admin, section, handlers
119
+ */
120
+ // ─── CHUNK_END: ADMIN_DATA_HANDLERS_SECTION ────────────────────────────────────────────────
75
121
 
76
122
  // ─── CHUNK_START: ADMIN_DATA_LIST ──────────────────────────────────────────────
77
123
  /**
@@ -230,32 +276,20 @@ export const get = createHandler(async (ctx, _body) => {
230
276
  })
231
277
  // ─── CHUNK_END: ADMIN_DATA_GET ────────────────────────────────────────────────
232
278
 
279
+ // ─── CHUNK_START: ADMIN_DATA_CREATE ──────────────────────────────────────────────
233
280
  /**
234
- * Creates a new record for an entity. Stamps `design_schema`,
235
- * `validation_schema`, audit fields, and `account_id` from the resolved type.
236
- * Fires `*_created` triggers asynchronously after DB insert.
237
- *
238
- * Body params:
239
- * - `entity` (required) — one of VALID_ENTITIES
240
- * - `type_id` (required) — UUID of an active type record
241
- * - all other fields are passed through to the insert
242
- *
243
- * @returns Sanitized created record
244
- * @throws Error('Valid entity parameter is required')
245
- * @throws Error('type_id is required')
246
- * @throws Error('type_id not found') — if type UUID doesn't exist
247
- * @throws Error('type_id references an inactive type')
248
- * @throws PostgREST error on RLS INSERT denial
249
- * @inputSpec type_id: string — valid UUID of active type record
250
- * @inputSpec body fields: Record<string, any> — record field values
251
- * @outputSpec sanitized created record
252
- * @sideEffects DB write: entity table (INSERT)
253
- * @sideEffects DB read: types table (type resolution)
254
- * @sideEffects fire-and-forget: fireCreateTriggers
255
- * @calledBy handler (POST)
256
- * @calls sanitizeRecordData, adminDb (type lookup), fireCreateTriggers
257
- * @testUnit tests/unit/admin-data.test.ts — 'create'
258
- * @testIntegration tests/integration/admin-data.test.ts — 'create'
281
+ * @chunk-id ADMIN_DATA_CREATE_1_0_0
282
+ * @version 1.0.0
283
+ * @hash TBD
284
+ * @macro Entity Create Handler
285
+ * @micro Creates a new record for an entity with type resolution and triggers
286
+ * @inputs ctx: CoreContext — Request context with principal and database
287
+ * @inputs body: any — Request body with entity, type_id/type_slug, and record fields
288
+ * @outputs Sanitized created record
289
+ * @depends-on [createHandler, sanitizeRecordData, adminDb, resolveTypeId, fireCreateTriggers]
290
+ * @depended-by [Netlify function routing]
291
+ * @side-effects [DB write, type lookup, async trigger fire]
292
+ * @tags admin, crud, create, type-resolution
259
293
  */
260
294
  export const create = createHandler(async (ctx, body) => {
261
295
  const entity = body?.entity || ctx.query?.entity
@@ -361,31 +395,27 @@ export const create = createHandler(async (ctx, body) => {
361
395
 
362
396
  return await sanitizeRecordData(ctx, data, entity)
363
397
  })
398
+ // ─── CHUNK_END: ADMIN_DATA_CREATE ────────────────────────────────────────────────
364
399
 
400
+ // ─── CHUNK_START: ADMIN_DATA_UPDATE ──────────────────────────────────────────────
365
401
  /**
366
- * Updates an existing record by ID. Stamps `updated_by` and `updated_at`.
367
- * Fires `*_updated` triggers asynchronously after DB update.
368
- *
369
- * Query params: `entity` (required), `id` (required)
370
- * Body: partial record fields to update (no schema re-stamping on update)
371
- *
372
- * @returns Sanitized updated record
373
- * @throws Error('Valid entity parameter is required')
374
- * @throws Error('ID is required for update')
375
- * @throws PostgREST error on RLS UPDATE denial
376
- * @inputSpec id: string — valid UUID of existing record
377
- * @inputSpec body: Partial<Record> — fields to patch
378
- * @outputSpec sanitized updated record
379
- * @sideEffects DB write: entity table (UPDATE)
380
- * @sideEffects fire-and-forget: fireUpdateTriggers
381
- * @calledBy handler (PATCH)
382
- * @calls sanitizeRecordData, fireUpdateTriggers
383
- * @testUnit tests/unit/admin-data.test.ts — 'update'
402
+ * @chunk-id ADMIN_DATA_UPDATE_1_0_0
403
+ * @version 1.0.0
404
+ * @hash TBD
405
+ * @macro Entity Update Handler
406
+ * @micro Updates an existing record by ID with audit fields and triggers
407
+ * @inputs ctx: CoreContext — Request context with principal and database
408
+ * @inputs body: any — Request body with partial record fields to update
409
+ * @outputs Sanitized updated record
410
+ * @depends-on [createHandler, sanitizeRecordData, fireUpdateTriggers]
411
+ * @depended-by [Netlify function routing]
412
+ * @side-effects [DB write, async trigger fire]
413
+ * @tags admin, crud, update
384
414
  */
385
415
  export const update = createHandler(async (ctx, body) => {
386
416
  const { entity, id } = ctx.query || {}
387
417
  // Strip server-only fields — client must never override these on update
388
- const { design_schema: _ds, validation_schema: _vs, account_id: _ai, type_id: _ti, ...recordData } = body || {}
418
+ const { design_schema: _ds, validation_schema: _vs, account_id: _ai, type_id: _ti, type_slug: _ts, ...recordData } = body || {}
389
419
 
390
420
  if (!entity || !VALID_ENTITIES.includes(entity)) {
391
421
  throw new Error('Valid entity parameter is required')
@@ -417,31 +447,22 @@ export const update = createHandler(async (ctx, body) => {
417
447
 
418
448
  return await sanitizeRecordData(ctx, data, entity)
419
449
  })
450
+ // ─── CHUNK_END: ADMIN_DATA_UPDATE ────────────────────────────────────────────────
420
451
 
452
+ // ─── CHUNK_START: ADMIN_DATA_REMOVE ──────────────────────────────────────────────
421
453
  /**
422
- * Deletes a record by ID. Defaults to soft delete (`is_active = false`) for
423
- * entities that support it; falls back to hard delete otherwise.
424
- *
425
- * Query params:
426
- * - `entity` (required) — one of VALID_ENTITIES
427
- * - `id` (required) — UUID of the record
428
- * - `soft` (default: 'true') — set to 'false' to force hard delete
429
- *
430
- * Soft-delete-capable entities: `accounts`, `people`, `items`, `threads`,
431
- * `messages`, `watchers`. All others always receive a hard delete.
432
- *
433
- * @returns `{ deleted: true, soft: true, data }` (soft) or `{ deleted: true, soft: false }` (hard)
434
- * @throws Error('Valid entity parameter is required')
435
- * @throws Error('ID is required for delete')
436
- * @throws PostgREST error on RLS DELETE denial
437
- * @inputSpec id: string — valid UUID
438
- * @inputSpec soft: 'true' | 'false'
439
- * @outputSpec { deleted: boolean, soft: boolean, data?: sanitizedRecord }
440
- * @sideEffects DB write: UPDATE is_active=false (soft) or DELETE (hard)
441
- * @sideEffects fire-and-forget: fireDeleteTriggers
442
- * @calledBy handler (DELETE)
443
- * @calls sanitizeRecordData, entitySupportsSoftDelete, fireDeleteTriggers
444
- * @testUnit tests/unit/admin-data.test.ts — 'remove'
454
+ * @chunk-id ADMIN_DATA_REMOVE_1_0_0
455
+ * @version 1.0.0
456
+ * @hash TBD
457
+ * @macro Entity Delete Handler
458
+ * @micro Deletes a record by ID with soft delete support and triggers
459
+ * @inputs ctx: CoreContext — Request context with principal and database
460
+ * @inputs _body: any — Request body (unused for DELETE)
461
+ * @outputs { deleted: boolean, soft: boolean, data?: sanitizedRecord }
462
+ * @depends-on [createHandler, sanitizeRecordData, entitySupportsSoftDelete, fireDeleteTriggers]
463
+ * @depended-by [Netlify function routing]
464
+ * @side-effects [DB write, async trigger fire]
465
+ * @tags admin, crud, delete, soft-delete
445
466
  */
446
467
  export const remove = createHandler(async (ctx, _body) => {
447
468
  const { entity, id, soft = 'true' } = ctx.query || {}
@@ -482,15 +503,24 @@ export const remove = createHandler(async (ctx, _body) => {
482
503
  return { deleted: true, soft: false }
483
504
  }
484
505
  })
506
+ // ─── CHUNK_END: ADMIN_DATA_REMOVE ────────────────────────────────────────────────
485
507
 
486
- // ─── HELPERS ─────────────────────────────────────────────────────────────────
487
-
508
+ // ─── CHUNK_START: ADMIN_DATA_HELPERS_SECTION ──────────────────────────────────────────────
488
509
  /**
489
- * Returns the primary text field to use for `ilike` search for a given entity.
490
- * Falls back to 'id' for unmapped entities.
491
- *
492
- * @calledBy list (search param handling)
510
+ * @chunk-id ADMIN_DATA_HELPERS_SECTION_1_0_0
511
+ * @version 1.0.0
512
+ * @hash TBD
513
+ * @macro Helpers Section Header
514
+ * @micro Section marker for helper functions
515
+ * @inputs none
516
+ * @outputs none
517
+ * @depends-on [none]
518
+ * @depended-by [none]
519
+ * @side-effects [none]
520
+ * @tags admin, section, helpers
493
521
  */
522
+ // ─── CHUNK_END: ADMIN_DATA_HELPERS_SECTION ────────────────────────────────────────────────
523
+
494
524
  // ─── CHUNK_START: ADMIN_DATA_GET_SEARCH_FIELD ──────────────────────────────────────────────
495
525
  /**
496
526
  * @chunk-id ADMIN_DATA_GET_SEARCH_FIELD_1_0_0
@@ -573,7 +603,21 @@ export const stats = createHandler(async (ctx, _body) => {
573
603
  })
574
604
  // ─── CHUNK_END: ADMIN_DATA_STATS ────────────────────────────────────────────────
575
605
 
576
- // ─── MAIN HANDLER ────────────────────────────────────────────────────────────
606
+ // ─── CHUNK_START: ADMIN_DATA_MAIN_HANDLER_SECTION ──────────────────────────────────────────────
607
+ /**
608
+ * @chunk-id ADMIN_DATA_MAIN_HANDLER_SECTION_1_0_0
609
+ * @version 1.0.0
610
+ * @hash TBD
611
+ * @macro Main Handler Section Header
612
+ * @micro Section marker for the main router handler
613
+ * @inputs none
614
+ * @outputs none
615
+ * @depends-on [none]
616
+ * @depended-by [none]
617
+ * @side-effects [none]
618
+ * @tags admin, section, main-handler
619
+ */
620
+ // ─── CHUNK_END: ADMIN_DATA_MAIN_HANDLER_SECTION ────────────────────────────────────────────────
577
621
 
578
622
  // ─── CHUNK_START: ADMIN_DATA_HANDLER ──────────────────────────────────────────────
579
623
  /**
@@ -626,4 +670,4 @@ export const handler = createHandler(async (ctx, body) => {
626
670
 
627
671
  throw new Error('Invalid action or method')
628
672
  })
629
- // ─── CHUNK_END: ADMIN_DATA_HANDLER ────────────────────────────────────────────────
673
+ // ─── CHUNK_END: ADMIN_DATA_HANDLER ────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spine-framework",
3
- "version": "0.3.113",
3
+ "version": "0.3.114",
4
4
  "description": "Multi-tenant, modular application platform for modern SaaS systems",
5
5
  "type": "module",
6
6
  "bin": {