@entifix/core 0.5.0 → 0.6.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 (61) hide show
  1. package/dist/command-palette/command-matching.d.ts +1 -1
  2. package/dist/command-palette/command-matching.js +1 -1
  3. package/dist/command-palette/command-matching.js.map +1 -1
  4. package/dist/command-palette/command-source.d.ts +3 -3
  5. package/dist/command-palette/command-source.js.map +1 -1
  6. package/dist/entity-definition/decorators/use-case/use-case.d.ts +1 -1
  7. package/dist/entity-definition/decorators/use-case/use-case.js +1 -1
  8. package/dist/entity-definition/decorators/use-case/use-case.js.map +1 -1
  9. package/dist/entity-definition/describe/describe-child-columns.d.ts +1 -1
  10. package/dist/entity-definition/describe/describe-child-columns.js +1 -1
  11. package/dist/entity-definition/describe/describe-child-columns.js.map +1 -1
  12. package/dist/entity-definition/describe/describe-entity-use-cases.d.ts +2 -1
  13. package/dist/entity-definition/describe/describe-entity-use-cases.d.ts.map +1 -1
  14. package/dist/entity-definition/describe/describe-entity-use-cases.js +2 -1
  15. package/dist/entity-definition/describe/describe-entity-use-cases.js.map +1 -1
  16. package/dist/entity-definition/helpers/entity-definition-helpers.d.ts +2 -2
  17. package/dist/entity-definition/helpers/entity-definition-helpers.js +2 -2
  18. package/dist/entity-definition/helpers/entity-definition-helpers.js.map +1 -1
  19. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.d.ts +4 -4
  20. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.js.map +1 -1
  21. package/dist/entity-definition/meta-entities/meta-use-case/meta-use-case.d.ts +1 -1
  22. package/dist/entity-definition/meta-entities/meta-use-case/meta-use-case.js.map +1 -1
  23. package/dist/entity-definition/reconstruct/clone-entity-draft.d.ts +1 -1
  24. package/dist/entity-definition/reconstruct/clone-entity-draft.js +1 -1
  25. package/dist/entity-definition/reconstruct/clone-entity-draft.js.map +1 -1
  26. package/dist/entity-definition/reconstruct/reconstruct-entity.js +1 -1
  27. package/dist/entity-definition/reconstruct/reconstruct-entity.js.map +1 -1
  28. package/dist/envelope/types.d.ts +5 -7
  29. package/dist/envelope/types.d.ts.map +1 -1
  30. package/dist/envelope/types.js.map +1 -1
  31. package/dist/http/session-cookies.d.ts +2 -3
  32. package/dist/http/session-cookies.d.ts.map +1 -1
  33. package/dist/http/session-cookies.js +2 -3
  34. package/dist/http/session-cookies.js.map +1 -1
  35. package/dist/i18n/locales.js +2 -2
  36. package/dist/i18n/locales.js.map +1 -1
  37. package/dist/types/BulkOutcome.d.ts +1 -2
  38. package/dist/types/BulkOutcome.d.ts.map +1 -1
  39. package/dist/types/BulkOutcome.js.map +1 -1
  40. package/dist/types/EntityAction.d.ts +1 -2
  41. package/dist/types/EntityAction.d.ts.map +1 -1
  42. package/dist/types/EntityAction.js +1 -2
  43. package/dist/types/EntityAction.js.map +1 -1
  44. package/dist/types/EntityDraft.d.ts +2 -3
  45. package/dist/types/EntityDraft.d.ts.map +1 -1
  46. package/dist/types/EntityDraft.js.map +1 -1
  47. package/dist/types/EntityRowDraft.d.ts +2 -3
  48. package/dist/types/EntityRowDraft.d.ts.map +1 -1
  49. package/dist/types/EntityRowDraft.js +1 -1
  50. package/dist/types/EntityRowDraft.js.map +1 -1
  51. package/dist/types/EntitySelection.d.ts +2 -3
  52. package/dist/types/EntitySelection.d.ts.map +1 -1
  53. package/dist/types/EntitySelection.js.map +1 -1
  54. package/dist/wizard/wizard-definition.d.ts +5 -8
  55. package/dist/wizard/wizard-definition.d.ts.map +1 -1
  56. package/dist/wizard/wizard-definition.js +3 -5
  57. package/dist/wizard/wizard-definition.js.map +1 -1
  58. package/dist/wizard/wizard-state.d.ts +4 -7
  59. package/dist/wizard/wizard-state.d.ts.map +1 -1
  60. package/dist/wizard/wizard-state.js.map +1 -1
  61. package/package.json +1 -1
@@ -18,15 +18,13 @@ import type { EntityLoadRequest } from '../types/EntityLoadRequest.js';
18
18
  * carry four shapes.** `transactionEvent` used to mean all of them, and a
19
19
  * discriminant that means several things asserts nothing when it is read —
20
20
  * which is what `readTransactionEventEnvelope` was doing on the `202`: checking
