@substrat-run/contract-tests 0.147.0 → 0.148.0

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 (55) hide show
  1. package/dist/bind-module.d.ts +4 -0
  2. package/dist/bind-module.d.ts.map +1 -1
  3. package/dist/capability-expiry-suite.d.ts.map +1 -1
  4. package/dist/capability-expiry-suite.js +6 -4
  5. package/dist/capability-expiry-suite.js.map +1 -1
  6. package/dist/counter-runner.d.ts +22 -0
  7. package/dist/counter-runner.d.ts.map +1 -0
  8. package/dist/counter-runner.js +96 -0
  9. package/dist/counter-runner.js.map +1 -0
  10. package/dist/derived-module.d.ts +4 -0
  11. package/dist/derived-module.d.ts.map +1 -1
  12. package/dist/entity-state-module.d.ts +8 -0
  13. package/dist/entity-state-module.d.ts.map +1 -1
  14. package/dist/entity-trash-module.d.ts +4 -0
  15. package/dist/entity-trash-module.d.ts.map +1 -1
  16. package/dist/erasure-module.d.ts +4 -0
  17. package/dist/erasure-module.d.ts.map +1 -1
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +6 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/modules.d.ts +124 -0
  23. package/dist/modules.d.ts.map +1 -1
  24. package/dist/modules.js +4 -0
  25. package/dist/modules.js.map +1 -1
  26. package/dist/permission-suite.d.ts.map +1 -1
  27. package/dist/permission-suite.js +19 -0
  28. package/dist/permission-suite.js.map +1 -1
  29. package/dist/scope-host-suite.d.ts.map +1 -1
  30. package/dist/scope-host-suite.js +118 -0
  31. package/dist/scope-host-suite.js.map +1 -1
  32. package/dist/scope-store-runner-suite.d.ts +46 -0
  33. package/dist/scope-store-runner-suite.d.ts.map +1 -0
  34. package/dist/scope-store-runner-suite.js +187 -0
  35. package/dist/scope-store-runner-suite.js.map +1 -0
  36. package/dist/scope-store-suite.d.ts.map +1 -1
  37. package/dist/scope-store-suite.js +45 -93
  38. package/dist/scope-store-suite.js.map +1 -1
  39. package/dist/stamps-module.d.ts +4 -0
  40. package/dist/stamps-module.d.ts.map +1 -1
  41. package/dist/store-kind-release-suite.d.ts +12 -0
  42. package/dist/store-kind-release-suite.d.ts.map +1 -0
  43. package/dist/store-kind-release-suite.js +353 -0
  44. package/dist/store-kind-release-suite.js.map +1 -0
  45. package/dist/upload-link-module.d.ts +168 -0
  46. package/dist/upload-link-module.d.ts.map +1 -0
  47. package/dist/upload-link-module.js +95 -0
  48. package/dist/upload-link-module.js.map +1 -0
  49. package/dist/upload-link-suite.d.ts +3 -0
  50. package/dist/upload-link-suite.d.ts.map +1 -0
  51. package/dist/upload-link-suite.js +328 -0
  52. package/dist/upload-link-suite.js.map +1 -0
  53. package/dist/vertical-events-suite.d.ts +8 -0
  54. package/dist/vertical-events-suite.d.ts.map +1 -1
  55. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"scope-host-suite.d.ts","sourceRoot":"","sources":["../src/scope-host-suite.ts"],"names":[],"mappings":"AAEA,OAAO,EAqBL,KAAK,SAAS,EAqBf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAeL,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAa9B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAuCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa,MACrB,OAAO,CAAC,OAAO,CAAC,QACb,SAAS,YACL,MAAM,KACf,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAC5C,IAAI,GAAE,qBAA0B,GAC/B,IAAI,CAytUN"}
1
+ {"version":3,"file":"scope-host-suite.d.ts","sourceRoot":"","sources":["../src/scope-host-suite.ts"],"names":[],"mappings":"AAEA,OAAO,EAqBL,KAAK,SAAS,EAqBf,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAeL,KAAK,SAAS,EACf,MAAM,sBAAsB,CAAC;AAa9B,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAuCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,aAAa,MACrB,OAAO,CAAC,OAAO,CAAC,QACb,SAAS,YACL,MAAM,KACf,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;CACvC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAC5C,IAAI,GAAE,qBAA0B,GAC/B,IAAI,CAu1UN"}
@@ -5236,6 +5236,113 @@ export function scopeHostContractSuite(adapterName, makeFixture, opts = {}) {
5236
5236
  expect(await host.admin.openSubjectPayloads(staff, t1, s, [{ subjectId: subject, sealed: sealed }]))
5237
5237
  .toEqual([null]);
5238
5238
  });
