@ddd-ts-kit/kernel 0.7.0 → 0.8.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 (128) hide show
  1. package/README.md +42 -9
  2. package/dist/application/audit/tenant-scope-of.d.ts +4 -1
  3. package/dist/application/audit/tenant-scope-of.d.ts.map +1 -1
  4. package/dist/application/audit/tenant-scope-of.js +6 -2
  5. package/dist/application/audit/tenant-scope-of.js.map +1 -1
  6. package/dist/application/cache/tenant-cache-key.d.ts +5 -0
  7. package/dist/application/cache/tenant-cache-key.d.ts.map +1 -0
  8. package/dist/application/cache/tenant-cache-key.js +7 -0
  9. package/dist/application/cache/tenant-cache-key.js.map +1 -0
  10. package/dist/application/events/execution-context-of-event.d.ts +2 -1
  11. package/dist/application/events/execution-context-of-event.d.ts.map +1 -1
  12. package/dist/application/events/execution-context-of-event.js +3 -1
  13. package/dist/application/events/execution-context-of-event.js.map +1 -1
  14. package/dist/application/events/integration-event.d.ts +6 -0
  15. package/dist/application/events/integration-event.d.ts.map +1 -1
  16. package/dist/application/events/scoped-publisher.d.ts +4 -3
  17. package/dist/application/events/scoped-publisher.d.ts.map +1 -1
  18. package/dist/application/events/scoped-publisher.js +11 -5
  19. package/dist/application/events/scoped-publisher.js.map +1 -1
  20. package/dist/application/execution/execution-context.d.ts +7 -1
  21. package/dist/application/execution/execution-context.d.ts.map +1 -1
  22. package/dist/application/execution/execution-context.js +7 -2
  23. package/dist/application/execution/execution-context.js.map +1 -1
  24. package/dist/application/execution/log-fields-of.d.ts.map +1 -1
  25. package/dist/application/execution/log-fields-of.js +1 -0
  26. package/dist/application/execution/log-fields-of.js.map +1 -1
  27. package/dist/application/ports/tenant-directory.d.ts +8 -0
  28. package/dist/application/ports/tenant-directory.d.ts.map +1 -0
  29. package/dist/application/ports/tenant-directory.js +10 -0
  30. package/dist/application/ports/tenant-directory.js.map +1 -0
  31. package/dist/application/scheduling/execution-context-of-job.d.ts +3 -1
  32. package/dist/application/scheduling/execution-context-of-job.d.ts.map +1 -1
  33. package/dist/application/scheduling/execution-context-of-job.js +1 -1
  34. package/dist/application/scheduling/execution-context-of-job.js.map +1 -1
  35. package/dist/application/scheduling/job-execution.d.ts +2 -0
  36. package/dist/application/scheduling/job-execution.d.ts.map +1 -1
  37. package/dist/application/scheduling/job-options.d.ts +6 -0
  38. package/dist/application/scheduling/job-options.d.ts.map +1 -1
  39. package/dist/application/scheduling/job-policy.d.ts.map +1 -1
  40. package/dist/application/scheduling/job-policy.js +4 -2
  41. package/dist/application/scheduling/job-policy.js.map +1 -1
  42. package/dist/application/session/has-scope.d.ts +5 -0
  43. package/dist/application/session/has-scope.d.ts.map +1 -0
  44. package/dist/application/session/has-scope.js +5 -0
  45. package/dist/application/session/has-scope.js.map +1 -0
  46. package/dist/application/session/session-scope.d.ts +4 -0
  47. package/dist/application/session/session-scope.d.ts.map +1 -0
  48. package/dist/application/session/session-scope.js +2 -0
  49. package/dist/application/session/session-scope.js.map +1 -0
  50. package/dist/application/session/session-scopes.d.ts +11 -0
  51. package/dist/application/session/session-scopes.d.ts.map +1 -0
  52. package/dist/application/session/session-scopes.js +2 -0
  53. package/dist/application/session/session-scopes.js.map +1 -0
  54. package/dist/application/session/session.d.ts +6 -0
  55. package/dist/application/session/session.d.ts.map +1 -1
  56. package/dist/application/tenancy/across-tenants.d.ts +12 -0
  57. package/dist/application/tenancy/across-tenants.d.ts.map +1 -0
  58. package/dist/application/tenancy/across-tenants.js +47 -0
  59. package/dist/application/tenancy/across-tenants.js.map +1 -0
  60. package/dist/application/tenancy/assert-tenant-id.d.ts +3 -0
  61. package/dist/application/tenancy/assert-tenant-id.d.ts.map +1 -0
  62. package/dist/application/tenancy/assert-tenant-id.js +10 -0
  63. package/dist/application/tenancy/assert-tenant-id.js.map +1 -0
  64. package/dist/application/tenancy/elevation-options.d.ts +12 -0
  65. package/dist/application/tenancy/elevation-options.d.ts.map +1 -0
  66. package/dist/application/tenancy/elevation-options.js +2 -0
  67. package/dist/application/tenancy/elevation-options.js.map +1 -0
  68. package/dist/application/tenancy/invalid-tenant-elevation-error.d.ts +5 -0
  69. package/dist/application/tenancy/invalid-tenant-elevation-error.d.ts.map +1 -0
  70. package/dist/application/tenancy/invalid-tenant-elevation-error.js +8 -0
  71. package/dist/application/tenancy/invalid-tenant-elevation-error.js.map +1 -0
  72. package/dist/application/tenancy/invalid-tenant-id-error.d.ts +6 -0
  73. package/dist/application/tenancy/invalid-tenant-id-error.d.ts.map +1 -0
  74. package/dist/application/tenancy/invalid-tenant-id-error.js +10 -0
  75. package/dist/application/tenancy/invalid-tenant-id-error.js.map +1 -0
  76. package/dist/application/tenancy/is-tenant-id.d.ts +3 -0
  77. package/dist/application/tenancy/is-tenant-id.d.ts.map +1 -0
  78. package/dist/application/tenancy/is-tenant-id.js +12 -0
  79. package/dist/application/tenancy/is-tenant-id.js.map +1 -0
  80. package/dist/application/tenancy/is-tenant-member.d.ts +4 -0
  81. package/dist/application/tenancy/is-tenant-member.d.ts.map +1 -0
  82. package/dist/application/tenancy/is-tenant-member.js +6 -0
  83. package/dist/application/tenancy/is-tenant-member.js.map +1 -0
  84. package/dist/application/tenancy/tenant-elevation.d.ts +11 -0
  85. package/dist/application/tenancy/tenant-elevation.d.ts.map +1 -0
  86. package/dist/application/tenancy/tenant-elevation.js +2 -0
  87. package/dist/application/tenancy/tenant-elevation.js.map +1 -0
  88. package/dist/application/tenancy/tenant-key.d.ts +9 -0
  89. package/dist/application/tenancy/tenant-key.d.ts.map +1 -0
  90. package/dist/application/tenancy/tenant-key.js +11 -0
  91. package/dist/application/tenancy/tenant-key.js.map +1 -0
  92. package/dist/application/tenancy/tenant-reach-of.d.ts +9 -0
  93. package/dist/application/tenancy/tenant-reach-of.d.ts.map +1 -0
  94. package/dist/application/tenancy/tenant-reach-of.js +18 -0
  95. package/dist/application/tenancy/tenant-reach-of.js.map +1 -0
  96. package/dist/application/tenancy/tenant-reach.d.ts +12 -0
  97. package/dist/application/tenancy/tenant-reach.d.ts.map +1 -0
  98. package/dist/application/tenancy/tenant-reach.js +2 -0
  99. package/dist/application/tenancy/tenant-reach.js.map +1 -0
  100. package/dist/application/tenancy/tenants-of.d.ts +8 -0
  101. package/dist/application/tenancy/tenants-of.d.ts.map +1 -0
  102. package/dist/application/tenancy/tenants-of.js +14 -0
  103. package/dist/application/tenancy/tenants-of.js.map +1 -0
  104. package/dist/index.d.ts +22 -0
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +22 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/infrastructure/in-memory/in-memory-tenant-directory.d.ts +8 -0
  109. package/dist/infrastructure/in-memory/in-memory-tenant-directory.d.ts.map +1 -0
  110. package/dist/infrastructure/in-memory/in-memory-tenant-directory.js +12 -0
  111. package/dist/infrastructure/in-memory/in-memory-tenant-directory.js.map +1 -0
  112. package/dist/infrastructure/tenancy/tenant-isolation-policies.d.ts +13 -0
  113. package/dist/infrastructure/tenancy/tenant-isolation-policies.d.ts.map +1 -0
  114. package/dist/infrastructure/tenancy/tenant-isolation-policies.js +13 -0
  115. package/dist/infrastructure/tenancy/tenant-isolation-policies.js.map +1 -0
  116. package/dist/infrastructure/tenancy/tenant-policy-expressions.d.ts +11 -0
  117. package/dist/infrastructure/tenancy/tenant-policy-expressions.d.ts.map +1 -0
  118. package/dist/infrastructure/tenancy/tenant-policy-expressions.js +15 -0
  119. package/dist/infrastructure/tenancy/tenant-policy-expressions.js.map +1 -0
  120. package/dist/infrastructure/tenancy/tenant-setting-values.d.ts +13 -0
  121. package/dist/infrastructure/tenancy/tenant-setting-values.d.ts.map +1 -0
  122. package/dist/infrastructure/tenancy/tenant-setting-values.js +19 -0
  123. package/dist/infrastructure/tenancy/tenant-setting-values.js.map +1 -0
  124. package/dist/infrastructure/tenancy/tenant-settings.d.ts +13 -0
  125. package/dist/infrastructure/tenancy/tenant-settings.d.ts.map +1 -0
  126. package/dist/infrastructure/tenancy/tenant-settings.js +13 -0
  127. package/dist/infrastructure/tenancy/tenant-settings.js.map +1 -0
  128. package/package.json +1 -1
