primitive-admin 1.0.55 → 1.0.57

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 (92) hide show
  1. package/assets/skill/skills/primitive-platform/SKILL.md +230 -2
  2. package/dist/bin/primitive.js +1 -1
  3. package/dist/src/commands/database-types.js +4 -4
  4. package/dist/src/commands/database-types.js.map +1 -1
  5. package/dist/src/commands/databases.js +5 -5
  6. package/dist/src/commands/databases.js.map +1 -1
  7. package/dist/src/commands/documents.js +2 -2
  8. package/dist/src/commands/documents.js.map +1 -1
  9. package/dist/src/commands/guides.d.ts +15 -12
  10. package/dist/src/commands/guides.js +22 -15
  11. package/dist/src/commands/guides.js.map +1 -1
  12. package/dist/src/commands/metadata.js +48 -3
  13. package/dist/src/commands/metadata.js.map +1 -1
  14. package/dist/src/commands/scripts.d.ts +18 -0
  15. package/dist/src/commands/scripts.js +59 -5
  16. package/dist/src/commands/scripts.js.map +1 -1
  17. package/dist/src/commands/sync-app-settings.d.ts +14 -4
  18. package/dist/src/commands/sync-app-settings.js +48 -8
  19. package/dist/src/commands/sync-app-settings.js.map +1 -1
  20. package/dist/src/commands/sync.d.ts +100 -0
  21. package/dist/src/commands/sync.js +1578 -402
  22. package/dist/src/commands/sync.js.map +1 -1
  23. package/dist/src/commands/workflows.js +23 -6
  24. package/dist/src/commands/workflows.js.map +1 -1
  25. package/dist/src/lib/api-client.d.ts +27 -1
  26. package/dist/src/lib/api-client.js +25 -2
  27. package/dist/src/lib/api-client.js.map +1 -1
  28. package/dist/src/lib/app-settings-descriptor.js +4 -0
  29. package/dist/src/lib/app-settings-descriptor.js.map +1 -1
  30. package/dist/src/lib/codegen-shared/generatedFiles.d.ts +21 -0
  31. package/dist/src/lib/codegen-shared/generatedFiles.js +40 -0
  32. package/dist/src/lib/codegen-shared/generatedFiles.js.map +1 -1
  33. package/dist/src/lib/codegen-shared/prettierStable.d.ts +262 -0
  34. package/dist/src/lib/codegen-shared/prettierStable.js +610 -0
  35. package/dist/src/lib/codegen-shared/prettierStable.js.map +1 -0
  36. package/dist/src/lib/db-codegen/dbGenerator.js +8 -5
  37. package/dist/src/lib/db-codegen/dbGenerator.js.map +1 -1
  38. package/dist/src/lib/db-codegen/dbNaming.d.ts +5 -0
  39. package/dist/src/lib/db-codegen/dbNaming.js +8 -2
  40. package/dist/src/lib/db-codegen/dbNaming.js.map +1 -1
  41. package/dist/src/lib/db-codegen/dbTemplates.d.ts +17 -2
  42. package/dist/src/lib/db-codegen/dbTemplates.js +85 -40
  43. package/dist/src/lib/db-codegen/dbTemplates.js.map +1 -1
  44. package/dist/src/lib/db-codegen/dbTsTypes.d.ts +13 -22
  45. package/dist/src/lib/db-codegen/dbTsTypes.js +33 -28
  46. package/dist/src/lib/db-codegen/dbTsTypes.js.map +1 -1
  47. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.d.ts +11 -0
  48. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js +11 -0
  49. package/dist/src/lib/db-codegen/generated-operation-def-descriptor.js.map +1 -1
  50. package/dist/src/lib/generated-allowlist.js +7 -0
  51. package/dist/src/lib/generated-allowlist.js.map +1 -1
  52. package/dist/src/lib/platform-owned.d.ts +37 -0
  53. package/dist/src/lib/platform-owned.js +46 -0
  54. package/dist/src/lib/platform-owned.js.map +1 -0
  55. package/dist/src/lib/swift-codegen/dbGenerator.d.ts +32 -0
  56. package/dist/src/lib/swift-codegen/dbGenerator.js +338 -36
  57. package/dist/src/lib/swift-codegen/dbGenerator.js.map +1 -1
  58. package/dist/src/lib/swift-codegen/dbSwiftTypes.d.ts +1 -1
  59. package/dist/src/lib/swift-codegen/dbSwiftTypes.js +1 -1
  60. package/dist/src/lib/swift-codegen/generator.d.ts +1 -0
  61. package/dist/src/lib/swift-codegen/generator.js +91 -3
  62. package/dist/src/lib/swift-codegen/generator.js.map +1 -1
  63. package/dist/src/lib/swift-codegen/siblingSymbols.d.ts +90 -0
  64. package/dist/src/lib/swift-codegen/siblingSymbols.js +152 -0
  65. package/dist/src/lib/swift-codegen/siblingSymbols.js.map +1 -0
  66. package/dist/src/lib/sync-resource-types.d.ts +222 -10
  67. package/dist/src/lib/sync-resource-types.js +407 -20
  68. package/dist/src/lib/sync-resource-types.js.map +1 -1
  69. package/dist/src/lib/template.d.ts +1 -1
  70. package/dist/src/lib/template.js +6 -3
  71. package/dist/src/lib/template.js.map +1 -1
  72. package/dist/src/lib/toml-metadata-config.js +18 -0
  73. package/dist/src/lib/toml-metadata-config.js.map +1 -1
  74. package/dist/src/lib/workflow-apply.js +12 -2
  75. package/dist/src/lib/workflow-apply.js.map +1 -1
  76. package/dist/src/lib/workflow-codegen/generator.d.ts +9 -0
  77. package/dist/src/lib/workflow-codegen/generator.js +126 -55
  78. package/dist/src/lib/workflow-codegen/generator.js.map +1 -1
  79. package/dist/src/lib/workflow-codegen/invokerIR.d.ts +6 -8
  80. package/dist/src/lib/workflow-codegen/invokerIR.js +8 -8
  81. package/dist/src/lib/workflow-codegen/invokerIR.js.map +1 -1
  82. package/dist/src/lib/workflow-codegen/schemaToTs.d.ts +17 -1
  83. package/dist/src/lib/workflow-codegen/schemaToTs.js +59 -38
  84. package/dist/src/lib/workflow-codegen/schemaToTs.js.map +1 -1
  85. package/dist/src/lib/workflow-payload.d.ts +19 -0
  86. package/dist/src/lib/workflow-payload.js +21 -0
  87. package/dist/src/lib/workflow-payload.js.map +1 -1
  88. package/dist/src/lib/workflow-toml-validator.d.ts +30 -0
  89. package/dist/src/lib/workflow-toml-validator.js +206 -0
  90. package/dist/src/lib/workflow-toml-validator.js.map +1 -1
  91. package/dist/src/types/index.d.ts +1 -0
  92. package/package.json +2 -1