5239
+ /** A vertical with one admitted version, served on `ref`, and a scope pinned to `scopeRef`. */
5240
+ const pinnedErasureFixture = async (scopeRef) => {
5241
+ const slug = `pin-${ulid().toLowerCase().slice(-8)}`;
5242
+ await host.admin.registerVertical(staff, { slug, name: slug, source: 'cli' });
5243
+ const versionId = ulid();
5244
+ const ref = `${slug}-serving`;
5245
+ await host.admin.publishVersion(staff, {
5246
+ id: versionId,
5247
+ verticalSlug: slug,
5248
+ version: `1.0.${versionId.slice(-4).toLowerCase()}`,
5249
+ manifestDigest: `m-${versionId}`,
5250
+ permissionDigest: 'p',
5251
+ migrationDigest: 'g',
5252
+ deploymentRef: ref,
5253
+ manifestJson: JSON.stringify({
5254
+ version: '1.0.0', entry: 'index.js', compatibilityDate: '2026-07-01',
5255
+ registry: { permissions: [], roles: [], entityGrants: [] },
5256
+ digests: { manifest: 'm', permission: 'p', migration: 'g' },
5257
+ }),
5258
+ });
5259
+ await host.admin.admitVersion(staff, versionId);
5260
+ const s = scopeId.parse(ulid());
5261
+ const subject = ulid();
5262
+ await host.provisionScope(staff, { tenantId: t1, scopeId: s, vertical: slug });
5263
+ const [sealed] = await host.admin.sealSubjectPayloads(staff, t1, s, [{ subjectId: subject, plaintext: 'private' }]);
5264
+ await host.admin.setVerticalServing(staff, slug, { ref, versionId, doClasses: ['ScopeDO'], migrationTag: 'v1' });
5265
+ await host.admin.setScopeServingRef(staff, t1, s, scopeRef ?? ref);
5266
+ const opened = () => host.admin.openSubjectPayloads(staff, t1, s, [{ subjectId: subject, sealed: sealed }]);
5267
+ return { slug, versionId, ref, s, subject, opened };
5268
+ };
5269
+ const redacted = [{ events: 0, intents: 0, jobRuns: 0, idempotencyResults: 0, intentIds: [],
5270
+ vertical: { verticalRows: [], hookRows: [], unreachedEntities: [] } }];
5271
+ it('an erasure claim fails when the serving pointer is not the pair it read, and the key stays (#2285)', async () => {
5272
+ const { versionId, ref, s, subject, opened } = await pinnedErasureFixture();
5273
+ const expected = { versionId: null, servingRef: ref, epoch: 0, copyCount: 0, servingPointer: { ref, versionId: ulid() } };
5274
+ await expectRefusal(host.admin.finalizeSubjectShred(staff, t1, s, subject, redacted, expected), 'precondition_failed');
5275
+ expect(await opened()).toEqual(['private']);
5276
+ expect((await host.admin.finalizeSubjectShred(staff, t1, s, subject, redacted, { ...expected, servingPointer: { ref, versionId } })).keyDestroyed).toBe(true);
5277
+ expect(await opened()).toEqual([null]);
5278
+ });
5279
+ it('a scope off its pin is fenced too: a pointer moved onto its script refuses the claim (#2285)', async () => {
5280
+ // The scope's own script is not the pointer's when the erasure reads it, so it runs no
5281
+ // pointer version. A rollback onto that script before the claim would make it run one.
5282
+ const { slug, versionId, ref, s, subject, opened } = await pinnedErasureFixture('old-script');
5283
+ const expected = { versionId: null, servingRef: 'old-script', epoch: 0, copyCount: 0, servingPointer: { ref, versionId } };
5284
+ await host.admin.setVerticalServing(staff, slug, { ref: 'old-script', versionId, doClasses: ['ScopeDO'], migrationTag: 'v1' });
5285
+ await expectRefusal(host.admin.finalizeSubjectShred(staff, t1, s, subject, redacted, expected), 'precondition_failed');
5286
+ expect(await opened()).toEqual(['private']);
5287
+ expect((await host.admin.finalizeSubjectShred(staff, t1, s, subject, redacted, { ...expected, servingPointer: { ref: 'old-script', versionId } })).keyDestroyed).toBe(true);
5288
+ });
5289
+ it('a direct shred with a fence claims it: a moved scope keeps the key, and the pair it read destroys it (#2285)', async () => {
5290
+ const { versionId, ref, s, subject, opened } = await pinnedErasureFixture();
5291
+ const fence = { versionId: null, servingRef: ref, epoch: 0, copyCount: 0, servingPointer: { ref, versionId: ulid() } };
5292
+ await expectRefusal(host.admin.shredSubject(staff, t1, s, subject, fence), 'precondition_failed');
5293
+ expect(await opened()).toEqual(['private']);
5294
+ expect(await host.admin.scopeErasureEpoch(staff, t1, s)).toBe(0);
5295
+ expect((await host.admin.shredSubject(staff, t1, s, subject, { ...fence, servingPointer: { ref, versionId } })).keyDestroyed)
5296
+ .toBe(true);
5297
+ expect(await opened()).toEqual([null]);
5298
+ // The claim moved the epoch every bind compares, as the orchestrated finalize's does.
5299
+ expect(await host.admin.scopeErasureEpoch(staff, t1, s)).toBe(1);
5300
+ });
5301
+ // #2286, L17: what the lifecycle reaches is every binding any version declared.
5302
+ it('keeps every scope-store binding a version declared, refuses a re-kind at publish, and names none it cannot read', async () => {
5303
+ const slug = `bind-${ulid().toLowerCase().slice(-8)}`;
5304
+ await host.admin.registerVertical(staff, { slug, name: slug, source: 'cli' });
5305
+ const publish = (scopeStores, raw) => {
5306
+ const id = ulid();
5307
+ return host.admin.publishVersion(staff, {
5308
+ id,
5309
+ verticalSlug: slug,
5310
+ version: `1.0.${id.slice(-6).toLowerCase()}`,
5311
+ manifestDigest: `m-${id}`,
5312
+ permissionDigest: 'p',
5313
+ migrationDigest: 'g',
5314
+ deploymentRef: null,
5315
+ manifestJson: raw ?? JSON.stringify({
5316
+ version: '1.0.0', entry: 'index.js', compatibilityDate: '2026-07-01',
5317
+ registry: { permissions: [], roles: [], entityGrants: [] },
5318
+ digests: { manifest: 'm', permission: 'p', migration: 'g' },
5319
+ ...(scopeStores ? { scopeStores } : {}),
5320
+ }),
5321
+ }).then(() => id);
5322
+ };
5323
+ await publish([{ binding: 'GRAPH', kind: 'graph' }, { binding: 'OLD', kind: 'graph' }]);
5324
+ await publish([{ binding: 'GRAPH', kind: 'graph' }]);
5325
+ // A dropped binding is still there.
5326
+ expect(await host.admin.verticalScopeStores(staff, slug)).toEqual([
5327
+ { binding: 'GRAPH', kind: 'graph' },
5328
+ { binding: 'OLD', kind: 'graph' },
5329
+ ]);
5330
+ // A re-kind is refused, and the version is not stored.
5331
+ const before = (await host.admin.listVersions(staff, slug)).length;
5332
+ await expectRefusal(publish([{ binding: 'NEW', kind: 'graph' }, { binding: 'OLD', kind: 'counter' }]), 'conflict');
5333
+ expect((await host.admin.listVersions(staff, slug)).length).toBe(before);
5334
+ // So is a version that declares one binding twice.
5335
+ await expectRefusal(publish([{ binding: 'NEW', kind: 'graph' }, { binding: 'NEW', kind: 'graph' }]), 'conflict');
5336
+ expect((await host.admin.listVersions(staff, slug)).length).toBe(before);
5337
+ // The twin: the same kind again, with a new binding beside it, is published.
5338
+ await publish([{ binding: 'OLD', kind: 'graph' }, { binding: 'NEW', kind: 'graph' }]);
5339
+ expect((await host.admin.verticalScopeStores(staff, slug)).map((s) => s.binding)).toEqual(['GRAPH', 'NEW', 'OLD']);
5340
+ // A version whose manifest cannot be read may declare a store nobody can name.
5341
+ await publish(undefined, '{nope');
5342
+ await expectRefusal(host.admin.verticalScopeStores(staff, slug), 'unavailable');
5343
+ // An unknown vertical declared none.
5344
+ expect(await host.admin.verticalScopeStores(staff, `none-${slug}`)).toEqual([]);
5345
+ });
5239
5346
  // #1722 (Codex #2008 r7): the admin-log record of a staff resolution of a kept copy. The