package/README.md CHANGED
@@ -552,8 +552,13 @@ The authenticated session of an actor, as plain data.
552
552
  | `authenticationStrength?` | `{ mfa?, assuranceLevel? }` |
553
553
  | `impersonatedBy?` | id of the support actor acting as `actor`; `logFieldsOf` carries it to every log line |
554
554
  | `locale?`, `timeZone?`, `client?` | BCP 47 tag, IANA zone, `{ ip?, userAgent? }` as opaque strings |
555
+ | `scopes?` | what the credential allows whoever presents it (the OAuth 2.0 `scope`): a limit on the client, never what the actor may do |
555
556
  | `extensions` | the fields of the project |
556
557
 
558
+ `hasScope(session, scope)` reads the scopes; a project names its scope vocabulary once by
559
+ declaration merging of `SessionScopes` (`interface SessionScopes { readonly 'invoices:read': true }`),
560
+ and `SessionScope`, the type a guard takes, is then that vocabulary instead of any string.
561
+
557
562
  `isExpired(session, clock.now())`, `isImpersonated(session)`, `anonymousSession()`,
558
563
  `systemSession(name, issuedAt?)`, and `isSession(value)`, the structural guard a boundary uses on
559
564
  what it received as a principal.
@@ -561,8 +566,8 @@ what it received as a principal.
561
566
  ### ExecutionContext and ExecutionScope
562
567
 
563
568
  What every unit of work carries, whatever started it: `correlationId`, `causationId?`, `actor`,