21
- * a name that the record route and the event type both also answered to
22
- * ([ADR 0055](../../../../../../docs/adr/0055-a-coordinator-resumes-from-its-own-record.md),
23
- * #176).
21
+ * a name that the record route and the event type both also answered to.
24
22
  *
25
23
  * - `transactionAccepted` — the `202` accept body, `{ transactionId, state }`.
26
24
  * Not a record: it has no `createdAt` and names no entity id yet.
27
25
  * - `transactionRecord` — the fold a client polls, a `TransactionRecord`.
28
26
  * - `sagaResult` — how a settled multi-step walk ended: which steps ran and
29
- * what each returned ([ADR 0052](../../../../../../docs/adr/0052-the-checkout-saga.md)).
27
+ * what each returned.
30
28
  * - `sagaInstance` — the durable instance itself, which is what a resumable
31
29
  * coordinator reads and what `GET /api/saga/:id` serves. It differs from
32
30
  * `sagaResult` in carrying the inputs and the resume count, because those are
@@ -81,7 +79,7 @@ export interface EntifixEventMeta {
81
79
  * makes a transaction's `completed` look like a duplicate of its `accepted`.
82
80
  */
83
81
  id: string;
84
- /** The emitting **slice** (ADR 0020's ownership noun), e.g. `marketplace-admin`. */
82
+ /** The emitting **slice** — the unit that owns a store — e.g. `catalog`. */
85
83
  source: string;
86
84
  /** ISO-8601 emission time. */
87
85
  at: string;
@@ -109,7 +107,7 @@ export interface EntifixEnvelopeMeta {
109
107
  *
110
108
  * Generic in the payload so a publisher keeps its own shape —
111
109
  * `DomainEvent<TransactionEvent>` today, `DomainEvent<PublishedOffering>` when
112
- * ADR 0009's projection event lands — without the transport learning either.
110
+ * a projection's event lands — without the transport learning either.
113
111
  */
114
112
  export interface DomainEvent<TData = unknown> extends EntifixEventMeta {
115
113
  data: TData;
@@ -117,7 +115,7 @@ export interface DomainEvent<TData = unknown> extends EntifixEventMeta {
117
115
  /**
118
116
  * A change to an entity that happened on the server, delivered out-of-band —
119
117
  * the `data` of a bus/stream `event` envelope rather than a framing of its own
120
- * (ADR 0036). `entity` is the `key ?? name` an adapter routes on, the same
118
+ *. `entity` is the `key ?? name` an adapter routes on, the same
121
119
  * string `entityQueryScope` uses, so a subscriber can invalidate exactly the
122
120
  * affected query keys.
123
121
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/envelope/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,kBAAkB,GAClB,YAAY,GACZ,SAAS,GACT,OAAO,GACP,qBAAqB,GACrB,mBAAmB,GACnB,YAAY,GACZ,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,mBAAmB,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC9B,qCAAqC;IACrC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,OAAO,CAAE,SAAQ,gBAAgB;IACpE,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1C,EAAE,EAAE,QAAQ,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK,GAAG,OAAO;IAC9C,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM;IACnE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAC5D,eAAe,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AACjD,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,CAAC,CAAC;AAE7E;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG;IACpE,IAAI,EAAE,mBAAmB,GAAG;QAAE,KAAK,EAAE,gBAAgB,CAAA;KAAE,CAAC;CACzD,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/envelope/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,MAAM,mBAAmB,GAC3B,QAAQ,GACR,kBAAkB,GAClB,YAAY,GACZ,SAAS,GACT,OAAO,GACP,qBAAqB,GACrB,mBAAmB,GACnB,YAAY,GACZ,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEtE;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,EAAE,EAAE,MAAM,CAAC;IACX,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,mBAAmB,CAAC;IAC1B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAC9B,qCAAqC;IACrC,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,OAAO,CAAE,SAAQ,gBAAgB;IACpE,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC1C,EAAE,EAAE,QAAQ,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe,CAAC,KAAK,GAAG,OAAO;IAC9C,IAAI,EAAE,mBAAmB,CAAC;IAC1B,IAAI,EAAE,KAAK,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM;IACnE,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,MAAM,cAAc,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;AAC/D,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,CAAC,OAAO,SAAS,MAAM,GAAG,MAAM,IAC5D,eAAe,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;AACjD,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC,sBAAsB,CAAC,CAAC;AAE7E;;;GAGG;AACH,MAAM,MAAM,aAAa,CAAC,KAAK,GAAG,OAAO,IAAI,eAAe,CAAC,KAAK,CAAC,GAAG;IACpE,IAAI,EAAE,mBAAmB,GAAG;QAAE,KAAK,EAAE,gBAAgB,CAAA;KAAE,CAAC;CACzD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/envelope/types.ts"],"sourcesContent":["import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport type { SerializedEntity } from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityId } from '../types/Entity.js';\nimport type { EntityLoadRequest } from '../types/EntityLoadRequest.js';\n\n/**\n * Discriminates what {@link EntifixEnvelope.data} carries.\n *\n * `command` extends the contract for the transactions layer: a write is issued\n * as a `command`, whose `data` shape lives in `@entifix/transactions` —\n * core only owns the discriminant so every artifact agrees on it.\n *\n * `event` is what actually rides the bus, and it is deliberately not named\n * after one publisher's flow: once a message can be `catalog.published` as\n * easily as `transaction.completed`, `transactionEvent` would be the wrong\n * name for it.\n *\n * ⚠️ **The four transaction-shaped discriminants below are four because they\n * carry four shapes.** `transactionEvent` used to mean all of them, and a\n * discriminant that means several things asserts nothing when it is read —\n * which is what `readTransactionEventEnvelope` was doing on the `202`: checking\n * a name that the record route and the event type both also answered to\n * ([ADR 0055](../../../../../../docs/adr/0055-a-coordinator-resumes-from-its-own-record.md),\n * #176).\n *\n * - `transactionAccepted` — the `202` accept body, `{ transactionId, state }`.\n * Not a record: it has no `createdAt` and names no entity id yet.\n * - `transactionRecord` — the fold a client polls, a `TransactionRecord`.\n * - `sagaResult` — how a settled multi-step walk ended: which steps ran and\n * what each returned ([ADR 0052](../../../../../../docs/adr/0052-the-checkout-saga.md)).\n * - `sagaInstance` — the durable instance itself, which is what a resumable\n * coordinator reads and what `GET /api/saga/:id` serves. It differs from\n * `sagaResult` in carrying the inputs and the resume count, because those are\n * what an operator needs to answer *why is this one still here*.\n *\n * `entityMetadata` is the same extension made once more, for the action model:\n * its `data` is an {@link EntityMetadataDocument}, which core does own because\n * both the service that computes it and the controls that render it are already\n * below the layer that would otherwise host it.\n */\nexport type EntifixEnvelopeType =\n | 'entity'\n | 'entityCollection'\n | 'entityPage'\n | 'command'\n | 'event'\n | 'transactionAccepted'\n | 'transactionRecord'\n | 'sagaResult'\n | 'sagaInstance'\n | 'entityMetadata';\n\nexport type EntifixEnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n/**\n * A HATEOAS affordance: where the peer can go next from this message. Optional —\n * the envelope stands on its own without links, and only the artifact that owns\n * a route surface (today the REST service) is able to fill them in.\n */\nexport interface EntifixEnvelopeLink {\n rel: string;\n href: string;\n method?: EntifixEnvelopeMethod;\n}\n\n/**\n * The facts a message carries about *itself*, as opposed to about what\n * happened. Present only when the envelope is a bus message.\n *\n * The split rule: `meta` describes the message, `data` describes the\n * occurrence. So `correlationId` belongs here and an outcome's `code` does not.\n *\n * **`source` is never a consumer branch.** It is there for routing,\n * observability and audit. A handler that behaves differently depending on who\n * published re-couples the two services the bus decoupled — which is why .NET's\n * canonical `(sender, eventArgs)` types its sender as bare `object`, making the\n * dependency awkward on purpose. TypeScript cannot reproduce that friction, so\n * here it is a rule rather than a type.\n */\nexport interface EntifixEventMeta {\n /**\n * What happened, in the register's vocabulary: `catalog.published`,\n * `transaction.completed`. Declared in `tools/slices/*.slice.ts` as\n * `publishedEvents`/`subscriptions`, and used verbatim as the AMQP routing\n * key — which is what makes a subscriber's declared interest and its actual\n * queue binding the same string.\n */\n name: string;\n /**\n * Unique per message, and **the** deduplication key. Delivery is\n * at-least-once, so a consumer that must not fold twice keys on this.\n *\n * Not `correlationId`: one flow emits several messages, so correlating and\n * deduplicating are different questions. Keying dedup on the correlation id\n * makes a transaction's `completed` look like a duplicate of its `accepted`.\n */\n id: string;\n /** The emitting **slice** (ADR 0020's ownership noun), e.g. `marketplace-admin`. */\n source: string;\n /** ISO-8601 emission time. */\n at: string;\n /** Ties every message of one flow together — a transaction id, a saga id. */\n correlationId?: string;\n}\n\nexport interface EntifixEnvelopeMeta {\n type: EntifixEnvelopeType;\n /**\n * The target entity's `key` (falling back to its class name).\n *\n * Optional, because a bus message need not be about an entity at all —\n * `settlement.run.completed` is about a run. What a message *is* lives in\n * {@link EntifixEventMeta.name}; this stays the entity label the HTTP arm\n * routes on.\n */\n entity?: string;\n links?: EntifixEnvelopeLink[];\n /** Present only on a bus message. */\n event?: EntifixEventMeta;\n}\n\n/**\n * One message on the bus: its own metadata, plus the payload describing what\n * happened.\n *\n * Generic in the payload so a publisher keeps its own shape —\n * `DomainEvent<TransactionEvent>` today, `DomainEvent<PublishedOffering>` when\n * ADR 0009's projection event lands — without the transport learning either.\n */\nexport interface DomainEvent<TData = unknown> extends EntifixEventMeta {\n data: TData;\n}\n\n/**\n * A change to an entity that happened on the server, delivered out-of-band —\n * the `data` of a bus/stream `event` envelope rather than a framing of its own\n * (ADR 0036). `entity` is the `key ?? name` an adapter routes on, the same\n * string `entityQueryScope` uses, so a subscriber can invalidate exactly the\n * affected query keys.\n *\n * It lives in core rather than beside the React `ReactiveChannel` that consumes\n * it because both ends need it: the service maps a `TransactionEvent` onto it,\n * and the React packages sit above every tier that does the mapping.\n *\n * Deliberately **no** `transactionId`, timestamp or sequence: all three already\n * exist one level up as `meta.event.correlationId`, `.at` and `.id`, and\n * duplicating them onto the payload is how two sources of the same fact drift.\n */\nexport interface EntityChangeEvent {\n entity: string;\n change: 'created' | 'updated' | 'deleted';\n id: EntityId;\n}\n\n/**\n * The standard message exchanged between entifix artifacts.\n *\n * Every request and response body is an envelope: `meta` describes the payload,\n * `data` is the payload. It is deliberately transport-free — it lives in `core`\n * and knows nothing about HTTP — so the same contract can carry entities over\n * REST today and amqp or websockets later.\n */\nexport interface EntifixEnvelope<TData = unknown> {\n meta: EntifixEnvelopeMeta;\n data: TData;\n}\n\n/**\n * `data` shape when `meta.type` is `entityPage`. Generic in the entity so the\n * echoed `request` keeps its `keyof TEntity` filter/sort types — `EntityLoadRequest`\n * is invariant in its entity, so a non-generic `EntityLoadRequest<Entity>` here\n * would not accept a caller's `EntityLoadRequest<Product>`.\n */\nexport interface SerializedEntityPage<TEntity extends Entity = Entity> {\n items: SerializedEntity[];\n total: number;\n request?: EntityLoadRequest<TEntity>;\n}\n\nexport type EntityEnvelope = EntifixEnvelope<SerializedEntity>;\nexport type EntityCollectionEnvelope = EntifixEnvelope<SerializedEntity[]>;\nexport type EntityPageEnvelope<TEntity extends Entity = Entity> =\n EntifixEnvelope<SerializedEntityPage<TEntity>>;\nexport type EntityMetadataEnvelope = EntifixEnvelope<EntityMetadataDocument>;\n\n/**\n * A bus message on the wire. `meta.event` is always populated, and\n * `readEventEnvelope` is what proves it before a consumer sees the payload.\n */\nexport type EventEnvelope<TData = unknown> = EntifixEnvelope<TData> & {\n meta: EntifixEnvelopeMeta & { event: EntifixEventMeta };\n};\n"],"names":[],"mappings":"AAyLA;;;CAGC,GACD,WAEE"}
1
+ {"version":3,"sources":["../../src/envelope/types.ts"],"sourcesContent":["import type { EntityMetadataDocument } from '../entity-definition/metadata/index.js';\nimport type { SerializedEntity } from '../entity-definition/serializer/index.js';\nimport type { Entity, EntityId } from '../types/Entity.js';\nimport type { EntityLoadRequest } from '../types/EntityLoadRequest.js';\n\n/**\n * Discriminates what {@link EntifixEnvelope.data} carries.\n *\n * `command` extends the contract for the transactions layer: a write is issued\n * as a `command`, whose `data` shape lives in `@entifix/transactions` —\n * core only owns the discriminant so every artifact agrees on it.\n *\n * `event` is what actually rides the bus, and it is deliberately not named\n * after one publisher's flow: once a message can be `catalog.published` as\n * easily as `transaction.completed`, `transactionEvent` would be the wrong\n * name for it.\n *\n * ⚠️ **The four transaction-shaped discriminants below are four because they\n * carry four shapes.** `transactionEvent` used to mean all of them, and a\n * discriminant that means several things asserts nothing when it is read —\n * which is what `readTransactionEventEnvelope` was doing on the `202`: checking\n * a name that the record route and the event type both also answered to.\n *\n * - `transactionAccepted` — the `202` accept body, `{ transactionId, state }`.\n * Not a record: it has no `createdAt` and names no entity id yet.\n * - `transactionRecord` — the fold a client polls, a `TransactionRecord`.\n * - `sagaResult` — how a settled multi-step walk ended: which steps ran and\n * what each returned.\n * - `sagaInstance` — the durable instance itself, which is what a resumable\n * coordinator reads and what `GET /api/saga/:id` serves. It differs from\n * `sagaResult` in carrying the inputs and the resume count, because those are\n * what an operator needs to answer *why is this one still here*.\n *\n * `entityMetadata` is the same extension made once more, for the action model:\n * its `data` is an {@link EntityMetadataDocument}, which core does own because\n * both the service that computes it and the controls that render it are already\n * below the layer that would otherwise host it.\n */\nexport type EntifixEnvelopeType =\n | 'entity'\n | 'entityCollection'\n | 'entityPage'\n | 'command'\n | 'event'\n | 'transactionAccepted'\n | 'transactionRecord'\n | 'sagaResult'\n | 'sagaInstance'\n | 'entityMetadata';\n\nexport type EntifixEnvelopeMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\n/**\n * A HATEOAS affordance: where the peer can go next from this message. Optional —\n * the envelope stands on its own without links, and only the artifact that owns\n * a route surface (today the REST service) is able to fill them in.\n */\nexport interface EntifixEnvelopeLink {\n rel: string;\n href: string;\n method?: EntifixEnvelopeMethod;\n}\n\n/**\n * The facts a message carries about *itself*, as opposed to about what\n * happened. Present only when the envelope is a bus message.\n *\n * The split rule: `meta` describes the message, `data` describes the\n * occurrence. So `correlationId` belongs here and an outcome's `code` does not.\n *\n * **`source` is never a consumer branch.** It is there for routing,\n * observability and audit. A handler that behaves differently depending on who\n * published re-couples the two services the bus decoupled — which is why .NET's\n * canonical `(sender, eventArgs)` types its sender as bare `object`, making the\n * dependency awkward on purpose. TypeScript cannot reproduce that friction, so\n * here it is a rule rather than a type.\n */\nexport interface EntifixEventMeta {\n /**\n * What happened, in the register's vocabulary: `catalog.published`,\n * `transaction.completed`. Declared in `tools/slices/*.slice.ts` as\n * `publishedEvents`/`subscriptions`, and used verbatim as the AMQP routing\n * key — which is what makes a subscriber's declared interest and its actual\n * queue binding the same string.\n */\n name: string;\n /**\n * Unique per message, and **the** deduplication key. Delivery is\n * at-least-once, so a consumer that must not fold twice keys on this.\n *\n * Not `correlationId`: one flow emits several messages, so correlating and\n * deduplicating are different questions. Keying dedup on the correlation id\n * makes a transaction's `completed` look like a duplicate of its `accepted`.\n */\n id: string;\n /** The emitting **slice** — the unit that owns a store — e.g. `catalog`. */\n source: string;\n /** ISO-8601 emission time. */\n at: string;\n /** Ties every message of one flow together — a transaction id, a saga id. */\n correlationId?: string;\n}\n\nexport interface EntifixEnvelopeMeta {\n type: EntifixEnvelopeType;\n /**\n * The target entity's `key` (falling back to its class name).\n *\n * Optional, because a bus message need not be about an entity at all —\n * `settlement.run.completed` is about a run. What a message *is* lives in\n * {@link EntifixEventMeta.name}; this stays the entity label the HTTP arm\n * routes on.\n */\n entity?: string;\n links?: EntifixEnvelopeLink[];\n /** Present only on a bus message. */\n event?: EntifixEventMeta;\n}\n\n/**\n * One message on the bus: its own metadata, plus the payload describing what\n * happened.\n *\n * Generic in the payload so a publisher keeps its own shape —\n * `DomainEvent<TransactionEvent>` today, `DomainEvent<PublishedOffering>` when\n * a projection's event lands — without the transport learning either.\n */\nexport interface DomainEvent<TData = unknown> extends EntifixEventMeta {\n data: TData;\n}\n\n/**\n * A change to an entity that happened on the server, delivered out-of-band —\n * the `data` of a bus/stream `event` envelope rather than a framing of its own\n *. `entity` is the `key ?? name` an adapter routes on, the same\n * string `entityQueryScope` uses, so a subscriber can invalidate exactly the\n * affected query keys.\n *\n * It lives in core rather than beside the React `ReactiveChannel` that consumes\n * it because both ends need it: the service maps a `TransactionEvent` onto it,\n * and the React packages sit above every tier that does the mapping.\n *\n * Deliberately **no** `transactionId`, timestamp or sequence: all three already\n * exist one level up as `meta.event.correlationId`, `.at` and `.id`, and\n * duplicating them onto the payload is how two sources of the same fact drift.\n */\nexport interface EntityChangeEvent {\n entity: string;\n change: 'created' | 'updated' | 'deleted';\n id: EntityId;\n}\n\n/**\n * The standard message exchanged between entifix artifacts.\n *\n * Every request and response body is an envelope: `meta` describes the payload,\n * `data` is the payload. It is deliberately transport-free — it lives in `core`\n * and knows nothing about HTTP — so the same contract can carry entities over\n * REST today and amqp or websockets later.\n */\nexport interface EntifixEnvelope<TData = unknown> {\n meta: EntifixEnvelopeMeta;\n data: TData;\n}\n\n/**\n * `data` shape when `meta.type` is `entityPage`. Generic in the entity so the\n * echoed `request` keeps its `keyof TEntity` filter/sort types — `EntityLoadRequest`\n * is invariant in its entity, so a non-generic `EntityLoadRequest<Entity>` here\n * would not accept a caller's `EntityLoadRequest<Product>`.\n */\nexport interface SerializedEntityPage<TEntity extends Entity = Entity> {\n items: SerializedEntity[];\n total: number;\n request?: EntityLoadRequest<TEntity>;\n}\n\nexport type EntityEnvelope = EntifixEnvelope<SerializedEntity>;\nexport type EntityCollectionEnvelope = EntifixEnvelope<SerializedEntity[]>;\nexport type EntityPageEnvelope<TEntity extends Entity = Entity> =\n EntifixEnvelope<SerializedEntityPage<TEntity>>;\nexport type EntityMetadataEnvelope = EntifixEnvelope<EntityMetadataDocument>;\n\n/**\n * A bus message on the wire. `meta.event` is always populated, and\n * `readEventEnvelope` is what proves it before a consumer sees the payload.\n */\nexport type EventEnvelope<TData = unknown> = EntifixEnvelope<TData> & {\n meta: EntifixEnvelopeMeta & { event: EntifixEventMeta };\n};\n"],"names":[],"mappings":"AAuLA;;;CAGC,GACD,WAEE"}
@@ -9,9 +9,8 @@
9
9
  * configured in one bundle would never reach the others: the middleware would
10
10
  * look for one name while a route wrote another, and the only symptom is being
11
11
  * signed out. So the names are fixed and shared, and every reader imports them
12
- * from here instead of re-declaring a literal — which is how r10c had five
13
- * separate copies of `r10c_at` that nothing kept in step
14
- * ([ADR 0059](../../../../../../docs/adr/0059-entifix-leaves-the-repo.md)).
12
+ * from here instead of re-declaring a literal — which is how one host once had
13
+ * five separate copies of its access-cookie name that nothing kept in step.
15
14
  *
16
15
  * Host-scoped in dev, where localhost shares cookies across ports, so every app
17
16
  * and service in a local fleet reads the same session.
@@ -1 +1 @@
1
- {"version":3,"file":"session-cookies.d.ts","sourceRoot":"","sources":["../../src/http/session-cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,qEAAqE;AACrE,eAAO,MAAM,aAAa,eAAe,CAAC;AAE1C,kFAAkF;AAClF,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,aAAa,gBAAgB,CAAC"}
1
+ {"version":3,"file":"session-cookies.d.ts","sourceRoot":"","sources":["../../src/http/session-cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,qEAAqE;AACrE,eAAO,MAAM,aAAa,eAAe,CAAC;AAE1C,kFAAkF;AAClF,eAAO,MAAM,cAAc,gBAAgB,CAAC;AAE5C,6EAA6E;AAC7E,eAAO,MAAM,aAAa,gBAAgB,CAAC"}
@@ -9,9 +9,8 @@
9
9
  * configured in one bundle would never reach the others: the middleware would
10
10
  * look for one name while a route wrote another, and the only symptom is being
11
11
  * signed out. So the names are fixed and shared, and every reader imports them
12
- * from here instead of re-declaring a literal — which is how r10c had five
13
- * separate copies of `r10c_at` that nothing kept in step
14
- * ([ADR 0059](../../../../../../docs/adr/0059-entifix-leaves-the-repo.md)).
12
+ * from here instead of re-declaring a literal — which is how one host once had
13
+ * five separate copies of its access-cookie name that nothing kept in step.
15
14
  *
16
15
  * Host-scoped in dev, where localhost shares cookies across ports, so every app
17
16
  * and service in a local fleet reads the same session.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/http/session-cookies.ts"],"sourcesContent":["/**\n * The names of the cookies an entifix application's session travels in.\n *\n * ⚠️ **Constants in one place, deliberately not a runtime option.** Three\n * packages read or write these — the Effect service shell verifies the access\n * token, the Next shell sets and clears all three, and the Playwright helpers\n * seed them — and a Next application runs its middleware, its server and its\n * client as separate bundles, each with its own module state. A prefix a host\n * configured in one bundle would never reach the others: the middleware would\n * look for one name while a route wrote another, and the only symptom is being\n * signed out. So the names are fixed and shared, and every reader imports them\n * from here instead of re-declaring a literal — which is how r10c had five\n * separate copies of `r10c_at` that nothing kept in step\n * ([ADR 0059](../../../../../../docs/adr/0059-entifix-leaves-the-repo.md)).\n *\n * Host-scoped in dev, where localhost shares cookies across ports, so every app\n * and service in a local fleet reads the same session.\n */\n\n/** Short-lived signed access token the services verify. httpOnly. */\nexport const ACCESS_COOKIE = 'entifix_at';\n\n/** Opaque session id — the revocation handle and the refresh source. httpOnly. */\nexport const SESSION_COOKIE = 'entifix_sid';\n\n/** Long-lived, opaque device id. Cleared with cookies — that is intended. */\nexport const DEVICE_COOKIE = 'entifix_did';\n"],"names":["ACCESS_COOKIE","SESSION_COOKIE","DEVICE_COOKIE"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GAED,mEAAmE,GACnE,OAAO,MAAMA,gBAAgB,aAAa;AAE1C,gFAAgF,GAChF,OAAO,MAAMC,iBAAiB,cAAc;AAE5C,2EAA2E,GAC3E,OAAO,MAAMC,gBAAgB,cAAc"}
1
+ {"version":3,"sources":["../../src/http/session-cookies.ts"],"sourcesContent":["/**\n * The names of the cookies an entifix application's session travels in.\n *\n * ⚠️ **Constants in one place, deliberately not a runtime option.** Three\n * packages read or write these — the Effect service shell verifies the access\n * token, the Next shell sets and clears all three, and the Playwright helpers\n * seed them — and a Next application runs its middleware, its server and its\n * client as separate bundles, each with its own module state. A prefix a host\n * configured in one bundle would never reach the others: the middleware would\n * look for one name while a route wrote another, and the only symptom is being\n * signed out. So the names are fixed and shared, and every reader imports them\n * from here instead of re-declaring a literal — which is how one host once had\n * five separate copies of its access-cookie name that nothing kept in step.\n *\n * Host-scoped in dev, where localhost shares cookies across ports, so every app\n * and service in a local fleet reads the same session.\n */\n\n/** Short-lived signed access token the services verify. httpOnly. */\nexport const ACCESS_COOKIE = 'entifix_at';\n\n/** Opaque session id — the revocation handle and the refresh source. httpOnly. */\nexport const SESSION_COOKIE = 'entifix_sid';\n\n/** Long-lived, opaque device id. Cleared with cookies — that is intended. */\nexport const DEVICE_COOKIE = 'entifix_did';\n"],"names":["ACCESS_COOKIE","SESSION_COOKIE","DEVICE_COOKIE"],"mappings":"AAAA;;;;;;;;;;;;;;;;CAgBC,GAED,mEAAmE,GACnE,OAAO,MAAMA,gBAAgB,aAAa;AAE1C,gFAAgF,GAChF,OAAO,MAAMC,iBAAiB,cAAc;AAE5C,2EAA2E,GAC3E,OAAO,MAAMC,gBAAgB,cAAc"}
@@ -1,8 +1,8 @@
1
1
  import { EntifixBuildError } from '../base-entities/entifix-error/index.js';
2
2
  /**
3
3
  * What a host gets when it declares nothing: English alone. Neutral rather than
4
- * any one adopter's choice — r10c, which once supplied the `es` default here,
5
- * now declares it like everyone else (#35).
4
+ * any one adopter's choice — the adopter that once supplied the `es` default
5
+ * here now declares it like everyone else (#35).
6
6
  */ const UNDECLARED = {
7
7
  locales: [
8
8
  'en'
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/i18n/locales.ts"],"sourcesContent":["import { EntifixBuildError } from '../base-entities/entifix-error/index.js';\n\n/**\n * Where a host narrows {@link Locale} to the locales it ships:\n *\n * ```ts\n * declare module '@entifix/core' {\n * interface LocaleRegister {\n * locale: 'es' | 'en';\n * }\n * }\n * ```\n *\n * The same mechanism i18next's `CustomTypeOptions` uses. Without it a locale is\n * any string, which is honest for a framework that does not know which\n * languages its host speaks.\n */\n// An empty interface is the point: a host merges its declaration into it.\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface LocaleRegister {}\n\n/** A locale the host declared, or any string when it declared none. */\nexport type Locale = LocaleRegister extends { locale: infer L extends string }\n ? L\n : string;\n\n/** What a host declares through {@link defineLocales}. */\nexport interface LocaleDefinition {\n /** Every locale the host ships, in the order a switcher offers them. */\n readonly locales: readonly Locale[];\n /**\n * The locale a visitor gets when nothing else decides, and the one a missing\n * catalog key falls back to.\n */\n readonly defaultLocale: Locale;\n}\n\n/**\n * What a host gets when it declares nothing: English alone. Neutral rather than\n * any one adopter's choice — r10c, which once supplied the `es` default here,\n * now declares it like everyone else (#35).\n */\nconst UNDECLARED: LocaleDefinition = {\n locales: ['en' as Locale],\n defaultLocale: 'en' as Locale,\n};\n\nlet declared: LocaleDefinition = UNDECLARED;\n\n/**\n * Declares the host's locales. Call it once, from a module every entry point\n * imports — including a Next middleware, which is bundled on its own and shares\n * no module state with the server.\n *\n * Read at call time by every helper below and by `@entifix/i18n`,\n * `@entifix/react-controls` and `@entifix/next-i18n`, so a host declaring\n * `['en', 'es']` with `en` first gets English as the fallback everywhere, and a\n * third locale needs no change to entifix.\n */\nexport function defineLocales(definition: LocaleDefinition): void {\n const { locales: list, defaultLocale: fallback } = definition;\n if (list.length === 0) {\n throw new EntifixBuildError('defineLocales needs at least one locale.');\n }\n const duplicate = list.find(\n (locale, index) => list.indexOf(locale) !== index,\n );\n if (duplicate !== undefined) {\n throw new EntifixBuildError(\n `defineLocales was given \"${duplicate}\" more than once.`,\n );\n }\n if (!list.includes(fallback)) {\n throw new EntifixBuildError(\n `The default locale \"${fallback}\" is not one of ${JSON.stringify(list)}.`,\n );\n }\n declared = { locales: [...list], defaultLocale: fallback };\n}\n\n/** The locales the host declared. */\nexport function locales(): readonly Locale[] {\n return declared.locales;\n}\n\n/** The host's default locale. */\nexport function defaultLocale(): Locale {\n return declared.defaultLocale;\n}\n\n/** Forgets the host's declaration. For specs. */\nexport function resetLocales(): void {\n declared = UNDECLARED;\n}\n\n/**\n * Host-scoped so the choice is shared across the fleet's localhost ports in dev,\n * exactly like `entifix_at`/`entifix_sid`. Written by the locale middleware on every\n * redirect, so a visitor who lands on `/en/...` keeps English on the next visit.\n */\nexport const LOCALE_COOKIE = 'entifix_locale';\n\n/**\n * Request header the locale middleware sets when it rewrites a prefixed path.\n * Server components read it instead of a route param, which is what lets the\n * route tree stay free of an `app/[locale]` segment.\n */\nexport const LOCALE_HEADER = 'x-entifix-locale';\n\nexport function isLocale(value: unknown): value is Locale {\n return (\n typeof value === 'string' &&\n (declared.locales as readonly string[]).includes(value)\n );\n}\n"],"names":["EntifixBuildError","UNDECLARED","locales","defaultLocale","declared","defineLocales","definition","list","fallback","length","duplicate","find","locale","index","indexOf","undefined","includes","JSON","stringify","resetLocales","LOCALE_COOKIE","LOCALE_HEADER","isLocale","value"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,0CAA0C;AAqC5E;;;;CAIC,GACD,MAAMC,aAA+B;IACnCC,SAAS;QAAC;KAAe;IACzBC,eAAe;AACjB;AAEA,IAAIC,WAA6BH;AAEjC;;;;;;;;;CASC,GACD,OAAO,SAASI,cAAcC,UAA4B;IACxD,MAAM,EAAEJ,SAASK,IAAI,EAAEJ,eAAeK,QAAQ,EAAE,GAAGF;IACnD,IAAIC,KAAKE,MAAM,KAAK,GAAG;QACrB,MAAM,IAAIT,kBAAkB;IAC9B;IACA,MAAMU,YAAYH,KAAKI,IAAI,CACzB,CAACC,QAAQC,QAAUN,KAAKO,OAAO,CAACF,YAAYC;IAE9C,IAAIH,cAAcK,WAAW;QAC3B,MAAM,IAAIf,kBACR,CAAC,yBAAyB,EAAEU,UAAU,iBAAiB,CAAC;IAE5D;IACA,IAAI,CAACH,KAAKS,QAAQ,CAACR,WAAW;QAC5B,MAAM,IAAIR,kBACR,CAAC,oBAAoB,EAAEQ,SAAS,gBAAgB,EAAES,KAAKC,SAAS,CAACX,MAAM,CAAC,CAAC;IAE7E;IACAH,WAAW;QAAEF,SAAS;eAAIK;SAAK;QAAEJ,eAAeK;IAAS;AAC3D;AAEA,mCAAmC,GACnC,OAAO,SAASN;IACd,OAAOE,SAASF,OAAO;AACzB;AAEA,+BAA+B,GAC/B,OAAO,SAASC;IACd,OAAOC,SAASD,aAAa;AAC/B;AAEA,+CAA+C,GAC/C,OAAO,SAASgB;IACdf,WAAWH;AACb;AAEA;;;;CAIC,GACD,OAAO,MAAMmB,gBAAgB,iBAAiB;AAE9C;;;;CAIC,GACD,OAAO,MAAMC,gBAAgB,mBAAmB;AAEhD,OAAO,SAASC,SAASC,KAAc;IACrC,OACE,OAAOA,UAAU,YACjB,AAACnB,SAASF,OAAO,CAAuBc,QAAQ,CAACO;AAErD"}
1
+ {"version":3,"sources":["../../src/i18n/locales.ts"],"sourcesContent":["import { EntifixBuildError } from '../base-entities/entifix-error/index.js';\n\n/**\n * Where a host narrows {@link Locale} to the locales it ships:\n *\n * ```ts\n * declare module '@entifix/core' {\n * interface LocaleRegister {\n * locale: 'es' | 'en';\n * }\n * }\n * ```\n *\n * The same mechanism i18next's `CustomTypeOptions` uses. Without it a locale is\n * any string, which is honest for a framework that does not know which\n * languages its host speaks.\n */\n// An empty interface is the point: a host merges its declaration into it.\n// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type\nexport interface LocaleRegister {}\n\n/** A locale the host declared, or any string when it declared none. */\nexport type Locale = LocaleRegister extends { locale: infer L extends string }\n ? L\n : string;\n\n/** What a host declares through {@link defineLocales}. */\nexport interface LocaleDefinition {\n /** Every locale the host ships, in the order a switcher offers them. */\n readonly locales: readonly Locale[];\n /**\n * The locale a visitor gets when nothing else decides, and the one a missing\n * catalog key falls back to.\n */\n readonly defaultLocale: Locale;\n}\n\n/**\n * What a host gets when it declares nothing: English alone. Neutral rather than\n * any one adopter's choice — the adopter that once supplied the `es` default\n * here now declares it like everyone else (#35).\n */\nconst UNDECLARED: LocaleDefinition = {\n locales: ['en' as Locale],\n defaultLocale: 'en' as Locale,\n};\n\nlet declared: LocaleDefinition = UNDECLARED;\n\n/**\n * Declares the host's locales. Call it once, from a module every entry point\n * imports — including a Next middleware, which is bundled on its own and shares\n * no module state with the server.\n *\n * Read at call time by every helper below and by `@entifix/i18n`,\n * `@entifix/react-controls` and `@entifix/next-i18n`, so a host declaring\n * `['en', 'es']` with `en` first gets English as the fallback everywhere, and a\n * third locale needs no change to entifix.\n */\nexport function defineLocales(definition: LocaleDefinition): void {\n const { locales: list, defaultLocale: fallback } = definition;\n if (list.length === 0) {\n throw new EntifixBuildError('defineLocales needs at least one locale.');\n }\n const duplicate = list.find(\n (locale, index) => list.indexOf(locale) !== index,\n );\n if (duplicate !== undefined) {\n throw new EntifixBuildError(\n `defineLocales was given \"${duplicate}\" more than once.`,\n );\n }\n if (!list.includes(fallback)) {\n throw new EntifixBuildError(\n `The default locale \"${fallback}\" is not one of ${JSON.stringify(list)}.`,\n );\n }\n declared = { locales: [...list], defaultLocale: fallback };\n}\n\n/** The locales the host declared. */\nexport function locales(): readonly Locale[] {\n return declared.locales;\n}\n\n/** The host's default locale. */\nexport function defaultLocale(): Locale {\n return declared.defaultLocale;\n}\n\n/** Forgets the host's declaration. For specs. */\nexport function resetLocales(): void {\n declared = UNDECLARED;\n}\n\n/**\n * Host-scoped so the choice is shared across the fleet's localhost ports in dev,\n * exactly like `entifix_at`/`entifix_sid`. Written by the locale middleware on every\n * redirect, so a visitor who lands on `/en/...` keeps English on the next visit.\n */\nexport const LOCALE_COOKIE = 'entifix_locale';\n\n/**\n * Request header the locale middleware sets when it rewrites a prefixed path.\n * Server components read it instead of a route param, which is what lets the\n * route tree stay free of an `app/[locale]` segment.\n */\nexport const LOCALE_HEADER = 'x-entifix-locale';\n\nexport function isLocale(value: unknown): value is Locale {\n return (\n typeof value === 'string' &&\n (declared.locales as readonly string[]).includes(value)\n );\n}\n"],"names":["EntifixBuildError","UNDECLARED","locales","defaultLocale","declared","defineLocales","definition","list","fallback","length","duplicate","find","locale","index","indexOf","undefined","includes","JSON","stringify","resetLocales","LOCALE_COOKIE","LOCALE_HEADER","isLocale","value"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,0CAA0C;AAqC5E;;;;CAIC,GACD,MAAMC,aAA+B;IACnCC,SAAS;QAAC;KAAe;IACzBC,eAAe;AACjB;AAEA,IAAIC,WAA6BH;AAEjC;;;;;;;;;CASC,GACD,OAAO,SAASI,cAAcC,UAA4B;IACxD,MAAM,EAAEJ,SAASK,IAAI,EAAEJ,eAAeK,QAAQ,EAAE,GAAGF;IACnD,IAAIC,KAAKE,MAAM,KAAK,GAAG;QACrB,MAAM,IAAIT,kBAAkB;IAC9B;IACA,MAAMU,YAAYH,KAAKI,IAAI,CACzB,CAACC,QAAQC,QAAUN,KAAKO,OAAO,CAACF,YAAYC;IAE9C,IAAIH,cAAcK,WAAW;QAC3B,MAAM,IAAIf,kBACR,CAAC,yBAAyB,EAAEU,UAAU,iBAAiB,CAAC;IAE5D;IACA,IAAI,CAACH,KAAKS,QAAQ,CAACR,WAAW;QAC5B,MAAM,IAAIR,kBACR,CAAC,oBAAoB,EAAEQ,SAAS,gBAAgB,EAAES,KAAKC,SAAS,CAACX,MAAM,CAAC,CAAC;IAE7E;IACAH,WAAW;QAAEF,SAAS;eAAIK;SAAK;QAAEJ,eAAeK;IAAS;AAC3D;AAEA,mCAAmC,GACnC,OAAO,SAASN;IACd,OAAOE,SAASF,OAAO;AACzB;AAEA,+BAA+B,GAC/B,OAAO,SAASC;IACd,OAAOC,SAASD,aAAa;AAC/B;AAEA,+CAA+C,GAC/C,OAAO,SAASgB;IACdf,WAAWH;AACb;AAEA;;;;CAIC,GACD,OAAO,MAAMmB,gBAAgB,iBAAiB;AAE9C;;;;CAIC,GACD,OAAO,MAAMC,gBAAgB,mBAAmB;AAEhD,OAAO,SAASC,SAASC,KAAc;IACrC,OACE,OAAOA,UAAU,YACjB,AAACnB,SAASF,OAAO,CAAuBc,QAAQ,CAACO;AAErD"}
@@ -11,8 +11,7 @@ import type { EntityId } from './Entity.js';
11
11
  * `code` is an **error code**, never a sentence. Services answer
12
12
  * `{ error, code, detail }` and the browser renders the code through the
13
13
  * `errors` catalog, so a failure reason stays translatable and a new one fails
14
- * `@r10c/i18n-check` rather than reaching a user in English
15
- * ([docs/I18N.md](../../../../../../docs/I18N.md)).
14
+ * a host's catalog check rather than reaching a user in English.
16
15
  */
17
16
  export interface BulkOutcome {
18
17
  id: EntityId;
@@ -1 +1 @@
1
- {"version":3,"file":"BulkOutcome.d.ts","sourceRoot":"","sources":["../../src/types/BulkOutcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,QAAQ,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,QAAQ,EAAE,CAEtE;AAED,kCAAkC;AAClC,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAEvE"}
1
+ {"version":3,"file":"BulkOutcome.d.ts","sourceRoot":"","sources":["../../src/types/BulkOutcome.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,QAAQ,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,oEAAoE;AACpE,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,QAAQ,EAAE,CAEtE;AAED,kCAAkC;AAClC,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAEvE"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/BulkOutcome.ts"],"sourcesContent":["import type { EntityId } from './Entity.js';\n\n/**\n * What happened to **one** row of a bulk action.\n *\n * Partial failure is the normal case, not an edge one: 40 rows selected, 3\n * refused. A single success/failure result for the whole run lies in both\n * directions — reported as a failure it hides the 37 that were written, and\n * reported as a success it hides the 3 that were not, which is the worse half\n * because the user walks away believing the work is done.\n *\n * `code` is an **error code**, never a sentence. Services answer\n * `{ error, code, detail }` and the browser renders the code through the\n * `errors` catalog, so a failure reason stays translatable and a new one fails\n * `@r10c/i18n-check` rather than reaching a user in English\n * ([docs/I18N.md](../../../../../../docs/I18N.md)).\n */\nexport interface BulkOutcome {\n id: EntityId;\n ok: boolean;\n /** Present only when `ok` is false. */\n code?: string;\n}\n\n/** The ids that failed — what a retry re-runs, and nothing else. */\nexport function failedIds(outcomes: readonly BulkOutcome[]): EntityId[] {\n return outcomes.filter(outcome => !outcome.ok).map(outcome => outcome.id);\n}\n\n/** How many rows were written. */\nexport function succeededCount(outcomes: readonly BulkOutcome[]): number {\n return outcomes.filter(outcome => outcome.ok).length;\n}\n"],"names":["failedIds","outcomes","filter","outcome","ok","map","id","succeededCount","length"],"mappings":"AAwBA,kEAAkE,GAClE,OAAO,SAASA,UAAUC,QAAgC;IACxD,OAAOA,SAASC,MAAM,CAACC,CAAAA,UAAW,CAACA,QAAQC,EAAE,EAAEC,GAAG,CAACF,CAAAA,UAAWA,QAAQG,EAAE;AAC1E;AAEA,gCAAgC,GAChC,OAAO,SAASC,eAAeN,QAAgC;IAC7D,OAAOA,SAASC,MAAM,CAACC,CAAAA,UAAWA,QAAQC,EAAE,EAAEI,MAAM;AACtD"}
1
+ {"version":3,"sources":["../../src/types/BulkOutcome.ts"],"sourcesContent":["import type { EntityId } from './Entity.js';\n\n/**\n * What happened to **one** row of a bulk action.\n *\n * Partial failure is the normal case, not an edge one: 40 rows selected, 3\n * refused. A single success/failure result for the whole run lies in both\n * directions — reported as a failure it hides the 37 that were written, and\n * reported as a success it hides the 3 that were not, which is the worse half\n * because the user walks away believing the work is done.\n *\n * `code` is an **error code**, never a sentence. Services answer\n * `{ error, code, detail }` and the browser renders the code through the\n * `errors` catalog, so a failure reason stays translatable and a new one fails\n * a host's catalog check rather than reaching a user in English.\n */\nexport interface BulkOutcome {\n id: EntityId;\n ok: boolean;\n /** Present only when `ok` is false. */\n code?: string;\n}\n\n/** The ids that failed — what a retry re-runs, and nothing else. */\nexport function failedIds(outcomes: readonly BulkOutcome[]): EntityId[] {\n return outcomes.filter(outcome => !outcome.ok).map(outcome => outcome.id);\n}\n\n/** How many rows were written. */\nexport function succeededCount(outcomes: readonly BulkOutcome[]): number {\n return outcomes.filter(outcome => outcome.ok).length;\n}\n"],"names":["failedIds","outcomes","filter","outcome","ok","map","id","succeededCount","length"],"mappings":"AAuBA,kEAAkE,GAClE,OAAO,SAASA,UAAUC,QAAgC;IACxD,OAAOA,SAASC,MAAM,CAACC,CAAAA,UAAW,CAACA,QAAQC,EAAE,EAAEC,GAAG,CAACF,CAAAA,UAAWA,QAAQG,EAAE;AAC1E;AAEA,gCAAgC,GAChC,OAAO,SAASC,eAAeN,QAAgC;IAC7D,OAAOA,SAASC,MAAM,CAACC,CAAAA,UAAWA,QAAQC,EAAE,EAAEI,MAAM;AACtD"}
@@ -7,8 +7,7 @@
7
7
  * the *permission* built from an action and re-exports these as `Actions` /
8
8
  * `Action`; `@entifix/react-controls` reads the same triple off a served
9
9
  * `EntityMetadataDocument` to decide whether to render Save and Delete, and
10
- * a T3 package may not depend on `@entifix/authz` (T4)
11
- * ([ADR 0026](../../../../../../docs/adr/0026-the-use-case-descriptor-and-served-entity-metadata.md)).
10
+ * a T3 package may not depend on `@entifix/authz` (T4).
12
11
  */
13
12
  export declare const ENTITY_ACTIONS: readonly ["read", "write", "delete"];
14
13
  export type EntityAction = (typeof ENTITY_ACTIONS)[number];
@@ -1 +1 @@
1
- {"version":3,"file":"EntityAction.d.ts","sourceRoot":"","sources":["../../src/types/EntityAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,sCAAuC,CAAC;AACnE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC"}
1
+ {"version":3,"file":"EntityAction.d.ts","sourceRoot":"","sources":["../../src/types/EntityAction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,sCAAuC,CAAC;AACnE,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC"}
@@ -7,8 +7,7 @@
7
7
  * the *permission* built from an action and re-exports these as `Actions` /
8
8
  * `Action`; `@entifix/react-controls` reads the same triple off a served
9
9
  * `EntityMetadataDocument` to decide whether to render Save and Delete, and
10
- * a T3 package may not depend on `@entifix/authz` (T4)
11
- * ([ADR 0026](../../../../../../docs/adr/0026-the-use-case-descriptor-and-served-entity-metadata.md)).
10
+ * a T3 package may not depend on `@entifix/authz` (T4).
12
11
  */ export const ENTITY_ACTIONS = [
13
12
  'read',
14
13
  'write',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/EntityAction.ts"],"sourcesContent":["/**\n * What a caller wants to do with a resource: the CRUD triple every entity has\n * by construction, as opposed to a verb a domain declares with `@useCase()`.\n *\n * It lives in `core` rather than in the authorization vocabulary because two\n * packages that may not import each other both need it. `@entifix/authz` owns\n * the *permission* built from an action and re-exports these as `Actions` /\n * `Action`; `@entifix/react-controls` reads the same triple off a served\n * `EntityMetadataDocument` to decide whether to render Save and Delete, and\n * a T3 package may not depend on `@entifix/authz` (T4)\n * ([ADR 0026](../../../../../../docs/adr/0026-the-use-case-descriptor-and-served-entity-metadata.md)).\n */\nexport const ENTITY_ACTIONS = ['read', 'write', 'delete'] as const;\nexport type EntityAction = (typeof ENTITY_ACTIONS)[number];\n"],"names":["ENTITY_ACTIONS"],"mappings":"AAAA;;;;;;;;;;;CAWC,GACD,OAAO,MAAMA,iBAAiB;IAAC;IAAQ;IAAS;CAAS,CAAU"}
1
+ {"version":3,"sources":["../../src/types/EntityAction.ts"],"sourcesContent":["/**\n * What a caller wants to do with a resource: the CRUD triple every entity has\n * by construction, as opposed to a verb a domain declares with `@useCase()`.\n *\n * It lives in `core` rather than in the authorization vocabulary because two\n * packages that may not import each other both need it. `@entifix/authz` owns\n * the *permission* built from an action and re-exports these as `Actions` /\n * `Action`; `@entifix/react-controls` reads the same triple off a served\n * `EntityMetadataDocument` to decide whether to render Save and Delete, and\n * a T3 package may not depend on `@entifix/authz` (T4).\n */\nexport const ENTITY_ACTIONS = ['read', 'write', 'delete'] as const;\nexport type EntityAction = (typeof ENTITY_ACTIONS)[number];\n"],"names":["ENTITY_ACTIONS"],"mappings":"AAAA;;;;;;;;;;CAUC,GACD,OAAO,MAAMA,iBAAiB;IAAC;IAAQ;IAAS;CAAS,CAAU"}
@@ -10,14 +10,13 @@ import type { EntityRowDraft } from './EntityRowDraft.js';
10
10
  * A **`composition`** member's value is an `EntityRowDraft[]` — rows the master
11
11
  * owns, each a string map carrying its own `ROW_KEY`. That is the whole reason
12
12
  * this is a union and not `string`: rows have no lossless string form, and the
13
- * two shapes a draft can hold are exactly the two ADR 0034 named.
13
+ * two shapes a draft can hold are exactly the two composition metadata names.
14
14
  *
15
15
  * A **`localized`** member's value is an {@link EntityLocalizedDraft}, a text per
16
16
  * locale.
17
17
  *
18
18
  * **Exactly three shapes, and the union says so rather than `JsonValue`.** Both
19
- * are JSON — a workspace autosaves a draft through `createJSONStorage`
20
- * ([ADR 0032](../../../../../../docs/adr/0032-what-may-live-in-an-autosaved-draft.md)),
19
+ * are JSON — a workspace autosaves a draft through `createJSONStorage`,
21
20
  * so a class instance, an `EntityLink` or a `Date` does not degrade, it returns
22
21
  * as something else, silently — but `JsonValue` would say more than is true. A
23
22
  * member whose value is neither a string nor a row list has no editor, no
@@ -1 +1 @@
1
- {"version":3,"file":"EntityDraft.d.ts","sourceRoot":"","sources":["../../src/types/EntityDraft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,MAAM,gBAAgB,GAC1B,MAAM,GAAG,SAAS,cAAc,EAAE,GAAG,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX,oBAAoB,CActB"}
1
+ {"version":3,"file":"EntityDraft.d.ts","sourceRoot":"","sources":["../../src/types/EntityDraft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,gBAAgB,GAC1B,MAAM,GAAG,SAAS,cAAc,EAAE,GAAG,oBAAoB,CAAC;AAE5D;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AAE3D;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,IAAI,EAAE,MAAM,GACX,oBAAoB,CActB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/EntityDraft.ts"],"sourcesContent":["import type { EntityRowDraft } from './EntityRowDraft.js';\n\n/**\n * A form's in-progress values, keyed by accessor name.\n *\n * A scalar member's value is a **string**, because that is what a native input\n * round-trips: a `scalarCollection` drafts as a comma list, a `date` as\n * `yyyy-mm-dd`, a relation as its foreign key. Reading one is therefore still\n * `values[name] ?? ''` in every place that only edits scalars.\n *\n * A **`composition`** member's value is an `EntityRowDraft[]` — rows the master\n * owns, each a string map carrying its own `ROW_KEY`. That is the whole reason\n * this is a union and not `string`: rows have no lossless string form, and the\n * two shapes a draft can hold are exactly the two ADR 0034 named.\n *\n * A **`localized`** member's value is an {@link EntityLocalizedDraft}, a text per\n * locale.\n *\n * **Exactly three shapes, and the union says so rather than `JsonValue`.** Both\n * are JSON — a workspace autosaves a draft through `createJSONStorage`\n * ([ADR 0032](../../../../../../docs/adr/0032-what-may-live-in-an-autosaved-draft.md)),\n * so a class instance, an `EntityLink` or a `Date` does not degrade, it returns\n * as something else, silently — but `JsonValue` would say more than is true. A\n * member whose value is neither a string nor a row list has no editor, no\n * coercion and no restore rule: `reconstructEntity` would carry it through\n * untouched and the serializer would write it.\n *\n * The narrower union is also what keeps the form engine compiling. `JsonValue`\n * is **recursive**, and TanStack Form derives a field-path type from the value\n * it is given, so a draft typed that way makes that derivation unbounded —\n * `Type instantiation is excessively deep and possibly infinite`. A row is a\n * flat string map, so this union has a bottom and the derivation terminates.\n *\n * The alias exists in **one** place on purpose. The same structural type was\n * declared four times — `EntityFormValues`, `EntityFormDraft`, `EntityLinkDraft`\n * and `EntityCrudDraft` — and collapsing them is what made this widening a\n * single edit rather than four found by search.\n */\nexport type EntityDraftValue =\n string | readonly EntityRowDraft[] | EntityLocalizedDraft;\n\n/**\n * A `localized` member's draft: one string per locale, the text each input\n * holds. Flat like a row, so the union still has a bottom for TanStack Form to\n * derive paths from, and JSON-safe for the same autosave a row has to survive.\n * Told apart from a row list by not being an array.\n */\nexport type EntityLocalizedDraft = Readonly<Record<string, string>>;\n\nexport type EntityDraft = Record<string, EntityDraftValue>;\n\n/**\n * The string a draft holds for a scalar member.\n *\n * A draft value is `JsonValue` now, but every scalar editor still writes and\n * reads a string, so this is the read every one of them performs — in one\n * place, so a member that came back from storage as a number or `null` renders\n * as an empty input instead of flipping React from controlled to uncontrolled\n * mid-render.\n */\nexport function readDraftString(\n values: EntityDraft | undefined,\n name: string,\n): string {\n const value = values?.[name];\n return typeof value === 'string' ? value : '';\n}\n\n/**\n * The per-locale texts a draft holds for a `localized` member, or an empty map\n * for anything else — a member never seeded, or one that came back from\n * storage in another shape.\n */\nexport function readLocalizedDraft(\n values: EntityDraft | undefined,\n name: string,\n): EntityLocalizedDraft {\n const value = values?.[name];\n if (\n value === undefined ||\n typeof value === 'string' ||\n Array.isArray(value)\n ) {\n return {};\n }\n return Object.fromEntries(\n Object.entries(value as EntityLocalizedDraft).filter(\n ([, text]) => typeof text === 'string',\n ),\n );\n}\n"],"names":["readDraftString","values","name","value","readLocalizedDraft","undefined","Array","isArray","Object","fromEntries","entries","filter","text"],"mappings":"AAmDA;;;;;;;;CAQC,GACD,OAAO,SAASA,gBACdC,MAA+B,EAC/BC,IAAY;IAEZ,MAAMC,QAAQF,0BAAAA,MAAQ,CAACC,KAAK;IAC5B,OAAO,OAAOC,UAAU,WAAWA,QAAQ;AAC7C;AAEA;;;;CAIC,GACD,OAAO,SAASC,mBACdH,MAA+B,EAC/BC,IAAY;IAEZ,MAAMC,QAAQF,0BAAAA,MAAQ,CAACC,KAAK;IAC5B,IACEC,UAAUE,aACV,OAAOF,UAAU,YACjBG,MAAMC,OAAO,CAACJ,QACd;QACA,OAAO,CAAC;IACV;IACA,OAAOK,OAAOC,WAAW,CACvBD,OAAOE,OAAO,CAACP,OAA+BQ,MAAM,CAClD,CAAC,GAAGC,KAAK,GAAK,OAAOA,SAAS;AAGpC"}
1
+ {"version":3,"sources":["../../src/types/EntityDraft.ts"],"sourcesContent":["import type { EntityRowDraft } from './EntityRowDraft.js';\n\n/**\n * A form's in-progress values, keyed by accessor name.\n *\n * A scalar member's value is a **string**, because that is what a native input\n * round-trips: a `scalarCollection` drafts as a comma list, a `date` as\n * `yyyy-mm-dd`, a relation as its foreign key. Reading one is therefore still\n * `values[name] ?? ''` in every place that only edits scalars.\n *\n * A **`composition`** member's value is an `EntityRowDraft[]` — rows the master\n * owns, each a string map carrying its own `ROW_KEY`. That is the whole reason\n * this is a union and not `string`: rows have no lossless string form, and the\n * two shapes a draft can hold are exactly the two composition metadata names.\n *\n * A **`localized`** member's value is an {@link EntityLocalizedDraft}, a text per\n * locale.\n *\n * **Exactly three shapes, and the union says so rather than `JsonValue`.** Both\n * are JSON — a workspace autosaves a draft through `createJSONStorage`,\n * so a class instance, an `EntityLink` or a `Date` does not degrade, it returns\n * as something else, silently — but `JsonValue` would say more than is true. A\n * member whose value is neither a string nor a row list has no editor, no\n * coercion and no restore rule: `reconstructEntity` would carry it through\n * untouched and the serializer would write it.\n *\n * The narrower union is also what keeps the form engine compiling. `JsonValue`\n * is **recursive**, and TanStack Form derives a field-path type from the value\n * it is given, so a draft typed that way makes that derivation unbounded —\n * `Type instantiation is excessively deep and possibly infinite`. A row is a\n * flat string map, so this union has a bottom and the derivation terminates.\n *\n * The alias exists in **one** place on purpose. The same structural type was\n * declared four times — `EntityFormValues`, `EntityFormDraft`, `EntityLinkDraft`\n * and `EntityCrudDraft` — and collapsing them is what made this widening a\n * single edit rather than four found by search.\n */\nexport type EntityDraftValue =\n string | readonly EntityRowDraft[] | EntityLocalizedDraft;\n\n/**\n * A `localized` member's draft: one string per locale, the text each input\n * holds. Flat like a row, so the union still has a bottom for TanStack Form to\n * derive paths from, and JSON-safe for the same autosave a row has to survive.\n * Told apart from a row list by not being an array.\n */\nexport type EntityLocalizedDraft = Readonly<Record<string, string>>;\n\nexport type EntityDraft = Record<string, EntityDraftValue>;\n\n/**\n * The string a draft holds for a scalar member.\n *\n * A draft value is `JsonValue` now, but every scalar editor still writes and\n * reads a string, so this is the read every one of them performs — in one\n * place, so a member that came back from storage as a number or `null` renders\n * as an empty input instead of flipping React from controlled to uncontrolled\n * mid-render.\n */\nexport function readDraftString(\n values: EntityDraft | undefined,\n name: string,\n): string {\n const value = values?.[name];\n return typeof value === 'string' ? value : '';\n}\n\n/**\n * The per-locale texts a draft holds for a `localized` member, or an empty map\n * for anything else — a member never seeded, or one that came back from\n * storage in another shape.\n */\nexport function readLocalizedDraft(\n values: EntityDraft | undefined,\n name: string,\n): EntityLocalizedDraft {\n const value = values?.[name];\n if (\n value === undefined ||\n typeof value === 'string' ||\n Array.isArray(value)\n ) {\n return {};\n }\n return Object.fromEntries(\n Object.entries(value as EntityLocalizedDraft).filter(\n ([, text]) => typeof text === 'string',\n ),\n );\n}\n"],"names":["readDraftString","values","name","value","readLocalizedDraft","undefined","Array","isArray","Object","fromEntries","entries","filter","text"],"mappings":"AAkDA;;;;;;;;CAQC,GACD,OAAO,SAASA,gBACdC,MAA+B,EAC/BC,IAAY;IAEZ,MAAMC,QAAQF,0BAAAA,MAAQ,CAACC,KAAK;IAC5B,OAAO,OAAOC,UAAU,WAAWA,QAAQ;AAC7C;AAEA;;;;CAIC,GACD,OAAO,SAASC,mBACdH,MAA+B,EAC/BC,IAAY;IAEZ,MAAMC,QAAQF,0BAAAA,MAAQ,CAACC,KAAK;IAC5B,IACEC,UAAUE,aACV,OAAOF,UAAU,YACjBG,MAAMC,OAAO,CAACJ,QACd;QACA,OAAO,CAAC;IACV;IACA,OAAOK,OAAOC,WAAW,CACvBD,OAAOE,OAAO,CAACP,OAA+BQ,MAAM,CAClD,CAAC,GAAGC,KAAK,GAAK,OAAOA,SAAS;AAGpC"}
@@ -4,7 +4,7 @@
4
4
  * A `composition`'s rows are editable before the record they belong to has ever
5
5
  * been saved, and the child is a **value** — `OrderItem` has no `id`, no domain
6
6
  * and no permission namespace
7
- * ([ADR 0034](../../../../../../docs/adr/0034-composition-metadata.md)). So the
7
+ *. So the
8
8
  * only identity a row can have is one the browser mints, and it exists for
9
9
  * exactly two jobs: a stable React key, and stable focus across a re-render.
10
10
  *
@@ -20,8 +20,7 @@ export declare const ROW_KEY = "$key";
20
20
  *
21
21
  * Members are strings for the reason every draft value is a string — that is
22
22
  * what a native input round-trips — and the whole row is JSON, because the
23
- * workspace autosaves the master's draft through `createJSONStorage`
24
- * ([ADR 0032](../../../../../../docs/adr/0032-what-may-live-in-an-autosaved-draft.md)).
23
+ * workspace autosaves the master's draft through `createJSONStorage`.
25
24
  * The key rides **inside** the row rather than in a parallel array precisely so
26
25
  * that it survives that round trip attached to the row it identifies; a parallel
27
26
  * structure re-introduces the index identity this exists to avoid.
@@ -1 +1 @@
1
- {"version":3,"file":"EntityRowDraft.d.ts","sourceRoot":"","sources":["../../src/types/EntityRowDraft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,SAAS,CAAC;AAE9B;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,EAAE,CAczE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,EAAE,CAE9D"}
1
+ {"version":3,"file":"EntityRowDraft.d.ts","sourceRoot":"","sources":["../../src/types/EntityRowDraft.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,SAAS,CAAC;AAE9B;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,EAAE,CAczE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,EAAE,CAE9D"}
@@ -4,7 +4,7 @@
4
4
  * A `composition`'s rows are editable before the record they belong to has ever
5
5
  * been saved, and the child is a **value** — `OrderItem` has no `id`, no domain
6
6
  * and no permission namespace
7
- * ([ADR 0034](../../../../../../docs/adr/0034-composition-metadata.md)). So the
7
+ *. So the
8
8
  * only identity a row can have is one the browser mints, and it exists for
9
9
  * exactly two jobs: a stable React key, and stable focus across a re-render.
10
10
  *
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/EntityRowDraft.ts"],"sourcesContent":["/**\n * The member a row draft carries its client-side identity in.\n *\n * A `composition`'s rows are editable before the record they belong to has ever\n * been saved, and the child is a **value** — `OrderItem` has no `id`, no domain\n * and no permission namespace\n * ([ADR 0034](../../../../../../docs/adr/0034-composition-metadata.md)). So the\n * only identity a row can have is one the browser mints, and it exists for\n * exactly two jobs: a stable React key, and stable focus across a re-render.\n *\n * `$` is not a legal first character for any member this repo declares, but that\n * is a convention rather than a rule, so `describeChildColumns` **throws** on a\n * child that declares this name instead of trusting it. A silent collision would\n * be a row whose key is overwritten by its own data — it would re-key on every\n * keystroke, lose focus mid-word, and address the wrong row's errors.\n */\nexport const ROW_KEY = '$key';\n\n/**\n * One row of an owned collection, as a form holds it.\n *\n * Members are strings for the reason every draft value is a string — that is\n * what a native input round-trips — and the whole row is JSON, because the\n * workspace autosaves the master's draft through `createJSONStorage`\n * ([ADR 0032](../../../../../../docs/adr/0032-what-may-live-in-an-autosaved-draft.md)).\n * The key rides **inside** the row rather than in a parallel array precisely so\n * that it survives that round trip attached to the row it identifies; a parallel\n * structure re-introduces the index identity this exists to avoid.\n *\n * {@link ROW_KEY} never reaches the wire: `reconstructChild` drops it when it\n * rebuilds the child, so the serializer never sees a member the entity does not\n * declare.\n */\nexport type EntityRowDraft = { [member: string]: string };\n\n/**\n * Mints a key for a row the user has just added.\n *\n * `crypto.randomUUID` rather than a counter, because two drafts restored into\n * one session must not collide, and a counter that resets on reload would\n * re-issue keys a persisted draft is still using.\n */\nexport function newRowKey(): string {\n return crypto.randomUUID();\n}\n\n/**\n * The key of the *n*th row as the record supplied it.\n *\n * Deterministic, and that is load-bearing rather than tidy. Seeding is not a\n * one-time event: `useEntityForm` re-seeds whenever its `entity` changes\n * identity, and a caller that builds the record inline re-seeds on **every\n * render**. Minting a fresh random key there produces a draft that differs from\n * the last one on every pass, so the form engine sees its defaults change\n * forever and React stops with `Maximum update depth exceeded` — a live hang,\n * not a test artifact.\n *\n * Positional is safe *here* precisely because it describes the record rather\n * than the live list: re-seeding the same record yields the same keys, and once\n * the user adds or removes anything the keys already in the draft are carried\n * through untouched. A row added at runtime takes a {@link newRowKey} instead,\n * which can never collide with this shape.\n */\nexport function seededRowKey(index: number): string {\n return `row-${index}`;\n}\n\n/**\n * Whether a draft value is a usable list of rows.\n *\n * A restored draft is `JsonValue`, so a composition member can come back as\n * anything — a string written by a build before this existed, `null`, or an\n * array of the wrong shape. Every caller must ask before reading, and the answer\n * for a value that fails is an empty grid rather than a crash: an unreadable\n * draft is an unfinished edit, and `restoreEntityDraft` already discards those\n * per entry rather than losing the whole form.\n *\n * A row missing its {@link ROW_KEY} fails, deliberately. Minting one here would\n * hide the case where a row was written by something that does not know the\n * contract, and the missing key would then be minted afresh on every render.\n */\nexport function isRowDraftArray(value: unknown): value is EntityRowDraft[] {\n return (\n Array.isArray(value) &&\n value.every(\n row =>\n typeof row === 'object' &&\n row !== null &&\n !Array.isArray(row) &&\n typeof (row as Record<string, unknown>)[ROW_KEY] === 'string' &&\n Object.values(row as Record<string, unknown>).every(\n member => typeof member === 'string',\n ),\n )\n );\n}\n\n/** The rows a draft value holds, or an empty list when it holds none usable. */\nexport function readRowDrafts(value: unknown): EntityRowDraft[] {\n return isRowDraftArray(value) ? value : [];\n}\n"],"names":["ROW_KEY","newRowKey","crypto","randomUUID","seededRowKey","index","isRowDraftArray","value","Array","isArray","every","row","Object","values","member","readRowDrafts"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMA,UAAU,OAAO;AAmB9B;;;;;;CAMC,GACD,OAAO,SAASC;IACd,OAAOC,OAAOC,UAAU;AAC1B;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASC,aAAaC,KAAa;IACxC,OAAO,CAAC,IAAI,EAAEA,OAAO;AACvB;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,gBAAgBC,KAAc;IAC5C,OACEC,MAAMC,OAAO,CAACF,UACdA,MAAMG,KAAK,CACTC,CAAAA,MACE,OAAOA,QAAQ,YACfA,QAAQ,QACR,CAACH,MAAMC,OAAO,CAACE,QACf,OAAO,AAACA,GAA+B,CAACX,QAAQ,KAAK,YACrDY,OAAOC,MAAM,CAACF,KAAgCD,KAAK,CACjDI,CAAAA,SAAU,OAAOA,WAAW;AAItC;AAEA,8EAA8E,GAC9E,OAAO,SAASC,cAAcR,KAAc;IAC1C,OAAOD,gBAAgBC,SAASA,QAAQ,EAAE;AAC5C"}
1
+ {"version":3,"sources":["../../src/types/EntityRowDraft.ts"],"sourcesContent":["/**\n * The member a row draft carries its client-side identity in.\n *\n * A `composition`'s rows are editable before the record they belong to has ever\n * been saved, and the child is a **value** — `OrderItem` has no `id`, no domain\n * and no permission namespace\n *. So the\n * only identity a row can have is one the browser mints, and it exists for\n * exactly two jobs: a stable React key, and stable focus across a re-render.\n *\n * `$` is not a legal first character for any member this repo declares, but that\n * is a convention rather than a rule, so `describeChildColumns` **throws** on a\n * child that declares this name instead of trusting it. A silent collision would\n * be a row whose key is overwritten by its own data — it would re-key on every\n * keystroke, lose focus mid-word, and address the wrong row's errors.\n */\nexport const ROW_KEY = '$key';\n\n/**\n * One row of an owned collection, as a form holds it.\n *\n * Members are strings for the reason every draft value is a string — that is\n * what a native input round-trips — and the whole row is JSON, because the\n * workspace autosaves the master's draft through `createJSONStorage`.\n * The key rides **inside** the row rather than in a parallel array precisely so\n * that it survives that round trip attached to the row it identifies; a parallel\n * structure re-introduces the index identity this exists to avoid.\n *\n * {@link ROW_KEY} never reaches the wire: `reconstructChild` drops it when it\n * rebuilds the child, so the serializer never sees a member the entity does not\n * declare.\n */\nexport type EntityRowDraft = { [member: string]: string };\n\n/**\n * Mints a key for a row the user has just added.\n *\n * `crypto.randomUUID` rather than a counter, because two drafts restored into\n * one session must not collide, and a counter that resets on reload would\n * re-issue keys a persisted draft is still using.\n */\nexport function newRowKey(): string {\n return crypto.randomUUID();\n}\n\n/**\n * The key of the *n*th row as the record supplied it.\n *\n * Deterministic, and that is load-bearing rather than tidy. Seeding is not a\n * one-time event: `useEntityForm` re-seeds whenever its `entity` changes\n * identity, and a caller that builds the record inline re-seeds on **every\n * render**. Minting a fresh random key there produces a draft that differs from\n * the last one on every pass, so the form engine sees its defaults change\n * forever and React stops with `Maximum update depth exceeded` — a live hang,\n * not a test artifact.\n *\n * Positional is safe *here* precisely because it describes the record rather\n * than the live list: re-seeding the same record yields the same keys, and once\n * the user adds or removes anything the keys already in the draft are carried\n * through untouched. A row added at runtime takes a {@link newRowKey} instead,\n * which can never collide with this shape.\n */\nexport function seededRowKey(index: number): string {\n return `row-${index}`;\n}\n\n/**\n * Whether a draft value is a usable list of rows.\n *\n * A restored draft is `JsonValue`, so a composition member can come back as\n * anything — a string written by a build before this existed, `null`, or an\n * array of the wrong shape. Every caller must ask before reading, and the answer\n * for a value that fails is an empty grid rather than a crash: an unreadable\n * draft is an unfinished edit, and `restoreEntityDraft` already discards those\n * per entry rather than losing the whole form.\n *\n * A row missing its {@link ROW_KEY} fails, deliberately. Minting one here would\n * hide the case where a row was written by something that does not know the\n * contract, and the missing key would then be minted afresh on every render.\n */\nexport function isRowDraftArray(value: unknown): value is EntityRowDraft[] {\n return (\n Array.isArray(value) &&\n value.every(\n row =>\n typeof row === 'object' &&\n row !== null &&\n !Array.isArray(row) &&\n typeof (row as Record<string, unknown>)[ROW_KEY] === 'string' &&\n Object.values(row as Record<string, unknown>).every(\n member => typeof member === 'string',\n ),\n )\n );\n}\n\n/** The rows a draft value holds, or an empty list when it holds none usable. */\nexport function readRowDrafts(value: unknown): EntityRowDraft[] {\n return isRowDraftArray(value) ? value : [];\n}\n"],"names":["ROW_KEY","newRowKey","crypto","randomUUID","seededRowKey","index","isRowDraftArray","value","Array","isArray","every","row","Object","values","member","readRowDrafts"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMA,UAAU,OAAO;AAkB9B;;;;;;CAMC,GACD,OAAO,SAASC;IACd,OAAOC,OAAOC,UAAU;AAC1B;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,SAASC,aAAaC,KAAa;IACxC,OAAO,CAAC,IAAI,EAAEA,OAAO;AACvB;AAEA;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,gBAAgBC,KAAc;IAC5C,OACEC,MAAMC,OAAO,CAACF,UACdA,MAAMG,KAAK,CACTC,CAAAA,MACE,OAAOA,QAAQ,YACfA,QAAQ,QACR,CAACH,MAAMC,OAAO,CAACE,QACf,OAAO,AAACA,GAA+B,CAACX,QAAQ,KAAK,YACrDY,OAAOC,MAAM,CAACF,KAAgCD,KAAK,CACjDI,CAAAA,SAAU,OAAOA,WAAW;AAItC;AAEA,8EAA8E,GAC9E,OAAO,SAASC,cAAcR,KAAc;IAC1C,OAAOD,gBAAgBC,SAASA,QAAQ,EAAE;AAC5C"}
@@ -20,7 +20,7 @@ import type { FilterGroup } from './EntityFiltering.js';
20
20
  * `total` is carried rather than derived because only the server knows it, and
21
21
  * the count is the whole affordance — "Retirar 3.200 marcas" is a different
22
22
  * sentence from "Retirar", and the user has to read the number *before* they
23
- * confirm ([ADR 0035](../../../../../../docs/adr/0035-entity-actions-selection-and-bulk.md)).
23
+ * confirm.
24
24
  */
25
25
  export type EntitySelection<TEntity extends Entity> = {
26
26
  mode: 'ids';
@@ -73,8 +73,7 @@ export declare function selectionIds<TEntity extends Entity>(selection: EntitySe
73
73
  *
74
74
  * `Set` does not survive `JSON.stringify` — it serializes to `{}`, silently,
75
75
  * and arrives as an empty object that reads as "no rows excluded" rather than
76
- * as an error. That is the same class of fault ADR 0032 recorded for autosaved
77
- * drafts, and it is worse here: a `matching` selection whose exclusions
76
+ * as an error. That is the same class of fault an autosaved draft meets, and it is worse here: a `matching` selection whose exclusions
78
77
  * evaporate acts on rows the operator deliberately took out.
79
78
  *
80
79
  * So the wire form is arrays and the in-memory form is sets, named separately
@@ -1 +1 @@
1
- {"version":3,"file":"EntitySelection.d.ts","sourceRoot":"","sources":["../../src/types/EntitySelection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,MAAM,IAC9C;IACE,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;CACjC,CAAC;AAEN,yEAAyE;AACzE,eAAO,MAAM,cAAc,GACzB,OAAO,SAAS,MAAM,OACnB,eAAe,CAAC,OAAO,CAAgD,CAAC;AAE7E;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,EAClD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,MAAM,CAIR;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAC/C,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,EACnC,EAAE,EAAE,QAAQ,GACX,OAAO,CAIT;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,EACrD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,SAAS,MAAM,EACnD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,EACnC,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,OAAO,GAChB,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EACjD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,QAAQ,EAAE,GAAG,SAAS,CAExB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mBAAmB,CAAC,OAAO,SAAS,MAAM,IAClD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,QAAQ,EAAE,CAAA;CAAE,GAChC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACtB,CAAC;AAEN;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,EACpD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,mBAAmB,CAAC,OAAO,CAAC,CAI9B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,EACtD,KAAK,EAAE,OAAO,GACb,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,CA0BtC"}
1
+ {"version":3,"file":"EntitySelection.d.ts","sourceRoot":"","sources":["../../src/types/EntitySelection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,eAAe,CAAC,OAAO,SAAS,MAAM,IAC9C;IACE,IAAI,EAAE,KAAK,CAAC;IACZ,GAAG,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,gFAAgF;IAChF,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,QAAQ,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;CACjC,CAAC;AAEN,yEAAyE;AACzE,eAAO,MAAM,cAAc,GACzB,OAAO,SAAS,MAAM,OACnB,eAAe,CAAC,OAAO,CAAgD,CAAC;AAE7E;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,EAClD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,MAAM,CAIR;AAED,gDAAgD;AAChD,wBAAgB,UAAU,CAAC,OAAO,SAAS,MAAM,EAC/C,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,EACnC,EAAE,EAAE,QAAQ,GACX,OAAO,CAIT;AAED,8CAA8C;AAC9C,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,EACrD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,OAAO,CAET;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,SAAS,MAAM,EACnD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,EACnC,EAAE,EAAE,QAAQ,EACZ,QAAQ,EAAE,OAAO,GAChB,eAAe,CAAC,OAAO,CAAC,CAY1B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EACjD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,QAAQ,EAAE,GAAG,SAAS,CAExB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,CAAC,OAAO,SAAS,MAAM,IAClD;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,GAAG,EAAE,QAAQ,EAAE,CAAA;CAAE,GAChC;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,EAAE,CAAC;CACtB,CAAC;AAEN;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,SAAS,MAAM,EACpD,SAAS,EAAE,eAAe,CAAC,OAAO,CAAC,GAClC,mBAAmB,CAAC,OAAO,CAAC,CAI9B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,SAAS,MAAM,EACtD,KAAK,EAAE,OAAO,GACb,eAAe,CAAC,OAAO,CAAC,GAAG,SAAS,CA0BtC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types/EntitySelection.ts"],"sourcesContent":["import type { Entity, EntityId } from './Entity.js';\nimport type { FilterGroup } from './EntityFiltering.js';\n\n/**\n * What a collection-bound action acts on.\n *\n * The two members are **not** two shapes of one idea, and collapsing them is\n * the defect this type exists to prevent. \"Select all on this page\" is a list\n * of ids the browser already holds; \"select all 3.200 matching the filter\" is a\n * *filter expression the server evaluates* — the set is by definition larger\n * than the page, so there is nothing to enumerate. Written as one optional\n * field (`ids?: …` beside `all?: boolean`) the second quietly becomes the\n * first, and a bulk action that reads as \"the 25 rows I can see\" runs over\n * every row in the store.\n *\n * `excluded` is what makes the second member usable rather than all-or-nothing:\n * an operator takes everything matching and then drops the two rows they know\n * about. It is a subtraction from a server-side set, never an addition to a\n * client-side one.\n *\n * `total` is carried rather than derived because only the server knows it, and\n * the count is the whole affordance — \"Retirar 3.200 marcas\" is a different\n * sentence from \"Retirar\", and the user has to read the number *before* they\n * confirm ([ADR 0035](../../../../../../docs/adr/0035-entity-actions-selection-and-bulk.md)).\n */\nexport type EntitySelection<TEntity extends Entity> =\n | {\n mode: 'ids';\n ids: ReadonlySet<EntityId>;\n }\n | {\n mode: 'matching';\n /** The filtering in effect when the set was taken. Absent means \"every row\". */\n filtering?: FilterGroup<TEntity>;\n /** How many rows the server said match. */\n total: number;\n /** Rows the user removed from the matching set. */\n excluded: ReadonlySet<EntityId>;\n };\n\n/** An empty `ids` selection — the state a table starts and clears to. */\nexport const emptySelection = <\n TEntity extends Entity,\n>(): EntitySelection<TEntity> => ({ mode: 'ids', ids: new Set<EntityId>() });\n\n/**\n * How many rows the action will touch.\n *\n * For `matching` this is the server's count minus what the user removed, which\n * is the number the confirmation must show. It can only ever be an estimate —\n * rows may have been written since the count was taken — and that is inherent\n * to the mode rather than a defect in it.\n */\nexport function selectionSize<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): number {\n return selection.mode === 'ids'\n ? selection.ids.size\n : Math.max(0, selection.total - selection.excluded.size);\n}\n\n/** Whether one row is part of the selection. */\nexport function isSelected<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n id: EntityId,\n): boolean {\n return selection.mode === 'ids'\n ? selection.ids.has(id)\n : !selection.excluded.has(id);\n}\n\n/** Whether the action would touch nothing. */\nexport function isSelectionEmpty<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): boolean {\n return selectionSize(selection) === 0;\n}\n\n/**\n * Add or remove one row, in whichever direction the current mode means.\n *\n * In `ids` mode a toggle writes the id set; in `matching` mode it writes the\n * *exclusion* set, because the selection is the server's answer and the only\n * thing the browser owns is what was taken back out of it. Both return a new\n * value — the selection is controlled state held above the table.\n */\nexport function toggleSelected<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n id: EntityId,\n selected: boolean,\n): EntitySelection<TEntity> {\n if (selection.mode === 'ids') {\n const ids = new Set(selection.ids);\n if (selected) ids.add(id);\n else ids.delete(id);\n return { mode: 'ids', ids };\n }\n\n const excluded = new Set(selection.excluded);\n if (selected) excluded.delete(id);\n else excluded.add(id);\n return { ...selection, excluded };\n}\n\n/**\n * The ids a bulk request should carry, or `undefined` when it must carry the\n * filter instead.\n *\n * A caller that treats `undefined` as \"no rows\" has made exactly the mistake\n * the two modes exist to prevent, so this returns `undefined` rather than an\n * empty array: an empty array is a plausible payload and would silently do\n * nothing, where `undefined` cannot be sent at all.\n */\nexport function selectionIds<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): EntityId[] | undefined {\n return selection.mode === 'ids' ? Array.from(selection.ids) : undefined;\n}\n\n/**\n * The selection as it crosses the wire.\n *\n * `Set` does not survive `JSON.stringify` — it serializes to `{}`, silently,\n * and arrives as an empty object that reads as \"no rows excluded\" rather than\n * as an error. That is the same class of fault ADR 0032 recorded for autosaved\n * drafts, and it is worse here: a `matching` selection whose exclusions\n * evaporate acts on rows the operator deliberately took out.\n *\n * So the wire form is arrays and the in-memory form is sets, named separately\n * rather than one type used loosely. The set is the right in-memory shape\n * because it dedupes by construction and answers `isSelected` in constant time;\n * the array is the only JSON shape there is.\n */\nexport type EntitySelectionWire<TEntity extends Entity> =\n | { mode: 'ids'; ids: EntityId[] }\n | {\n mode: 'matching';\n filtering?: FilterGroup<TEntity>;\n total: number;\n excluded: EntityId[];\n };\n\n/**\n * Convert a selection to its wire form.\n *\n * ⚠️ **`Array.from(set)`, never `[...set]`.** Every package here compiles with\n * `\"loose\": true` in its `.swcrc`, and SWC's loose spread helper skips the\n * iterable protocol: it treats the operand as array-like, finds no `length` on\n * a `Set`, and **wraps it as a single element** — `[...someSet]` becomes\n * `[Set]`, silently, with the right `.size` still on the inner value. Measured\n * against the running service, where it turned a two-row selection into one\n * outcome whose `id` serialized as `{}`. Vitest and Turbopack transpile\n * differently, so it passes every unit test and only fails in the built\n * bundle.\n */\nexport function toWireSelection<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): EntitySelectionWire<TEntity> {\n return selection.mode === 'ids'\n ? { mode: 'ids', ids: Array.from(selection.ids) }\n : { ...selection, excluded: Array.from(selection.excluded) };\n}\n\n/**\n * Read a wire selection back, rejecting anything that is not one.\n *\n * Untrusted input: this arrives in a request body and decides which rows a\n * bulk write touches. A missing `mode` must not fall through to a default —\n * defaulting to `ids` with no ids would act on nothing (merely confusing),\n * while defaulting to `matching` would act on **everything**.\n */\nexport function readWireSelection<TEntity extends Entity>(\n value: unknown,\n): EntitySelection<TEntity> | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const candidate = value as Partial<EntitySelectionWire<TEntity>>;\n\n if (candidate.mode === 'ids') {\n return Array.isArray(candidate.ids)\n ? { mode: 'ids', ids: new Set(candidate.ids) }\n : undefined;\n }\n\n if (candidate.mode === 'matching') {\n const matching = candidate as Extract<\n EntitySelectionWire<TEntity>,\n { mode: 'matching' }\n >;\n if (typeof matching.total !== 'number') return undefined;\n if (!Array.isArray(matching.excluded)) return undefined;\n return {\n mode: 'matching',\n filtering: matching.filtering,\n total: matching.total,\n excluded: new Set(matching.excluded),\n };\n }\n\n return undefined;\n}\n"],"names":["emptySelection","mode","ids","Set","selectionSize","selection","size","Math","max","total","excluded","isSelected","id","has","isSelectionEmpty","toggleSelected","selected","add","delete","selectionIds","Array","from","undefined","toWireSelection","readWireSelection","value","candidate","isArray","matching","filtering"],"mappings":";AAwCA,uEAAuE,GACvE,OAAO,MAAMA,iBAAiB,IAEI,CAAA;QAAEC,MAAM;QAAOC,KAAK,IAAIC;IAAgB,CAAA,EAAG;AAE7E;;;;;;;CAOC,GACD,OAAO,SAASC,cACdC,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QACtBI,UAAUH,GAAG,CAACI,IAAI,GAClBC,KAAKC,GAAG,CAAC,GAAGH,UAAUI,KAAK,GAAGJ,UAAUK,QAAQ,CAACJ,IAAI;AAC3D;AAEA,8CAA8C,GAC9C,OAAO,SAASK,WACdN,SAAmC,EACnCO,EAAY;IAEZ,OAAOP,UAAUJ,IAAI,KAAK,QACtBI,UAAUH,GAAG,CAACW,GAAG,CAACD,MAClB,CAACP,UAAUK,QAAQ,CAACG,GAAG,CAACD;AAC9B;AAEA,4CAA4C,GAC5C,OAAO,SAASE,iBACdT,SAAmC;IAEnC,OAAOD,cAAcC,eAAe;AACtC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASU,eACdV,SAAmC,EACnCO,EAAY,EACZI,QAAiB;IAEjB,IAAIX,UAAUJ,IAAI,KAAK,OAAO;QAC5B,MAAMC,MAAM,IAAIC,IAAIE,UAAUH,GAAG;QACjC,IAAIc,UAAUd,IAAIe,GAAG,CAACL;aACjBV,IAAIgB,MAAM,CAACN;QAChB,OAAO;YAAEX,MAAM;YAAOC;QAAI;IAC5B;IAEA,MAAMQ,WAAW,IAAIP,IAAIE,UAAUK,QAAQ;IAC3C,IAAIM,UAAUN,SAASQ,MAAM,CAACN;SACzBF,SAASO,GAAG,CAACL;IAClB,OAAO,aAAKP;QAAWK;;AACzB;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASS,aACdd,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QAAQmB,MAAMC,IAAI,CAAChB,UAAUH,GAAG,IAAIoB;AAChE;AAyBA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,gBACdlB,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QACtB;QAAEA,MAAM;QAAOC,KAAKkB,MAAMC,IAAI,CAAChB,UAAUH,GAAG;IAAE,IAC9C,aAAKG;QAAWK,UAAUU,MAAMC,IAAI,CAAChB,UAAUK,QAAQ;;AAC7D;AAEA;;;;;;;CAOC,GACD,OAAO,SAASc,kBACdC,KAAc;IAEd,IAAI,OAAOA,UAAU,YAAYA,UAAU,MAAM,OAAOH;IACxD,MAAMI,YAAYD;IAElB,IAAIC,UAAUzB,IAAI,KAAK,OAAO;QAC5B,OAAOmB,MAAMO,OAAO,CAACD,UAAUxB,GAAG,IAC9B;YAAED,MAAM;YAAOC,KAAK,IAAIC,IAAIuB,UAAUxB,GAAG;QAAE,IAC3CoB;IACN;IAEA,IAAII,UAAUzB,IAAI,KAAK,YAAY;QACjC,MAAM2B,WAAWF;QAIjB,IAAI,OAAOE,SAASnB,KAAK,KAAK,UAAU,OAAOa;QAC/C,IAAI,CAACF,MAAMO,OAAO,CAACC,SAASlB,QAAQ,GAAG,OAAOY;QAC9C,OAAO;YACLrB,MAAM;YACN4B,WAAWD,SAASC,SAAS;YAC7BpB,OAAOmB,SAASnB,KAAK;YACrBC,UAAU,IAAIP,IAAIyB,SAASlB,QAAQ;QACrC;IACF;IAEA,OAAOY;AACT"}
1
+ {"version":3,"sources":["../../src/types/EntitySelection.ts"],"sourcesContent":["import type { Entity, EntityId } from './Entity.js';\nimport type { FilterGroup } from './EntityFiltering.js';\n\n/**\n * What a collection-bound action acts on.\n *\n * The two members are **not** two shapes of one idea, and collapsing them is\n * the defect this type exists to prevent. \"Select all on this page\" is a list\n * of ids the browser already holds; \"select all 3.200 matching the filter\" is a\n * *filter expression the server evaluates* — the set is by definition larger\n * than the page, so there is nothing to enumerate. Written as one optional\n * field (`ids?: …` beside `all?: boolean`) the second quietly becomes the\n * first, and a bulk action that reads as \"the 25 rows I can see\" runs over\n * every row in the store.\n *\n * `excluded` is what makes the second member usable rather than all-or-nothing:\n * an operator takes everything matching and then drops the two rows they know\n * about. It is a subtraction from a server-side set, never an addition to a\n * client-side one.\n *\n * `total` is carried rather than derived because only the server knows it, and\n * the count is the whole affordance — \"Retirar 3.200 marcas\" is a different\n * sentence from \"Retirar\", and the user has to read the number *before* they\n * confirm.\n */\nexport type EntitySelection<TEntity extends Entity> =\n | {\n mode: 'ids';\n ids: ReadonlySet<EntityId>;\n }\n | {\n mode: 'matching';\n /** The filtering in effect when the set was taken. Absent means \"every row\". */\n filtering?: FilterGroup<TEntity>;\n /** How many rows the server said match. */\n total: number;\n /** Rows the user removed from the matching set. */\n excluded: ReadonlySet<EntityId>;\n };\n\n/** An empty `ids` selection — the state a table starts and clears to. */\nexport const emptySelection = <\n TEntity extends Entity,\n>(): EntitySelection<TEntity> => ({ mode: 'ids', ids: new Set<EntityId>() });\n\n/**\n * How many rows the action will touch.\n *\n * For `matching` this is the server's count minus what the user removed, which\n * is the number the confirmation must show. It can only ever be an estimate —\n * rows may have been written since the count was taken — and that is inherent\n * to the mode rather than a defect in it.\n */\nexport function selectionSize<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): number {\n return selection.mode === 'ids'\n ? selection.ids.size\n : Math.max(0, selection.total - selection.excluded.size);\n}\n\n/** Whether one row is part of the selection. */\nexport function isSelected<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n id: EntityId,\n): boolean {\n return selection.mode === 'ids'\n ? selection.ids.has(id)\n : !selection.excluded.has(id);\n}\n\n/** Whether the action would touch nothing. */\nexport function isSelectionEmpty<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): boolean {\n return selectionSize(selection) === 0;\n}\n\n/**\n * Add or remove one row, in whichever direction the current mode means.\n *\n * In `ids` mode a toggle writes the id set; in `matching` mode it writes the\n * *exclusion* set, because the selection is the server's answer and the only\n * thing the browser owns is what was taken back out of it. Both return a new\n * value — the selection is controlled state held above the table.\n */\nexport function toggleSelected<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n id: EntityId,\n selected: boolean,\n): EntitySelection<TEntity> {\n if (selection.mode === 'ids') {\n const ids = new Set(selection.ids);\n if (selected) ids.add(id);\n else ids.delete(id);\n return { mode: 'ids', ids };\n }\n\n const excluded = new Set(selection.excluded);\n if (selected) excluded.delete(id);\n else excluded.add(id);\n return { ...selection, excluded };\n}\n\n/**\n * The ids a bulk request should carry, or `undefined` when it must carry the\n * filter instead.\n *\n * A caller that treats `undefined` as \"no rows\" has made exactly the mistake\n * the two modes exist to prevent, so this returns `undefined` rather than an\n * empty array: an empty array is a plausible payload and would silently do\n * nothing, where `undefined` cannot be sent at all.\n */\nexport function selectionIds<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): EntityId[] | undefined {\n return selection.mode === 'ids' ? Array.from(selection.ids) : undefined;\n}\n\n/**\n * The selection as it crosses the wire.\n *\n * `Set` does not survive `JSON.stringify` — it serializes to `{}`, silently,\n * and arrives as an empty object that reads as \"no rows excluded\" rather than\n * as an error. That is the same class of fault an autosaved draft meets, and it is worse here: a `matching` selection whose exclusions\n * evaporate acts on rows the operator deliberately took out.\n *\n * So the wire form is arrays and the in-memory form is sets, named separately\n * rather than one type used loosely. The set is the right in-memory shape\n * because it dedupes by construction and answers `isSelected` in constant time;\n * the array is the only JSON shape there is.\n */\nexport type EntitySelectionWire<TEntity extends Entity> =\n | { mode: 'ids'; ids: EntityId[] }\n | {\n mode: 'matching';\n filtering?: FilterGroup<TEntity>;\n total: number;\n excluded: EntityId[];\n };\n\n/**\n * Convert a selection to its wire form.\n *\n * ⚠️ **`Array.from(set)`, never `[...set]`.** Every package here compiles with\n * `\"loose\": true` in its `.swcrc`, and SWC's loose spread helper skips the\n * iterable protocol: it treats the operand as array-like, finds no `length` on\n * a `Set`, and **wraps it as a single element** — `[...someSet]` becomes\n * `[Set]`, silently, with the right `.size` still on the inner value. Measured\n * against the running service, where it turned a two-row selection into one\n * outcome whose `id` serialized as `{}`. Vitest and Turbopack transpile\n * differently, so it passes every unit test and only fails in the built\n * bundle.\n */\nexport function toWireSelection<TEntity extends Entity>(\n selection: EntitySelection<TEntity>,\n): EntitySelectionWire<TEntity> {\n return selection.mode === 'ids'\n ? { mode: 'ids', ids: Array.from(selection.ids) }\n : { ...selection, excluded: Array.from(selection.excluded) };\n}\n\n/**\n * Read a wire selection back, rejecting anything that is not one.\n *\n * Untrusted input: this arrives in a request body and decides which rows a\n * bulk write touches. A missing `mode` must not fall through to a default —\n * defaulting to `ids` with no ids would act on nothing (merely confusing),\n * while defaulting to `matching` would act on **everything**.\n */\nexport function readWireSelection<TEntity extends Entity>(\n value: unknown,\n): EntitySelection<TEntity> | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const candidate = value as Partial<EntitySelectionWire<TEntity>>;\n\n if (candidate.mode === 'ids') {\n return Array.isArray(candidate.ids)\n ? { mode: 'ids', ids: new Set(candidate.ids) }\n : undefined;\n }\n\n if (candidate.mode === 'matching') {\n const matching = candidate as Extract<\n EntitySelectionWire<TEntity>,\n { mode: 'matching' }\n >;\n if (typeof matching.total !== 'number') return undefined;\n if (!Array.isArray(matching.excluded)) return undefined;\n return {\n mode: 'matching',\n filtering: matching.filtering,\n total: matching.total,\n excluded: new Set(matching.excluded),\n };\n }\n\n return undefined;\n}\n"],"names":["emptySelection","mode","ids","Set","selectionSize","selection","size","Math","max","total","excluded","isSelected","id","has","isSelectionEmpty","toggleSelected","selected","add","delete","selectionIds","Array","from","undefined","toWireSelection","readWireSelection","value","candidate","isArray","matching","filtering"],"mappings":";AAwCA,uEAAuE,GACvE,OAAO,MAAMA,iBAAiB,IAEI,CAAA;QAAEC,MAAM;QAAOC,KAAK,IAAIC;IAAgB,CAAA,EAAG;AAE7E;;;;;;;CAOC,GACD,OAAO,SAASC,cACdC,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QACtBI,UAAUH,GAAG,CAACI,IAAI,GAClBC,KAAKC,GAAG,CAAC,GAAGH,UAAUI,KAAK,GAAGJ,UAAUK,QAAQ,CAACJ,IAAI;AAC3D;AAEA,8CAA8C,GAC9C,OAAO,SAASK,WACdN,SAAmC,EACnCO,EAAY;IAEZ,OAAOP,UAAUJ,IAAI,KAAK,QACtBI,UAAUH,GAAG,CAACW,GAAG,CAACD,MAClB,CAACP,UAAUK,QAAQ,CAACG,GAAG,CAACD;AAC9B;AAEA,4CAA4C,GAC5C,OAAO,SAASE,iBACdT,SAAmC;IAEnC,OAAOD,cAAcC,eAAe;AACtC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASU,eACdV,SAAmC,EACnCO,EAAY,EACZI,QAAiB;IAEjB,IAAIX,UAAUJ,IAAI,KAAK,OAAO;QAC5B,MAAMC,MAAM,IAAIC,IAAIE,UAAUH,GAAG;QACjC,IAAIc,UAAUd,IAAIe,GAAG,CAACL;aACjBV,IAAIgB,MAAM,CAACN;QAChB,OAAO;YAAEX,MAAM;YAAOC;QAAI;IAC5B;IAEA,MAAMQ,WAAW,IAAIP,IAAIE,UAAUK,QAAQ;IAC3C,IAAIM,UAAUN,SAASQ,MAAM,CAACN;SACzBF,SAASO,GAAG,CAACL;IAClB,OAAO,aAAKP;QAAWK;;AACzB;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASS,aACdd,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QAAQmB,MAAMC,IAAI,CAAChB,UAAUH,GAAG,IAAIoB;AAChE;AAwBA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC,gBACdlB,SAAmC;IAEnC,OAAOA,UAAUJ,IAAI,KAAK,QACtB;QAAEA,MAAM;QAAOC,KAAKkB,MAAMC,IAAI,CAAChB,UAAUH,GAAG;IAAE,IAC9C,aAAKG;QAAWK,UAAUU,MAAMC,IAAI,CAAChB,UAAUK,QAAQ;;AAC7D;AAEA;;;;;;;CAOC,GACD,OAAO,SAASc,kBACdC,KAAc;IAEd,IAAI,OAAOA,UAAU,YAAYA,UAAU,MAAM,OAAOH;IACxD,MAAMI,YAAYD;IAElB,IAAIC,UAAUzB,IAAI,KAAK,OAAO;QAC5B,OAAOmB,MAAMO,OAAO,CAACD,UAAUxB,GAAG,IAC9B;YAAED,MAAM;YAAOC,KAAK,IAAIC,IAAIuB,UAAUxB,GAAG;QAAE,IAC3CoB;IACN;IAEA,IAAII,UAAUzB,IAAI,KAAK,YAAY;QACjC,MAAM2B,WAAWF;QAIjB,IAAI,OAAOE,SAASnB,KAAK,KAAK,UAAU,OAAOa;QAC/C,IAAI,CAACF,MAAMO,OAAO,CAACC,SAASlB,QAAQ,GAAG,OAAOY;QAC9C,OAAO;YACLrB,MAAM;YACN4B,WAAWD,SAASC,SAAS;YAC7BpB,OAAOmB,SAASnB,KAAK;YACrBC,UAAU,IAAIP,IAAIyB,SAASlB,QAAQ;QACrC;IACF;IAEA,OAAOY;AACT"}
@@ -15,9 +15,8 @@ export type WizardStepKind = (typeof WizardStepKinds)[number];
15
15
  * one may lead to, so reachability, unknown targets, cycles and terminality are
16
16
  * decidable by reading the definition — no probing, no enumeration of the state
17
17
  * space, and no rule that "only appears to hold" because a `next` reading a form
18
- * value could never be enumerated. It is the move
19
- * [ADR 0039](../../../../../../docs/adr/0039-multi-step-sagas-are-orchestrated.md)
20
- * made for saga steps, for the same reason: a walkable definition is checkable
18
+ * value could never be enumerated. It is the move a saga definition makes for
19
+ * its steps, for the same reason: a walkable definition is checkable
21
20
  * at load, and a function is not.
22
21
  *
23
22
  * A step with an empty `to` is **terminal**, and
@@ -84,8 +83,7 @@ export declare const findStep: (definition: WizardDefinition, stepId: string) =>
84
83
  * branch names a step that does not exist, must fail on the first render of any
85
84
  * surface rather than on the render of the step nobody reached — otherwise the
86
85
  * failure of the longest flow in the product arrives at the end of it. That is
87
- * the posture `assertLinkSourcesAreEditable` and `surfaceFor` already take
88
- * ([ADR 0035](../../../../../../docs/adr/0035-entity-actions-selection-and-bulk.md)).
86
+ * the posture `assertLinkSourcesAreEditable` and `surfaceFor` already take.
89
87
  *
90
88
  * Six faults, each of which is silent otherwise:
91
89
  *
@@ -96,9 +94,8 @@ export declare const findStep: (definition: WizardDefinition, stepId: string) =>
96
94
  * - **a step nothing reaches**, which is either a dead branch or a missing edge;
97
95
  * - **the wrong terminal step** — the flow must end in the summary, and there
98
96
  * must be exactly one of those. A wizard ending anywhere else commits without
99
- * the operator ever seeing what they answered, which
100
- * [#111](https://github.com/r10c-technologies/r10c/issues/111) records as not
101
- * optional.
97
+ * the operator ever seeing what they answered, which is not optional in a
98
+ * long flow.
102
99
  *
103
100
  * A cycle needs no separate check: every step is reachable from the entry and
104
101
  * exactly one is terminal, so a cycle would leave the steps on it unable to
@@ -1 +1 @@
1
- {"version":3,"file":"wizard-definition.d.ts","sourceRoot":"","sources":["../../src/wizard/wizard-definition.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,+DAMlB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,qDAAqD;AACrD,eAAO,MAAM,SAAS,GAAI,YAAY,gBAAgB,KAAG,UAMxD,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,GACnB,YAAY,gBAAgB,EAC5B,QAAQ,MAAM,KACb,UAAU,GAAG,SAA8D,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAiCzE"}
1
+ {"version":3,"file":"wizard-definition.d.ts","sourceRoot":"","sources":["../../src/wizard/wizard-definition.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,+DAMlB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAAC;CACvC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,qDAAqD;AACrD,eAAO,MAAM,SAAS,GAAI,YAAY,gBAAgB,KAAG,UAMxD,CAAC;AAEF,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,GACnB,YAAY,gBAAgB,EAC5B,QAAQ,MAAM,KACb,UAAU,GAAG,SAA8D,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAiCzE"}
@@ -29,8 +29,7 @@ import { EntifixBuildError } from '../base-entities/entifix-error/index.js';
29
29
  * branch names a step that does not exist, must fail on the first render of any
30
30
  * surface rather than on the render of the step nobody reached — otherwise the
31
31
  * failure of the longest flow in the product arrives at the end of it. That is
32
- * the posture `assertLinkSourcesAreEditable` and `surfaceFor` already take
33
- * ([ADR 0035](../../../../../../docs/adr/0035-entity-actions-selection-and-bulk.md)).
32
+ * the posture `assertLinkSourcesAreEditable` and `surfaceFor` already take.
34
33
  *
35
34
  * Six faults, each of which is silent otherwise:
36
35
  *
@@ -41,9 +40,8 @@ import { EntifixBuildError } from '../base-entities/entifix-error/index.js';
41
40
  * - **a step nothing reaches**, which is either a dead branch or a missing edge;
42
41
  * - **the wrong terminal step** — the flow must end in the summary, and there
43
42
  * must be exactly one of those. A wizard ending anywhere else commits without
44
- * the operator ever seeing what they answered, which
45
- * [#111](https://github.com/r10c-technologies/r10c/issues/111) records as not
46
- * optional.
43
+ * the operator ever seeing what they answered, which is not optional in a
44
+ * long flow.
47
45
  *
48
46
  * A cycle needs no separate check: every step is reachable from the entry and
49
47
  * exactly one is terminal, so a cycle would leave the steps on it unable to