5240
5347
  // resolution itself runs in the vertical's store; the directory only records it, in full.
5241
5348
  it('records a kept-copy resolution in the admin log, before and after, and refuses an unknown scope', async () => {
@@ -6916,6 +7023,17 @@ export function scopeHostContractSuite(adapterName, makeFixture, opts = {}) {
6916
7023
  const reclaimed = (await host.admin.listHostnames(staff, { scopeId: s2 })).find((h) => h.hostname === 'eu.example.com');
6917
7024
  expect(reclaimed?.scopeId).toBe(s2);
6918
7025
  });
7026
+ it('an unbind for one scope leaves a name rebound to another scope bound (#2285)', async () => {
7027
+ // `eu.example.com` is s2's now (the test above). An unroute of s1 that listed the name while
7028
+ // it was s1's must not take it from s2: the owner is judged by the delete itself.
7029
+ const s2Names = async () => (await host.admin.listHostnames(staff, { scopeId: s2 })).map((h) => h.hostname);
7030
+ await host.admin.unbindHostname(staff, 'eu.example.com', { tenantId: t1, scopeId: s1 });
7031
+ expect(await s2Names()).toContain('eu.example.com');
7032
+ // The twin: the owner it names unbinds it, and the unbind is audited on that scope.
7033
+ await host.admin.unbindHostname(staff, 'EU.example.com', { tenantId: t2, scopeId: s2 });
7034
+ expect(await s2Names()).not.toContain('eu.example.com');
7035
+ expect((await host.admin.auditLog(staff, { scopeId: s2 })).map((e) => e.action)).toContain('unbindHostname');
7036
+ });
6919
7037
  it('rejects duplicate module registration', () => {
6920
7038
  expect(() => host.registerModule({ manifest: testModManifest })).toThrow(/already registered/);
6921
7039
  });