564
- `session?`, `tenantId?`, `locale?`, `deadline?`, `scheduledAt?` (for a scheduled job, the instant it
565
- was due), `origin` (`http`, `event`, `schedule`, `cli`, or a string of the project) and `extensions`. `executionContext(init)` builds one and takes the tenant
569
+ `session?`, `tenantId?`, `elevation?` (platform staff reading over tenants, see "Tenancy"),
570
+ `locale?`, `deadline?`, `scheduledAt?` (for a scheduled job, the instant it was due), `origin` (`http`, `event`, `schedule`, `cli`, or a string of the project) and `extensions`. `executionContext(init)` builds one and takes the tenant
566
571
  of the actor; `withActor(context, actor)` is the same work seen as another actor.
567
572
 
568
573
  `ExecutionScope` (`current()`, `require()`, `run(context, work)`) gives the context of the work
@@ -584,11 +589,13 @@ tenant scoping and outgoing headers.
584
589
 
585
590
  A consumed event opens its own context: `executionContextOfEvent(event, { consumer })` has
586
591
  origin `event`, the correlation of the event (its id when it names none), the event as
587
- `causationId` and `systemActor(consumer)` as actor; `inEventScope(scope, { consumer }, handler)`
592
+ `causationId`, `systemActor(consumer)` as actor and the `tenantId` of the event, so the
593
+ consumer reads and writes in that tenant only; `inEventScope(scope, { consumer }, handler)`
588
594
  wraps a handler with it. The standard envelope carries no actor, so that no personal data
589
595
  travels by default: a project whose envelopes name one passes `actorOf`. On the way out,
590
- `scopedPublisher(publisher, scope)` stamps the correlation of the work in progress on every
591
- event published without one, so everything one stimulus causes is found again across processes.
596
+ `scopedPublisher(publisher, scope)` stamps the correlation and the tenant of the work in
597
+ progress on every event published without them, so everything one stimulus causes is found
598
+ again across processes and stays in its tenant.
592
599
 
593
600
  `ActorContext` stays, unchanged. `ScopedActorContext` implements it on top of a scope: the
594
601
  current actor is the actor of the current execution context, so the two cannot disagree.
@@ -701,7 +708,8 @@ const hit: Quote | undefined = await cache.get(quoteKey('EUR-USD'));
701
708
  ```
702
709
 
703
710
  The key carries the type of its value. `InMemoryCache(clock, { maxEntries? })` expires by the
704
- `Clock` and is bounded.
711
+ `Clock` and is bounded. `tenantCacheKey<Quote>(scope, 'quote', pair)` is the same key confined to
712
+ the tenant of the work in progress: what one tenant cached is never served to another.
705
713
 
706
714
  ### Lock
707
715
 
@@ -748,9 +756,10 @@ instead of rejecting: only an unknown name rejects (`UnknownJobError`), and a na
748
756
  twice throws (`DuplicateJobError`).
749
757
 
750
758
  A run happens inside `ExecutionScope`, in the context `executionContextOfJob(name, { correlationId,
751
- scheduledAt, deadline? })` builds: origin `schedule`, `systemActor(name)`, a correlation of its
759
+ scheduledAt, deadline?, tenantId? })` builds: origin `schedule`, `systemActor(name)`, a correlation of its
752
760
  own, and `scheduledAt`, the instant the run was due, which `ExecutionContext` carries for this
753
- origin.
761
+ origin. A job is `global` by default and runs in no tenant; `tenancy: 'per-tenant'` makes the
762
+ scheduler run it once per tenant of a `TenantDirectory`, each run in that tenant.
754
763
 
755
764
  `parseJobSchedule(schedule)` validates a schedule and answers the form occurrences are
756
765
  computed from (`InvalidJobScheduleError`, code `INVALID_JOB_SCHEDULE`, otherwise);
