@happyvertical/smrt-core 0.57.29 → 0.57.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -15,6 +15,7 @@ and repository rules.
15
15
  | `src/data-query.ts` | Transport-neutral bounded query normalization | [agents/data-query.md](agents/data-query.md) |
16
16
  | `src/schema/`, `src/migrations/`, `src/cascade.ts`, `src/system/` | DDL parity, indexes, migrations, delete integrity, retention | [agents/schema-paths.md](agents/schema-paths.md) |
17
17
  | `src/postgres-permissions.ts` | Explicit PostgreSQL ACL plans and atomic reconciliation | [deployment permission contract](../../docs/content/postgres-permissions.md) |
18
+ | `src/migrations/deployment-phase-tracker.ts` | Permanent checksummed PostgreSQL deployment steps, atomic markers and explicit external-effect reconciliation | [agents/deployment-phases.md](agents/deployment-phases.md) |
18
19
  | `src/change-feed.ts` | Durable changes, cursors, table versions, retention | [agents/change-feed.md](agents/change-feed.md) |
19
20
  | `src/change-feed-authz.ts` | Consumer table/row change-feed authorization hooks (#3020) | [agents/change-feed.md](agents/change-feed.md) |
20
21
  | `src/change-signals.ts` | Signal bus, replica fan-out, SSE | [agents/change-signals.md](agents/change-signals.md) |
@@ -121,6 +121,14 @@ that declares `svelte` anyway only loses an optional context import.
121
121
  Transitive-only object modules are loaded through `importSmrtDependency()`
122
122
  from their provider's context, which needs no root import.
123
123
 
124
+ Selective SSR compilation compares a JavaScript barrel's `svelte` export target
125
+ against the package instance visible from its actual importer. pnpm may install
126
+ the same version in multiple peer contexts; the app-root package directory is
127
+ not the identity of a transitive import. Read that importer's installed export
128
+ map and root together, preserving exact target matching. Plain server entries
129
+ remain external; do not fix peer-context resolution by deduplicating app
130
+ packages or bundling every server export (#4318).
131
+
124
132
  Not covered by contract: a package whose root is a Svelte entry AND whose
125
133
  `@smrt()` objects are exported only from that root. The object loop must then
126
134
  import the root (it is the only module exporting the constructors) — statically
@@ -0,0 +1,71 @@
1
+ # Durable deployment data steps
2
+
3
+ `DeploymentPhaseTracker`, exported from `@happyvertical/smrt-core/migrations`,
4
+ is a PostgreSQL-only deployment data-step API. SQL schema steps continue to use
5
+ `MigrationTracker`, including its checksums and concurrent-index recovery.
6
+
7
+ A permanent `_smrt_deployment_steps` ledger keys each step by `application`,
8
+ `environment`, and stable `stepId`. Reusing an ID with a different checksum or
9
+ execution mode fails before its work or inspection executes. A release identifier
10
+ alone is not a stable step ID: otherwise later releases replay old data changes.
11
+ There is no retention. Preserve this table when restoring data; deleting markers
12
+ permits replay. Checkpoints must never contain secrets. `MAX_DEPLOYMENT_CHECKPOINT_BYTES` limits
13
+ serialized checkpoint JSON to 16 KiB measured in UTF-8 bytes, including quotes
14
+ and JSON syntax. Oversized or unserializable values fail with
15
+ `INVALID_CHECKPOINT` before their ledger query; the prior durable checkpoint is
16
+ preserved. Values follow `JSON.stringify` conventions: undefined/null checkpoints
17
+ normalize to null, nonfinite numbers become null, object properties containing
18
+ undefined/functions/symbols are omitted, and those array elements become null.
19
+ Circular references, BigInt, and top-level functions/symbols are rejected.
20
+ A checkpoint rejection does not undo prior external effects: reconciliation
21
+ still determines whether they completed or may safely resume.
22
+
23
+ Every execution requires `checkSafety(connection)`: a read-only callback that
24
+ throws unless runtime/static schema agreement and writer quiescence have been
25
+ proved. It runs after acquiring the scope lock and before any ledger DDL or data
26
+ write, including a replay. The caller owns platform fencing and complete writer
27
+ inventory; an advisory lock excludes cooperating deployment runners, not runtime
28
+ writers. Guards must not open transactions or change session/lock state.
29
+
30
+ The adapter must expose public `acquireSession()`. A session advisory lock
31
+ serializes every step in the same application/environment; the same pinned
32
+ session executes data writes and markers. Lock acquisition times out after
33
+ `lockTimeoutMs` (default 30 seconds). Other adapters fail closed. DDL provision
34
+ runs outside the data transaction under a separate initialization lock. The
35
+ migration role needs schema CREATE and ledger read/write permissions.
36
+
37
+ `runTransactionalStep(options, async tx => { ... })` exposes a query-only handle
38
+ on that pinned session. Use **only that handle** for all transactional mutations.
39
+ Data and the completed marker commit together. A thrown callback rolls both back;
40
+ a lost COMMIT response is never retried internally. On retry, the committed marker
41
+ skips work, or the absent marker permits a fresh transaction. Do not issue
42
+ transaction control, DDL requiring autocommit, session configuration or advisory
43
+ lock changes inside callbacks. Work outside this handle is not made atomic.
44
+
45
+ `runReconciledStep({ ...options, inspect, apply })` commits a running marker before
46
+ calling either callback. `inspect(context)` must independently classify actual
47
+ effects as `complete`, `safe-to-resume`, or `needs-repair`. It runs on fresh and
48
+ interrupted attempts. Only `safe-to-resume` permits `apply(context)`, and another
49
+ inspection must prove complete afterward. `saveCheckpoint(json)` persists a
50
+ running checkpoint before external effects; `context.checkpoint` exposes the
51
+ latest value. Each effect must tolerate the interval between performing it and
52
+ saving its checkpoint. No blind shell-command replay is provided.
53
+
54
+ A failure records needs-repair if the session survives; process/connection death
55
+ can leave running. Both are unresolved and require inspection on the next attempt.
56
+ `getStepStatus(key)` is read-only and reports the durable status without guessing
57
+ whether a running owner is alive. A reconciler may prove a needs-repair attempt
58
+ safe to resume after an operator repairs the underlying problem. A callback that
59
+ cannot determine safety must return needs-repair and refuse further mutation.
60
+
61
+ External effects cannot be atomically fenced by a PostgreSQL connection:
62
+ `assertActive()` checks the pinned session, but it cannot stop an already-running
63
+ shell command or fence an external service after connection loss. Applications
64
+ must supply service-specific fencing/idempotency and truthful inspection, or
65
+ require manual repair. This API makes no exactly-once claim for external effects.
66
+
67
+ Focused validation: `pnpm --filter @happyvertical/smrt-core exec vitest run
68
+ src/migrations/__tests__/deployment-phase-tracker-postgres.optional.test.ts` with
69
+ `SMRT_TEST_POSTGRES_URL` pointing to an isolated PostgreSQL database. Tests use
70
+ separate pools to exercise serialization, atomic marker/data rollback and commit,
71
+ checksum refusal, and ambiguous nontransactional recovery.
@@ -457,3 +457,29 @@ model-level validation, and regenerate RLS policies when adopting a changed
457
457
  binding.
458
458
 
459
459
  Custom actions retain the `{ action, result }` response envelope.
460
+
461
+ ## Collection-hosted REST factory identity (#4320)
462
+
463
+ Generated collection custom-action handlers resolve their executable collection
464
+ using the declaring resource's exact qualified registration key. Keep that
465
+ factory key separate from the item model's qualified type/policy metadata:
466
+ item collection slugs still govern permissions, and ordinary model routes keep
467
+ their own factory lookup. A resource in another package must neither rebuild
468
+ an item identity under its package nor steal the item's existing package when
469
+ its collection decorator merges configuration. Registration and generated
470
+ factory selection share one identity resolver; never install global aliases to
471
+ repair an inferred or bundled class name.
472
+
473
+ Every collection hosting a generated REST action receives its exact constructor
474
+ binding even with `cli: false` or omitted CLI configuration. This runtime-only
475
+ binding does not add discovery metadata: named HTTP CLI resources keep their
476
+ existing explicit-include eligibility. Constructor-only qualified receivers keep
477
+ their own collection-cache identity while resolving backing item metadata, so
478
+ ordinary model factories cannot reuse a different resource receiver. Neither
479
+ CLI nor MCP discovery gains a resource or default CRUD surface from this binding.
480
+
481
+ When consumer registration owns external packages (`smrtDependencies`), its
482
+ `.smrt/register.js` binds external REST action receivers using the resolved
483
+ canonical/provider-context export. The SvelteKit registration module continues
484
+ to delegate external imports there; external bindings also add no discovery
485
+ metadata and do not depend on CLI inclusion.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAYnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,uCAAuC,CAAC;AAqB/C,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAiJnC;;;GAGG;AACH,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,oBAAoB,GAAG,SAAS,GAC5C,OAAO,CAcT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,oBAAoB,GAAG,SAAS,GAC5C,MAAM,GAAG,SAAS,CAEpB;AAED;;;;GAIG;AACH,MAAM,WAAW,4BACf,SAAQ,OAAO,CACb,IAAI,CACF,gBAAgB,EACd,WAAW,GACX,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,aAAa,GACb,gBAAgB,CACnB,CACF;IACD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,4BAA4B,CAAC;IACnD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAmXD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAykBtE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAa,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9C,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAYnC,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,uCAAuC,CAAC;AAqB/C,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAiJnC;;;GAGG;AACH,UAAU,oBAAoB;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,oBAAoB,GAAG,SAAS,GAC5C,OAAO,CAcT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,oBAAoB,GAAG,SAAS,GAC5C,MAAM,GAAG,SAAS,CAEpB;AAED;;;;GAIG;AACH,MAAM,WAAW,4BACf,SAAQ,OAAO,CACb,IAAI,CACF,gBAAgB,EACd,WAAW,GACX,YAAY,GACZ,YAAY,GACZ,gBAAgB,GAChB,aAAa,GACb,cAAc,GACd,aAAa,GACb,gBAAgB,CACnB,CACF;IACD,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,4BAA4B,CAAC;IACnD;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAmXD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA2lBtE"}
@@ -1,6 +1,6 @@
1
1
  import "../scanner/types.js";
2
2
  import { canonicalSvelteKitPath } from "../vite-plugin/sveltekit-path.js";
3
- import { clearGeneratedSvelteKitRouteFiles, reconcileSvelteKitRouteGitignore } from "../vite-plugin/sveltekit-generator.js";
3
+ import { clearGeneratedSvelteKitRouteFiles, reconcileSvelteKitRouteGitignore, resolveApiActionSet } from "../vite-plugin/sveltekit-generator.js";
4
4
  import { generateWebModule, isCollectionManifestClass, resolveCollectionItemObject } from "../vite-plugin/web-collections.js";
5
5
  import { publishArtifactFiles } from "./artifact-publication.js";
6
6
  import { resolveFileKnowledgeConfig } from "../knowledge-config.js";
@@ -523,7 +523,10 @@ function smrtConsumer(options = {}) {
523
523
  skipSelf: true
524
524
  });
525
525
  const compile = resolved?.id.endsWith(".svelte") || resolved?.id.endsWith(".css");
526
- const compileSvelteBarrel = resolvesToSvelteCondition(packageName, selectivePackage.packageJson, selectivePackage.packageRoot, id, resolved?.id);
526
+ const importerPath = importer?.split("?")[0];
527
+ const packageJsonPath = importerPath && path$1.isAbsolute(importerPath) ? findInstalledPackageJson(packageName, path$1.dirname(importerPath)) : void 0;
528
+ const packageJson = packageJsonPath ? readInstalledPackageJson(packageJsonPath) : selectivePackage.packageJson;
529
+ const compileSvelteBarrel = packageJson && resolvesToSvelteCondition(packageName, packageJson, packageJsonPath ? path$1.dirname(packageJsonPath) : selectivePackage.packageRoot, id, resolved?.id);
527
530
  if (!compile && !compileSvelteBarrel) return {
528
531
  id,
529
532
  external: true
@@ -953,7 +956,10 @@ async function generateRegistrationFile(manifest, projectRoot) {
953
956
  itemBinding: exportBinding
954
957
  });
955
958
  importedEntryCount++;
956
- if (isCollectionClass(def)) continue;
959
+ if (isCollectionClass(def)) {
960
+ if (resolveApiActionSet(def, manifest).size > 0) registrations.push(`if (${exportBinding}) ObjectRegistry.registerCollection(${JSON.stringify(qualifiedName)}, ${exportBinding});`);
961
+ continue;
962
+ }
957
963
  const logicalName = def.className || exportName;
958
964
  registrationManifests[objectName] = {
959
965
  ...manifest,