@@ -14,6 +14,7 @@
14
14
  * — this table is layout metadata, not a write framework.
15
15
  */
16
16
  import type { SyncState } from "../types/index.js";
17
+ import type { ApiClient } from "./api-client.js";
17
18
  export type SyncEntitySlot = keyof SyncState["entities"];
18
19
  export interface SyncResourceType {
19
20
  /** Singular name used in pull/diff output, e.g. `"rule-set"`. */
@@ -40,17 +41,31 @@ export interface SyncResourceType {
40
41
  * listing" really means "gone from the server".
41
42
  *
42
43
  * Prune deletes files, so a successful fetch is not enough — the response also
43
- * has to be complete. Only the four listings below paginate (server returns
44
- * `nextCursor`, and `sync pull` drains it with `fetchAll`). The rest call
45
- * `queryByIndex` with no cursor and return a single DynamoDB page (~1MB), so
46
- * a big enough app gets a truncated list with a 200 and no error: the entities
47
- * past the page boundary would look deleted and prune would remove the files
48
- * of entities that are still live. `cron-triggers` is the sharpest case a
49
- * hard `{ limit: 100 }` with the archived filter applied after the query, so
50
- * its effective ceiling is below 100 rows.
44
+ * has to be complete. Two shapes make a listing complete: the cursor-paginating
45
+ * admin endpoints (integrations/webhooks/prompts/workflows server returns
46
+ * `nextCursor` and `sync pull` drains it with `fetchAll`), and the config
47
+ * listings that drain every DynamoDB page server-side via `queryAllPages`
48
+ * (#1668). Before #1668 the config listings returned a single 100-row page with
49
+ * a 200 and no cursor, so an app past the page boundary got a silently
50
+ * truncated list and prune would have removed files of still-live entities;
51
+ * those types were held at `false`. The server now drains them, so all shipping
52
+ * types are complete.
51
53
  *
52
- * Those types are skipped for pruning until their listings are complete
53
- * (#1668). Flip this to `true` per type as each listing learns to paginate.
54
+ * Version-skew assumption (accepted risk): setting a config type `true` assumes
55
+ * the target server carries #1668's drain. The CLI can target an independently
56
+ * versioned environment, so a new CLI against a pre-#1668 server still gets a
57
+ * silently 100-row-truncated listing and prune would delete local files for
58
+ * still-live entities past the page boundary. This is accepted per the sponsor
59
+ * ruling on PR #1689 / issue #1668 rather than gated on a server capability or
60
+ * version marker: the CLI npm publish is coupled to the production deploy (so
61
+ * new-CLI-vs-old-server is a narrow window), the worst case is git-recoverable
62
+ * local file deletion touching nothing server-side, and it only affects apps
63
+ * with >100 entities of one config type — whose `sync pull` was already
64
+ * silently incomplete before this change.
65
+ *
66
+ * The flag remains as a guard: a future type whose listing is not yet complete
67
+ * ships `false` and prune skips it (`listing-incomplete`) until its server side
68
+ * drains.
54
69
  */
55
70
  listingComplete: boolean;
56
71
  }
@@ -223,3 +238,200 @@ export declare function applyPrune(options: {
223
238
  }>;
224
239
  logger?: (msg: string) => void;
225
240
  }): Promise<PruneResult>;
241
+ /** Address a prior-state entry for detail-fetch and delete. */
242
+ export type PruneAddressOf = (key: string, entry: any) => string | undefined;
243
+ /** Read `id` from an id-addressed slot's state entry. */
244
+ export declare const addressById: PruneAddressOf;
245
+ /** Key-addressed slots are addressed by the state key itself. */
246
+ export declare const addressByKey: PruneAddressOf;
247
+ /**
248
+ * Live modified timestamp used by the drift check. Most admin/app detail
249
+ * endpoints serialize `modifiedAt` at the top level (the same field `sync pull`
250
+ * records into state), with `updatedAt` as a defensive fallback. Two detail
251
+ * endpoints nest the entity under a wrapper key and need their own extractors
252
+ * below (`workflowPruneModifiedAt`, `emailTemplatePruneModifiedAt`) so the
253
+ * drift check reads the real timestamp instead of `undefined` — a top-level
254
+ * read there always looks like drift and blocks `--prune` without `--force`.
255
+ */
256
+ export declare const pruneModifiedAt: (detail: any) => string | undefined;
257
+ /**
258
+ * Workflow detail-read extractor. `getWorkflow` (server `getAppWorkflow`,
259
+ * src/admin-api.ts) returns `{ workflow, draft, revisions, configs }` — the
260
+ * entity and its `modifiedAt` are nested under `workflow`, unlike every other
261
+ * detail endpoint. Read there, not the top level.
262
+ */
263
+ export declare const workflowPruneModifiedAt: (detail: any) => string | undefined;
264
+ /**
265
+ * Email-template detail-read extractor. `getEmailTemplate` returns
266
+ * `{ emailType, label, hasOverride, override, default, variables }`; the
267
+ * editable copy `sync` manages — and its `modifiedAt` — live under `override`
268
+ * (null when there is no override). `sync pull` likewise records
269
+ * `template.override?.modifiedAt`, so the drift check must read the same path.
270
+ */
271
+ export declare const emailTemplatePruneModifiedAt: (detail: any) => string | undefined;
272
+ /**
273
+ * How to confirm and delete one resource type during push-prune. Narrow by
274
+ * design (state slot + address + detail-fetch + drift field + delete) rather
275
+ * than widening `SyncResourceType`, which is pull/diff layout metadata.
276
+ */
277
+ export interface PushPruneSpec {
278
+ /** The layout descriptor (dir/ext/fileFromKey/stateSlot/label/testBlockType). */
279
+ resourceType: SyncResourceType;
280
+ /**
281
+ * Derive the entity key a local managed file declares — the SAME derivation
282
+ * the type's `sync push` loop uses (`<in-file identity> || basename`). Prune
283
+ * collects the set of these across every file in the type's directory and
284
+ * treats a prior-state key as a candidate only when NO local file declares
285
+ * it. This must mirror push exactly: a file renamed but keeping its internal
286
+ * key is still updated by push, so it must NOT be seen as a delete (#1655).
287
+ */
288
+ keyFromFile: (filePath: string) => string;
289
+ /** Address a prior-state entry for the point read and delete. */
290
+ addressOf: PruneAddressOf;
291
+ /** Point-read the live entity; rejects with `ApiError` status 404 when gone. */
292
+ fetchDetail: (client: ApiClient, appId: string, address: string) => Promise<any>;
293
+ /** Live `modifiedAt` for the drift check. */
294
+ modifiedAtOf: (detail: any) => string | undefined;
295
+ /**
296
+ * Delete the confirmed entity; rejects with `ConflictError` (409) when the
297
+ * entity is still referenced. `null` for a type whose CLI delete surface has
298
+ * not landed yet (metadata-category-configs, gated on #1426): a candidate of
299
+ * such a type is reported "no delete surface yet" rather than deleted.
300
+ */
301
+ deleteEntity: ((client: ApiClient, appId: string, address: string) => Promise<void>) | null;
302
+ }
303
+ /**
304
+ * Every resource type push-prune can retire. Id-addressed types (integrations,
305
+ * webhooks, cron-triggers, blob-buckets, prompts, workflows, transforms,
306
+ * rule-sets) fetch/delete by the stored id; key-addressed types (database-types,
307
+ * group-type-configs, collection-type-configs, email-templates) by the key.
308
+ *
309
+ * `--force` (the CLI's existing "override baseline drift" flag) only relaxes the
310
+ * drift check — it deliberately does NOT pass a server-side force flag on the
311
+ * delete, so each type's existing 409 dangling-ref guard stays active and a
312
+ * still-referenced entity is reported "blocked", not force-deleted.
313
+ */
314
+ export declare const PUSH_PRUNE_SPECS: PushPruneSpec[];
315
+ /**
316
+ * The set of entity keys the local managed files of a type declare — collected
317
+ * the way `sync push` derives keys: parse every file in the type's directory
318
+ * and read its declared identity (`<in-file key> || basename`), NOT by assuming
319
+ * the file lives at its canonical `<key>.toml` path.
320
+ *
321
+ * This is the crux of the #1655 destructive-delete fix. `sync push` accepts a
322
+ * managed file at a **noncanonical filename** (e.g. `prompts/custom.toml` with
323
+ * `prompt.key = "greeting"`) and still updates the `greeting` entity. Collecting
324
+ * declared keys — rather than checking whether `greeting.toml` exists — means a
325
+ * renamed-but-same-key file keeps its entity out of the prune set, so prune
326
+ * never deletes an entity push just updated.
327
+ */
328
+ export declare function collectLocalDeclaredKeys(input: {
329
+ spec: PushPruneSpec;
330
+ configDir: string;
331
+ }): Set<string>;
332
+ /**
333
+ * A managed entity whose local declaration is gone — a candidate for
334
+ * server-side deletion. Pure and side-effect-free: computed from the prior sync
335
+ * state and the filesystem only (no server call), so it is immune to listing
336
+ * truncation.
337
+ *
338
+ * A key is a candidate iff it was in the prior state slot (a prior pull managed
339
+ * it) AND **no** local file in the type's directory declares that key (via
340
+ * {@link collectLocalDeclaredKeys}, mirroring how `sync push` derives keys). A
341
+ * key still declared by some file — even one renamed away from the canonical
342
+ * `<key>.toml` path — is left alone, since `sync push` would update it, not
343
+ * delete it (issue #1655). A file whose key was never in prior state was
344
+ * hand-authored and is left for `sync push` to create; it is never a candidate.
345
+ */
346
+ export declare function collectPushPruneCandidates(input: {
347
+ spec: PushPruneSpec;
348
+ configDir: string;
349
+ priorEntities: Record<string, any> | undefined;
350
+ }): Array<{
351
+ key: string;
352
+ entry: any;
353
+ }>;
354
+ /**
355
+ * Outcome of the point read that confirms (or denies) a candidate's presence.
356
+ * A flat shape (rather than a discriminated union) because the CLI compiles with
357
+ * `strict: false`, where union narrowing on `ok` does not hold: `detail` is set
358
+ * only when `ok`, `status` only on a failed fetch (`status === 404` ⇒ gone).
359
+ */
360
+ export interface PushPruneFetch {
361
+ ok: boolean;
362
+ detail?: any;
363
+ status?: number;
364
+ }
365
+ export type PushPruneSkipReason =
366
+ /** State entry has no usable id/key (corrupt or partial state). */
367
+ "no-id"
368
+ /** Type has no CLI delete surface yet (metadata-category-configs, #1426). */
369
+ | "unsupported"
370
+ /** Live `modifiedAt` differs from prior state — edited out-of-band. */
371
+ | "drift"
372
+ /** A non-404 fetch error — presence could not be confirmed. */
373
+ | "unconfirmed";
374
+ export type PushPruneDecision =
375
+ /** Confirmed present and unchanged (or `--force`): delete it. */
376
+ {
377
+ action: "delete";
378
+ address: string;
379
+ serverModifiedAt?: string;
380
+ }
381
+ /** 404: already gone server-side. Drop its stale state, do not delete. */
382
+ | {
383
+ action: "drop-state";
384
+ }
385
+ /** Leave the entity and its prior-state slot intact. */
386
+ | {
387
+ action: "skip";
388
+ reason: PushPruneSkipReason;
389
+ serverModifiedAt?: string;
390
+ };
391
+ /**
392
+ * Decide what to do with one prune candidate, given the point read's outcome.
393
+ * Pure so the safety rules are unit-testable without a live server.
394
+ *
395
+ * Fail-closed throughout: a candidate is deleted ONLY on a successful read whose
396
+ * live `modifiedAt` matches prior state (or under `--force`). A 404 means the
397
+ * work is already done (drop the stale state). Any other read failure, a missing
398
+ * address, or a type without a delete surface leaves everything untouched.
399
+ */
400
+ export declare function decidePushPruneCandidate(input: {
401
+ spec: PushPruneSpec;
402
+ address: string | undefined;
403
+ priorModifiedAt: string | undefined;
404
+ fetch: PushPruneFetch;
405
+ force: boolean;
406
+ }): PushPruneDecision;
407
+ /**
408
+ * Remove a pruned block's sidecar `<key>.tests/` directory and its
409
+ * `entities.testCases` state records. The main file is already gone (that is
410
+ * what made this a prune candidate), so `applyPrune` above — which bails when
411
+ * the main file is absent — does not clean the sidecar; this does.
412
+ */
413
+ export declare function removePrunedSidecar(options: {
414
+ configDir: string;
415
+ resourceType: SyncResourceType;
416
+ key: string;
417
+ testCaseEntities?: Record<string, {
418
+ blockType?: string;
419
+ blockKey?: string;
420
+ }>;
421
+ logger?: (msg: string) => void;
422
+ }): {
423
+ removed: string[];
424
+ removedTestCaseKeys: string[];
425
+ };
426
+ /** `sync diff` status for a managed remote entity whose local file is gone. */
427
+ export declare const REMOTE_ONLY_WILL_DELETE = "remote only (will delete with push --prune)";
428
+ /** `sync diff` status for a server entity that was never synced (hand-created). */
429
+ export declare const REMOTE_ONLY_UNMANAGED = "remote only (left alone)";
430
+ /**
431
+ * Classify a "remote only" entity (present server-side, no local file) for
432
+ * `sync diff` (issue #1655). A key the prior pull managed means the operator
433
+ * deleted its local file and `push --prune` will delete the remote entity; a key
434
+ * never synced was authored on the server and is left alone. Prior sync state is
435
+ * the discriminator, exactly as it is for the local-only / pull-prune split.
436
+ */
437
+ export declare function classifyRemoteOnly(priorEntities: SyncState["entities"] | undefined, slot: SyncEntitySlot, key: string): typeof REMOTE_ONLY_WILL_DELETE | typeof REMOTE_ONLY_UNMANAGED;