@@ -770,12 +779,36 @@ if (!decision.allowed) {
770
779
 
771
780
  A sliding window: at any instant, at most `limit` allowed calls in the `windowMs` before it. A
772
781
  `RateLimitDecision` is `{ allowed, remaining, retryAfterMs }`; a refused call is not counted.
773
- The key is whatever is limited: a client, a tenant, a route and an address together.
782
+ The key is whatever is limited: a client, a tenant, a route and an address together. In a
783
+ multi-tenant project, `tenantKey(scope, 'password-reset', accountId)` confines it to the tenant of
784
+ the work in progress (`tenant:<id>:password-reset:<account>`) and refuses work that names none.
774
785
 
775
786
  `InMemoryRateLimiter(clock)` counts inside one process; the adapter shared by several instances
776
787
  is in `@ddd-ts-kit/redis`. `rateLimitDecisionOf(counted, limit)` turns what an adapter counted
777
788
  into the decision.
778
789
 
790
+ ### Tenancy
791
+
792
+ The tenant of the work is `ExecutionContext.tenantId`, set at the boundary and read by
793
+ infrastructure only, like the rest of the context. The kernel holds what every strategy shares;
794
+ the isolation itself (tenant column, row-level security, schema or database per tenant) is in the
795
+ persistence packages, and the whole picture in
796
+ [Multitenancy](../../docs/concepts/multitenancy.md).
797
+
798
+ | Concept | What it does |
799
+ |---|---|
800
+ | `isTenantId(value)`, `assertTenantId(id)`, `InvalidTenantIdError` | the one grammar of a tenant id (letters, digits, `_`, `-`, at most 63): `executionContext`, `executionContextOfEvent`, `acrossTenants`, `InMemoryTenantDirectory`, `tenantScopeOf`, `tenantReachOf` and the row-level security settings refuse any other, so that no tenant id can be read as a wildcard, a list or a key separator |
801
+ | `tenantScopeOf(scope)` | the one tenant a write names; `NoCurrentTenantError` for work that names none |
802
+ | `tenantReachOf(scope)` | what a read may see: `{ kind: 'tenant', tenantId }`, or `{ kind: 'elevated', tenants }` under an elevation |
803
+ | `tenantsOf(actor)`, `isTenantMember(actor, tenantId)` | the tenants an actor belongs to: its `tenantId` and its `tenants` attribute |
804
+ | `acrossTenants({ scope, trail, clock, ids }, { reason, tenants }, work)` | platform staff reading over tenants: refused without a reason or a tenant, recorded in the `AuditTrail` (action `tenancy:elevate`) before the work runs; writes still name one tenant |
805
+ | `tenantKey(scope, ...segments)`, `tenantCacheKey(scope, ...segments)` | keys of rate limits, locks and caches confined to the tenant: `tenant:<id>:<segments>`, each segment percent-encoded, so that two lists of segments never give one key |
806
+ | `TenantDirectory`, `InMemoryTenantDirectory` | the tenants that exist, for per-tenant jobs, provisioning and migrations |
807
+ | `TENANT_SETTINGS`, `tenantSettingValues(context)`, `TENANT_ISOLATION_POLICIES`, `tenantPolicyExpressions(column)` | what the row-level security of the persistence packages shares: the three settings of a transaction (`ddd_ts_kit.tenant_id`, and for an elevation only `ddd_ts_kit.reach` and `ddd_ts_kit.every_tenant`), their values for the work in progress, the names of the four policies (one per command) and their `read` and `write` expressions |
808
+
809
+ An elevation is never implied by a role: the platform decides the permission to elevate (a use
810
+ case or a guard), then calls `acrossTenants`. A trail that fails to record it refuses it.
811
+
779
812
  ### Page and PageRequest
780
813
 
781
814
  One shape for every search, in two variants:
@@ -1,5 +1,8 @@
1
1
  import type { ExecutionScope } from '../ports/execution-scope.js';
2
2
  import type { TenantScoped } from './tenant-scoped.js';
3
- /** For a repository adapter scoping a statement: the tenant of the work in progress. */
3
+ /**
4
+ * For a repository adapter scoping a statement: the tenant of the work in progress, checked
5
+ * against the grammar of `isTenantId` again however the context was built.
6
+ */
4
7
  export declare function tenantScopeOf(scope: ExecutionScope): TenantScoped;
5
8
  //# sourceMappingURL=tenant-scope-of.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-scope-of.d.ts","sourceRoot":"","sources":["../../../src/application/audit/tenant-scope-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,CAQjE"}
1
+ {"version":3,"file":"tenant-scope-of.d.ts","sourceRoot":"","sources":["../../../src/application/audit/tenant-scope-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,YAAY,CAQjE"}
@@ -1,10 +1,14 @@
1
+ import { assertTenantId } from '../tenancy/assert-tenant-id.js';
1
2
  import { NoCurrentTenantError } from './no-current-tenant-error.js';
2
- /** For a repository adapter scoping a statement: the tenant of the work in progress. */
3
+ /**
4
+ * For a repository adapter scoping a statement: the tenant of the work in progress, checked
5
+ * against the grammar of `isTenantId` again however the context was built.
6
+ */
3
7
  export function tenantScopeOf(scope) {
4
8
  const { tenantId } = scope.require();
5
9
  if (tenantId === undefined) {
6
10
  throw new NoCurrentTenantError();
7
11
  }
8
- return { tenantId };
12
+ return { tenantId: assertTenantId(tenantId) };
9
13
  }
10
14
  //# sourceMappingURL=tenant-scope-of.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tenant-scope-of.js","sourceRoot":"","sources":["../../../src/application/audit/tenant-scope-of.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGpE,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAErC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"tenant-scope-of.js","sourceRoot":"","sources":["../../../src/application/audit/tenant-scope-of.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGpE;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;IAErC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;AAChD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ExecutionScope } from '../ports/execution-scope.js';
2
+ import { type CacheKey } from './cache-key.js';
3
+ /** `cacheKey` confined to the tenant of the work in progress: what one tenant cached is never served to another. */
4
+ export declare function tenantCacheKey<T>(scope: ExecutionScope, ...segments: readonly string[]): CacheKey<T>;
5
+ //# sourceMappingURL=tenant-cache-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-cache-key.d.ts","sourceRoot":"","sources":["../../../src/application/cache/tenant-cache-key.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,gBAAgB,CAAC;AAEzD,oHAAoH;AACpH,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAEpG"}
@@ -0,0 +1,7 @@
1
+ import { tenantKey } from '../tenancy/tenant-key.js';
2
+ import { cacheKey } from './cache-key.js';
3
+ /** `cacheKey` confined to the tenant of the work in progress: what one tenant cached is never served to another. */
4
+ export function tenantCacheKey(scope, ...segments) {
5
+ return cacheKey(tenantKey(scope, ...segments));
6
+ }
7
+ //# sourceMappingURL=tenant-cache-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-cache-key.js","sourceRoot":"","sources":["../../../src/application/cache/tenant-cache-key.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAiB,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEzD,oHAAoH;AACpH,MAAM,UAAU,cAAc,CAAI,KAAqB,EAAE,GAAG,QAA2B;IACrF,OAAO,QAAQ,CAAI,SAAS,CAAC,KAAK,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC;AACpD,CAAC"}
@@ -3,7 +3,8 @@ import type { EventExecutionOptions } from './event-execution-options.js';
3
3
  import type { IntegrationEvent } from './integration-event.js';
4
4
  /**
5
5
  * The execution context of a consumed event: origin `event`, the correlation of the event (its
6
- * own id when it names none, which starts a new chain), and the event as the direct cause.
6
+ * own id when it names none, which starts a new chain), the event as the direct cause, and the
7
+ * tenant the event belongs to, so the consumer reads and writes in that tenant only.
7
8
  */
8
9
  export declare function executionContextOfEvent(event: IntegrationEvent, options: EventExecutionOptions): ExecutionContext;
9
10
  //# sourceMappingURL=execution-context-of-event.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context-of-event.d.ts","sourceRoot":"","sources":["../../../src/application/events/execution-context-of-event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,gBAAgB,CAOjH"}
1
+ {"version":3,"file":"execution-context-of-event.d.ts","sourceRoot":"","sources":["../../../src/application/events/execution-context-of-event.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,mCAAmC,CAAC;AAC5F,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,qBAAqB,GAAG,gBAAgB,CAQjH"}
@@ -2,7 +2,8 @@ import { systemActor } from '../actor/system-actor.js';
2
2
  import { executionContext } from '../execution/execution-context.js';
3
3
  /**
4
4
  * The execution context of a consumed event: origin `event`, the correlation of the event (its
5
- * own id when it names none, which starts a new chain), and the event as the direct cause.
5
+ * own id when it names none, which starts a new chain), the event as the direct cause, and the
6
+ * tenant the event belongs to, so the consumer reads and writes in that tenant only.
6
7
  */
7
8
  export function executionContextOfEvent(event, options) {
8
9
  return executionContext({
@@ -10,6 +11,7 @@ export function executionContextOfEvent(event, options) {
10
11
  causationId: event.id,
11
12
  actor: options.actorOf?.(event) ?? systemActor(options.consumer),
12
13
  origin: 'event',
14
+ ...(event.tenantId === undefined ? {} : { tenantId: event.tenantId }),
13
15
  });
14
16
  }
15
17
  //# sourceMappingURL=execution-context-of-event.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context-of-event.js","sourceRoot":"","sources":["../../../src/application/events/execution-context-of-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAyB,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAI5F;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAuB,EAAE,OAA8B;IAC7F,OAAO,gBAAgB,CAAC;QACtB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,EAAE;QAC9C,WAAW,EAAE,KAAK,CAAC,EAAE;QACrB,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC;QAChE,MAAM,EAAE,OAAO;KAChB,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"execution-context-of-event.js","sourceRoot":"","sources":["../../../src/application/events/execution-context-of-event.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAyB,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAI5F;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAuB,EAAE,OAA8B;IAC7F,OAAO,gBAAgB,CAAC;QACtB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,EAAE;QAC9C,WAAW,EAAE,KAAK,CAAC,EAAE;QACrB,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC;QAChE,MAAM,EAAE,OAAO;QACf,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;KACtE,CAAC,CAAC;AACL,CAAC"}
@@ -13,5 +13,11 @@ export interface IntegrationEvent<Payload = unknown> {
13
13
  /** W3C traceparent captured at publication so consumers continue the same trace. */
14
14
  readonly traceparent?: string;
15
15
  readonly correlationId?: string;
16
+ /**
17
+ * The tenant the fact belongs to, stamped from the work that published it. A consumer handles
18
+ * the event in that tenant's scope, so its reads and writes stay in the tenant. Absent for a
19
+ * fact of no tenant (the platform's own).
20
+ */
21
+ readonly tenantId?: string;
16
22
  }
17
23
  //# sourceMappingURL=integration-event.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integration-event.d.ts","sourceRoot":"","sources":["../../../src/application/events/integration-event.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,OAAO,GAAG,OAAO;IACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC"}
1
+ {"version":3,"file":"integration-event.d.ts","sourceRoot":"","sources":["../../../src/application/events/integration-event.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,gBAAgB,CAAC,OAAO,GAAG,OAAO;IACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B"}
@@ -2,9 +2,10 @@ import type { ExecutionScope } from '../ports/execution-scope.js';
2
2
  import type { IntegrationEventPublisher } from '../ports/integration-event-publisher.js';
3
3
  /**
4
4
  * Decorates a publisher: an event published without `correlationId` takes the one of the work
5
- * in progress, so everything one stimulus causes can be found again. An event that names its
6
- * correlation keeps it, and outside of any execution context nothing is stamped. The name of
7
- * the transport (`messagingSystem`) is passed on.
5
+ * in progress, so everything one stimulus causes can be found again, and one without `tenantId`
6
+ * the tenant of that work, so its consumers run in the same tenant. What an event names is
7
+ * kept, and outside of any execution context nothing is stamped. The name of the transport
8
+ * (`messagingSystem`) is passed on.
8
9
  */
9
10
  export declare function scopedPublisher(publisher: IntegrationEventPublisher, scope: ExecutionScope): IntegrationEventPublisher;
10
11
  //# sourceMappingURL=scoped-publisher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-publisher.d.ts","sourceRoot":"","sources":["../../../src/application/events/scoped-publisher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AAIzF;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,yBAAyB,EACpC,KAAK,EAAE,cAAc,GACpB,yBAAyB,CAO3B"}
1
+ {"version":3,"file":"scoped-publisher.d.ts","sourceRoot":"","sources":["../../../src/application/events/scoped-publisher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AAIzF;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,yBAAyB,EACpC,KAAK,EAAE,cAAc,GACpB,yBAAyB,CAO3B"}
@@ -1,9 +1,10 @@
1
1
  import { messagingSystemOf } from './messaging-system-of.js';
2
2
  /**
3
3
  * Decorates a publisher: an event published without `correlationId` takes the one of the work
4
- * in progress, so everything one stimulus causes can be found again. An event that names its
5
- * correlation keeps it, and outside of any execution context nothing is stamped. The name of
6
- * the transport (`messagingSystem`) is passed on.
4
+ * in progress, so everything one stimulus causes can be found again, and one without `tenantId`
5
+ * the tenant of that work, so its consumers run in the same tenant. What an event names is
6
+ * kept, and outside of any execution context nothing is stamped. The name of the transport
7
+ * (`messagingSystem`) is passed on.
7
8
  */
8
9
  export function scopedPublisher(publisher, scope) {
9
10
  const messagingSystem = messagingSystemOf(publisher);
@@ -13,9 +14,14 @@ export function scopedPublisher(publisher, scope) {
13
14
  };
14
15
  }
15
16
  function correlated(event, context) {
16
- if (event.correlationId !== undefined || context === undefined) {
17
+ if (context === undefined) {
17
18
  return event;
18
19
  }
19
- return { ...event, correlationId: context.correlationId };
20
+ const { correlationId, tenantId } = context;
21
+ return {
22
+ ...event,
23
+ ...(event.correlationId === undefined ? { correlationId } : {}),
24
+ ...(event.tenantId === undefined && tenantId !== undefined ? { tenantId } : {}),
25
+ };
20
26
  }
21
27
  //# sourceMappingURL=scoped-publisher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-publisher.js","sourceRoot":"","sources":["../../../src/application/events/scoped-publisher.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAoC,EACpC,KAAqB;IAErB,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAErD,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjG,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;KAC9D,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAuB,EAAE,OAAqC;IAChF,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC/D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,EAAE,GAAG,KAAK,EAAE,aAAa,EAAE,OAAO,CAAC,aAAa,EAAE,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"scoped-publisher.js","sourceRoot":"","sources":["../../../src/application/events/scoped-publisher.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAoC,EACpC,KAAqB;IAErB,MAAM,eAAe,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAErD,OAAO;QACL,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACjG,GAAG,CAAC,eAAe,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC;KAC9D,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,KAAuB,EAAE,OAAqC;IAChF,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAE5C,OAAO;QACL,GAAG,KAAK;QACR,GAAG,CAAC,KAAK,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,GAAG,CAAC,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChF,CAAC;AACJ,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import type { Actor } from '../actor/actor.js';
2
2
  import type { Session } from '../session/session.js';
3
+ import type { TenantElevation } from '../tenancy/tenant-elevation.js';
3
4
  import type { ExecutionContextExtensionValues } from './execution-context-extension-values.js';
4
5
  import type { ExecutionOrigin } from './execution-origin.js';
5
6
  /**
@@ -14,6 +15,8 @@ export interface ExecutionContext {
14
15
  readonly actor: Actor;
15
16
  readonly session?: Session;
16
17
  readonly tenantId?: string;
18
+ /** Platform staff reading over tenants, opened by `acrossTenants` and audited. Absent otherwise. */
19
+ readonly elevation?: TenantElevation;
17
20
  readonly locale?: string;
18
21
  /** The instant after which the result is useless to the caller. */
19
22
  readonly deadline?: Date;
@@ -23,6 +26,9 @@ export interface ExecutionContext {
23
26
  readonly extensions: ExecutionContextExtensionValues;
24
27
  }
25
28
  export type ExecutionContextInit = Omit<ExecutionContext, 'extensions' | 'tenantId'> & Partial<Pick<ExecutionContext, 'extensions' | 'tenantId'>>;
26
- /** Fills what can be derived: the tenant of the actor, no extension. */
29
+ /**
30
+ * Fills what can be derived: the tenant of the actor, no extension. A tenant id outside the
31
+ * grammar of `isTenantId` is refused (`InvalidTenantIdError`), whatever named it.
32
+ */
27
33
  export declare function executionContext(init: ExecutionContextInit): ExecutionContext;
28
34
  //# sourceMappingURL=execution-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../../../src/application/execution/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB,mGAAmG;IACnG,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,+BAA+B,CAAC;CACtD;AAED,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,CAAC,GAClF,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC;AAE7D,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAQ7E"}
1
+ {"version":3,"file":"execution-context.d.ts","sourceRoot":"","sources":["../../../src/application/execution/execution-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,yCAAyC,CAAC;AAC/F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,CAAC;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB,mGAAmG;IACnG,QAAQ,CAAC,WAAW,CAAC,EAAE,IAAI,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,+BAA+B,CAAC;CACtD;AAED,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,CAAC,GAClF,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAS7E"}
@@ -1,6 +1,11 @@
1
- /** Fills what can be derived: the tenant of the actor, no extension. */
1
+ import { assertTenantId } from '../tenancy/assert-tenant-id.js';
2
+ /**
3
+ * Fills what can be derived: the tenant of the actor, no extension. A tenant id outside the
4
+ * grammar of `isTenantId` is refused (`InvalidTenantIdError`), whatever named it.
5
+ */
2
6
  export function executionContext(init) {
3
- const tenantId = init.tenantId ?? init.actor.tenantId;
7
+ const named = init.tenantId ?? init.actor.tenantId;
8
+ const tenantId = named === undefined ? undefined : assertTenantId(named);
4
9
  return Object.freeze({
5
10
  ...init,
6
11
  ...(tenantId === undefined ? {} : { tenantId }),
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context.js","sourceRoot":"","sources":["../../../src/application/execution/execution-context.ts"],"names":[],"mappings":"AA6BA,wEAAwE;AACxE,MAAM,UAAU,gBAAgB,CAAC,IAA0B;IACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAEtD,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,IAAI;QACP,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;QAC/C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;KACjD,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"execution-context.js","sourceRoot":"","sources":["../../../src/application/execution/execution-context.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AA+BhE;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAA0B;IACzD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IACnD,MAAM,QAAQ,GAAG,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAEzE,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,GAAG,IAAI;QACP,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;QAC/C,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;KACjD,CAAC,CAAC;AACL,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"log-fields-of.d.ts","sourceRoot":"","sources":["../../../src/application/execution/log-fields-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAShE"}
1
+ {"version":3,"file":"log-fields-of.d.ts","sourceRoot":"","sources":["../../../src/application/execution/log-fields-of.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,iGAAiG;AACjG,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAUhE"}
@@ -6,6 +6,7 @@ export function logFieldsOf(context) {
6
6
  actorId: context.actor.id,
7
7
  ...(context.causationId === undefined ? {} : { causationId: context.causationId }),
8
8
  ...(context.tenantId === undefined ? {} : { tenantId: context.tenantId }),
9
+ ...(context.elevation === undefined ? {} : { elevation: context.elevation.reason }),
9
10
  ...(context.session?.impersonatedBy === undefined ? {} : { impersonatedBy: context.session.impersonatedBy }),
10
11
  };
11
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"log-fields-of.js","sourceRoot":"","sources":["../../../src/application/execution/log-fields-of.ts"],"names":[],"mappings":"AAGA,iGAAiG;AACjG,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO;QACL,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;QACzB,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAClF,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;KAC7G,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"log-fields-of.js","sourceRoot":"","sources":["../../../src/application/execution/log-fields-of.ts"],"names":[],"mappings":"AAGA,iGAAiG;AACjG,MAAM,UAAU,WAAW,CAAC,OAAyB;IACnD,OAAO;QACL,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE;QACzB,GAAG,CAAC,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC;QAClF,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACnF,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;KAC7G,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The tenants that exist, for work that runs once per tenant: a job declared `tenancy:
3
+ * 'per-tenant'`, the provisioning of a schema or a database, a migration applied to each.
4
+ */
5
+ export declare abstract class TenantDirectory {
6
+ abstract tenants(): Promise<readonly string[]>;
7
+ }
8
+ //# sourceMappingURL=tenant-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-directory.d.ts","sourceRoot":"","sources":["../../../src/application/ports/tenant-directory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,8BAAsB,eAAe;IAKnC,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;CAChD"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The tenants that exist, for work that runs once per tenant: a job declared `tenancy:
3
+ * 'per-tenant'`, the provisioning of a schema or a database, a migration applied to each.
4
+ */
5
+ export class TenantDirectory {
6
+ static {
7
+ Object.defineProperty(this, 'tokenKey', { value: 'kernel.TenantDirectory' });
8
+ }
9
+ }
10
+ //# sourceMappingURL=tenant-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-directory.js","sourceRoot":"","sources":["../../../src/application/ports/tenant-directory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,OAAgB,eAAe;IACnC;QACE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE,CAAC,CAAC;IAC/E,CAAC;CAGF"}
@@ -4,10 +4,12 @@ export interface JobExecutionStart {
4
4
  readonly scheduledAt: Date;
5
5
  /** The instant the timeout of the job ends the run, when it has one. */
6
6
  readonly deadline?: Date;
7
+ /** For a job declared `tenancy: 'per-tenant'`: the tenant of this run. */
8
+ readonly tenantId?: string;
7
9
  }
8
10
  /**
9
11
  * The execution context of one run: origin `schedule`, the system actor named after the job, a
10
- * correlation of its own and the instant the run was due.
12
+ * correlation of its own, the instant the run was due, and the tenant of a per-tenant run.
11
13
  */
12
14
  export declare function executionContextOfJob(job: string, start: JobExecutionStart): ExecutionContext;
13
15
  //# sourceMappingURL=execution-context-of-job.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context-of-job.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/execution-context-of-job.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,mCAAmC,CAAC;AAE5F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,CAE7F"}
1
+ {"version":3,"file":"execution-context-of-job.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/execution-context-of-job.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,gBAAgB,EAAoB,MAAM,mCAAmC,CAAC;AAE5F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,CAE7F"}
@@ -2,7 +2,7 @@ import { systemActor } from '../actor/system-actor.js';
2
2
  import { executionContext } from '../execution/execution-context.js';
3
3
  /**
4
4
  * The execution context of one run: origin `schedule`, the system actor named after the job, a
5
- * correlation of its own and the instant the run was due.
5
+ * correlation of its own, the instant the run was due, and the tenant of a per-tenant run.
6
6
  */
7
7
  export function executionContextOfJob(job, start) {
8
8
  return executionContext({ ...start, actor: systemActor(job), origin: 'schedule' });
@@ -1 +1 @@
1
- {"version":3,"file":"execution-context-of-job.js","sourceRoot":"","sources":["../../../src/application/scheduling/execution-context-of-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAyB,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAS5F;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,KAAwB;IACzE,OAAO,gBAAgB,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AACrF,CAAC"}
1
+ {"version":3,"file":"execution-context-of-job.js","sourceRoot":"","sources":["../../../src/application/scheduling/execution-context-of-job.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAyB,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAW5F;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,KAAwB;IACzE,OAAO,gBAAgB,CAAC,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;AACrF,CAAC"}
@@ -8,5 +8,7 @@ export interface JobExecution {
8
8
  readonly attempt: number;
9
9
  /** Aborted when the run exceeds its timeout or the scheduler stops: a long job checks it. */
10
10
  readonly signal: AbortSignal;
11
+ /** The tenant of this run, for a job declared `tenancy: 'per-tenant'`. */
12
+ readonly tenantId?: string;
11
13
  }
12
14
  //# sourceMappingURL=job-execution.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"job-execution.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-execution.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,yGAAyG;IACzG,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B"}
1
+ {"version":3,"file":"job-execution.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-execution.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;IAC3B,yGAAyG;IACzG,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,uGAAuG;IACvG,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B"}
@@ -21,5 +21,11 @@ export interface JobOptions {
21
21
  * an application running several runs it. Best effort: see the scheduler.
22
22
  */
23
23
  readonly singleInstance?: boolean;
24
+ /**
25
+ * `global` (default): one run per occurrence, in no tenant. `per-tenant`: one run per tenant
26
+ * of the `TenantDirectory` for each occurrence, each in that tenant's execution scope, so the
27
+ * job reads and writes one tenant at a time; one failing tenant does not stop the others.
28
+ */
29
+ readonly tenancy?: 'global' | 'per-tenant';
24
30
  }
25
31
  //# sourceMappingURL=job-options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"job-options.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sHAAsH;IACtH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uGAAuG;IACvG,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC"}
1
+ {"version":3,"file":"job-options.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-options.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;IACtC,6EAA6E;IAC7E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,sHAAsH;IACtH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uGAAuG;IACvG,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,QAAQ,GAAG,YAAY,CAAC;CAC5C"}
@@ -1 +1 @@
1
- {"version":3,"file":"job-policy.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,iDAAiD;AACjD,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAEhG,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAiBxD"}
1
+ {"version":3,"file":"job-policy.d.ts","sourceRoot":"","sources":["../../../src/application/scheduling/job-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,iDAAiD;AACjD,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;AAEhG,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAmBxD"}
@@ -3,15 +3,17 @@ import { parseDuration } from './parse-duration.js';
3
3
  /** Validates the options and fills in the defaults. Throws `InvalidJobScheduleError`. */
4
4
  export function jobPolicy(options) {
5
5
  const { overlap = 'skip', missed = 'skip', timeoutMs, jitterMs = 0, enabled = true, singleInstance = true } = options;
6
+ const tenancy = options.tenancy ?? 'global';
6
7
  assertOneOf('overlap', overlap, ['skip', 'queue']);
7
8
  assertOneOf('missed', missed, ['skip', 'run-once']);
9
+ assertOneOf('tenancy', tenancy, ['global', 'per-tenant']);
8
10
  if (!Number.isSafeInteger(jitterMs) || jitterMs < 0) {
9
11
  throw new InvalidJobScheduleError(`jitterMs is ${jitterMs}: a whole number of milliseconds, zero or more, is expected.`);
10
12
  }
11
13
  if (timeoutMs === undefined) {
12
- return { overlap, missed, jitterMs, enabled, singleInstance };
14
+ return { overlap, missed, jitterMs, enabled, singleInstance, tenancy };
13
15
  }
14
- return { overlap, missed, jitterMs, enabled, singleInstance, timeoutMs: parseDuration(timeoutMs) };
16
+ return { overlap, missed, jitterMs, enabled, singleInstance, tenancy, timeoutMs: parseDuration(timeoutMs) };
15
17
  }
16
18
  function assertOneOf(option, value, allowed) {
17
19
  if (allowed.includes(value)) {
@@ -1 +1 @@
1
- {"version":3,"file":"job-policy.js","sourceRoot":"","sources":["../../../src/application/scheduling/job-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpD,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,cAAc,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAEtH,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAEpD,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,uBAAuB,CAC/B,eAAe,QAAQ,8DAA8D,CACtF,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAChE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;AACrG,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,KAAa,EAAE,OAA0B;IAC5E,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,MAAM,IAAI,uBAAuB,CAAC,GAAG,MAAM,QAAQ,KAAK,aAAa,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAC1G,CAAC"}
1
+ {"version":3,"file":"job-policy.js","sourceRoot":"","sources":["../../../src/application/scheduling/job-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpD,yFAAyF;AACzF,MAAM,UAAU,SAAS,CAAC,OAAmB;IAC3C,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,QAAQ,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,cAAc,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACtH,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC;IAE5C,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IACpD,WAAW,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC;IAE1D,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,uBAAuB,CAC/B,eAAe,QAAQ,8DAA8D,CACtF,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC;IACzE,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,EAAE,CAAC;AAC9G,CAAC;AAED,SAAS,WAAW,CAAC,MAAc,EAAE,KAAa,EAAE,OAA0B;IAC5E,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,MAAM,IAAI,uBAAuB,CAAC,GAAG,MAAM,QAAQ,KAAK,aAAa,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;AAC1G,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { SessionScope } from './session-scope.js';
2
+ import type { Session } from './session.js';
3
+ /** Whether the session was granted the scope; a request without a session was granted none. */
4
+ export declare function hasScope(session: Session | undefined, scope: SessionScope): boolean;
5
+ //# sourceMappingURL=has-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has-scope.d.ts","sourceRoot":"","sources":["../../../src/application/session/has-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,+FAA+F;AAC/F,wBAAgB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAEnF"}
@@ -0,0 +1,5 @@
1
+ /** Whether the session was granted the scope; a request without a session was granted none. */
2
+ export function hasScope(session, scope) {
3
+ return session?.scopes?.includes(scope) === true;
4
+ }
5
+ //# sourceMappingURL=has-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"has-scope.js","sourceRoot":"","sources":["../../../src/application/session/has-scope.ts"],"names":[],"mappings":"AAGA,+FAA+F;AAC/F,MAAM,UAAU,QAAQ,CAAC,OAA4B,EAAE,KAAmB;IACxE,OAAO,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC;AACnD,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SessionScopes } from './session-scopes.js';
2
+ /** A scope of the project's vocabulary once `SessionScopes` declares one, any string before. */
3
+ export type SessionScope = [keyof SessionScopes] extends [never] ? string : Extract<keyof SessionScopes, string>;
4
+ //# sourceMappingURL=session-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scope.d.ts","sourceRoot":"","sources":["../../../src/application/session/session-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,gGAAgG;AAChG,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,aAAa,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,aAAa,EAAE,MAAM,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scope.js","sourceRoot":"","sources":["../../../src/application/session/session-scope.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The vocabulary of the scopes a project grants its clients, by declaration merging, so that a
3
+ * guard names only scopes that exist:
4
+ *
5
+ * declare module '@ddd-ts-kit/kernel' {
6
+ * interface SessionScopes { readonly 'invoices:read': true; readonly 'invoices:write': true }
7
+ * }
8
+ */
9
+ export interface SessionScopes {
10
+ }
11
+ //# sourceMappingURL=session-scopes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scopes.d.ts","sourceRoot":"","sources":["../../../src/application/session/session-scopes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;CAAG"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session-scopes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-scopes.js","sourceRoot":"","sources":["../../../src/application/session/session-scopes.ts"],"names":[],"mappings":""}
@@ -22,6 +22,12 @@ export interface Session {
22
22
  /** IANA time zone, for example `Europe/Paris`. */
23
23
  readonly timeZone?: string;
24
24
  readonly client?: SessionClient;
25
+ /**
26
+ * What the credential allows whoever presents it (the OAuth 2.0 `scope` of the token): a
27
+ * limit on the client, checked at the edge (`HasScope`), never what the actor may do, which
28
+ * stays a permission of the use case. Absent when the credential carries none.
29
+ */
30
+ readonly scopes?: readonly string[];
25
31
  readonly extensions: SessionExtensionValues;
26
32
  }
27
33
  //# sourceMappingURL=session.d.ts.map