@entifix/rest 0.13.0 → 0.15.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 (73) hide show
  1. package/dist/adapters/entity-rest/build-entity-rest-adapter-delete/build-entity-rest-adapter-delete.d.ts +1 -1
  2. package/dist/adapters/entity-rest/build-entity-rest-adapter-delete/build-entity-rest-adapter-delete.d.ts.map +1 -1
  3. package/dist/adapters/entity-rest/build-entity-rest-adapter-facets/build-entity-rest-adapter-facets.d.ts +1 -1
  4. package/dist/adapters/entity-rest/build-entity-rest-adapter-facets/build-entity-rest-adapter-facets.d.ts.map +1 -1
  5. package/dist/adapters/entity-rest/build-entity-rest-adapter-get/build-entity-rest-adapter-get.d.ts +1 -1
  6. package/dist/adapters/entity-rest/build-entity-rest-adapter-get/build-entity-rest-adapter-get.d.ts.map +1 -1
  7. package/dist/adapters/entity-rest/build-entity-rest-adapter-load/build-entity-rest-adapter-load.d.ts +1 -1
  8. package/dist/adapters/entity-rest/build-entity-rest-adapter-load/build-entity-rest-adapter-load.d.ts.map +1 -1
  9. package/dist/adapters/entity-rest/build-entity-rest-adapter-mixins/build-entity-base-url.d.ts +1 -1
  10. package/dist/adapters/entity-rest/build-entity-rest-adapter-mixins/build-entity-base-url.d.ts.map +1 -1
  11. package/dist/adapters/entity-rest/build-entity-rest-adapter-mixins/index.d.ts +1 -1
  12. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.d.ts +4 -8
  13. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.d.ts.map +1 -1
  14. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.js +15 -94
  15. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.js.map +1 -1
  16. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/build-entity-rest-adapter-write.d.ts +10 -0
  17. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/build-entity-rest-adapter-write.d.ts.map +1 -0
  18. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/build-entity-rest-adapter-write.js +49 -0
  19. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/build-entity-rest-adapter-write.js.map +1 -0
  20. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/index.d.ts +2 -0
  21. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/index.d.ts.map +1 -0
  22. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/index.js +3 -0
  23. package/dist/adapters/entity-rest/build-entity-rest-adapter-write/index.js.map +1 -0
  24. package/dist/adapters/entity-rest/pending-save-keys.d.ts +13 -0
  25. package/dist/adapters/entity-rest/pending-save-keys.d.ts.map +1 -0
  26. package/dist/adapters/entity-rest/pending-save-keys.js +13 -0
  27. package/dist/adapters/entity-rest/pending-save-keys.js.map +1 -0
  28. package/dist/adapters/entity-rest/read-write-response.d.ts +13 -0
  29. package/dist/adapters/entity-rest/read-write-response.d.ts.map +1 -0
  30. package/dist/adapters/entity-rest/read-write-response.js +17 -0
  31. package/dist/adapters/entity-rest/read-write-response.js.map +1 -0
  32. package/dist/adapters/entity-rest/types.d.ts +5 -15
  33. package/dist/adapters/entity-rest/types.d.ts.map +1 -1
  34. package/dist/adapters/entity-rest/types.js.map +1 -1
  35. package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.d.ts +3 -1
  36. package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.d.ts.map +1 -1
  37. package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.js +9 -2
  38. package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.js.map +1 -1
  39. package/dist/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.d.ts +19 -0
  40. package/dist/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.d.ts.map +1 -0
  41. package/dist/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.js +27 -0
  42. package/dist/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.js.map +1 -0
  43. package/dist/adapters/flow-status/build-flow-status-reader/index.d.ts +2 -0
  44. package/dist/adapters/flow-status/build-flow-status-reader/index.d.ts.map +1 -0
  45. package/dist/adapters/flow-status/build-flow-status-reader/index.js +3 -0
  46. package/dist/adapters/flow-status/build-flow-status-reader/index.js.map +1 -0
  47. package/dist/adapters/is-not-found.d.ts +7 -0
  48. package/dist/adapters/is-not-found.d.ts.map +1 -0
  49. package/dist/adapters/is-not-found.js +8 -0
  50. package/dist/adapters/is-not-found.js.map +1 -0
  51. package/dist/clients/types.d.ts +1 -1
  52. package/dist/clients/types.d.ts.map +1 -1
  53. package/dist/clients/types.js.map +1 -1
  54. package/dist/configuration-client/configuration-client-rest-client.d.ts.map +1 -1
  55. package/dist/configuration-client/configuration-client-rest-client.js +5 -3
  56. package/dist/configuration-client/configuration-client-rest-client.js.map +1 -1
  57. package/dist/endpoint/participant-client.d.ts +3 -2
  58. package/dist/endpoint/participant-client.d.ts.map +1 -1
  59. package/dist/endpoint/participant-client.js +10 -16
  60. package/dist/endpoint/participant-client.js.map +1 -1
  61. package/dist/index.d.ts +2 -1
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +2 -1
  64. package/dist/index.js.map +1 -1
  65. package/package.json +6 -6
  66. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.d.ts +0 -4
  67. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.d.ts.map +0 -1
  68. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.js +0 -47
  69. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.js.map +0 -1
  70. package/dist/adapters/transaction-status/build-transaction-status-reader/index.d.ts +0 -2
  71. package/dist/adapters/transaction-status/build-transaction-status-reader/index.d.ts.map +0 -1
  72. package/dist/adapters/transaction-status/build-transaction-status-reader/index.js +0 -3
  73. package/dist/adapters/transaction-status/build-transaction-status-reader/index.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/adapters/entity-use-case/make-entity-rest-use-case-runner.ts"],"sourcesContent":["import {\n type BulkOutcome,\n type Entity,\n type EntityConstructor,\n type EntityUseCaseRunner,\n extractMetaEntity,\n ProtocolFailure,\n toWireSelection,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\nimport { performHttpRequestThroughFetch } from '../../clients/fetch/index.js';\nimport { buildEntityRestAdapterMixins as adapterMixins } from '../entity-rest/build-entity-rest-adapter-mixins/index.js';\nimport type { BuildEntityRestOptions } from '../entity-rest/types.js';\n\n/**\n * Runs an entity's verbs against the service that serves them, at the path the\n * shell derives from each verb's key and binding — under the same base URL the\n * entity's other adapters use, so a same-origin proxy is followed the same way\n * ([ADR 0020](../../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)).\n *\n * A refusal arrives as the failure the service sent, decoded from its problem\n * body, so a screen shows the coded message rather than a generic error.\n */\nexport const makeEntityRestUseCaseRunner = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n { uriConfig, configuration }: BuildEntityRestOptions,\n): EntityUseCaseRunner => {\n const metaEntity = extractMetaEntity(entityConstructor);\n const name = metaEntity.key ?? metaEntity.name;\n const baseUrl = (id?: string) =>\n adapterMixins.buildEntityBaseUrl(configuration, { uriConfig }, name, id);\n\n return {\n run: (useCase, id) =>\n Effect.gen(function* () {\n const url = yield* baseUrl(\n useCase.binding === 'entity' && id != null ? String(id) : undefined,\n );\n yield* performHttpRequestThroughFetch({\n method: 'POST',\n url: `${url}/${useCase.key}`,\n });\n }),\n bulk: (useCase, selection) =>\n Effect.gen(function* () {\n const url = yield* baseUrl();\n // The wire form, never the selection itself: a `Set` serializes to\n // `{}`, so a selection sent raw would lose its ids.\n const response = yield* performHttpRequestThroughFetch<{\n readonly data?: unknown;\n }>({\n method: 'POST',\n url: `${url}/${useCase.key}`,\n headers: { 'Content-Type': 'application/json' },\n body: { selection: toWireSelection(selection) },\n });\n // A bulk answer is a `bulkOutcome` envelope, one outcome per record in\n // its `data`; anything else is a peer speaking something else.\n const outcomes = response.body?.data;\n if (!Array.isArray(outcomes)) {\n return yield* new ProtocolFailure({\n reason:\n 'a bulk answer carries its outcomes in a bulkOutcome envelope',\n });\n }\n return outcomes as readonly BulkOutcome[];\n }),\n };\n};\n"],"names":["extractMetaEntity","ProtocolFailure","toWireSelection","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","makeEntityRestUseCaseRunner","entityConstructor","uriConfig","configuration","metaEntity","name","key","baseUrl","id","buildEntityBaseUrl","run","useCase","gen","url","binding","String","undefined","method","bulk","selection","response","headers","body","outcomes","data","Array","isArray","reason"],"mappings":"AAAA,SAKEA,iBAAiB,EACjBC,eAAe,EACfC,eAAe,QACV,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,8BAA8B,QAAQ,+BAA+B;AAC9E,SAASC,gCAAgCC,aAAa,QAAQ,2DAA2D;AAGzH;;;;;;;;CAQC,GACD,OAAO,MAAMC,8BAA8B,CACzCC,mBACA,EAAEC,SAAS,EAAEC,aAAa,EAA0B;QAGvCC;IADb,MAAMA,aAAaX,kBAAkBQ;IACrC,MAAMI,QAAOD,kBAAAA,WAAWE,GAAG,YAAdF,kBAAkBA,WAAWC,IAAI;IAC9C,MAAME,UAAU,CAACC,KACfT,cAAcU,kBAAkB,CAACN,eAAe;YAAED;QAAU,GAAGG,MAAMG;IAEvE,OAAO;QACLE,KAAK,CAACC,SAASH,KACbZ,OAAOgB,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAON,QACjBI,QAAQG,OAAO,KAAK,YAAYN,MAAM,OAAOO,OAAOP,MAAMQ;gBAE5D,OAAOnB,+BAA+B;oBACpCoB,QAAQ;oBACRJ,KAAK,GAAGA,IAAI,CAAC,EAAEF,QAAQL,GAAG,EAAE;gBAC9B;YACF;QACFY,MAAM,CAACP,SAASQ,YACdvB,OAAOgB,GAAG,CAAC;oBAcQQ;gBAbjB,MAAMP,MAAM,OAAON;gBACnB,mEAAmE;gBACnE,oDAAoD;gBACpD,MAAMa,WAAW,OAAOvB,+BAErB;oBACDoB,QAAQ;oBACRJ,KAAK,GAAGA,IAAI,CAAC,EAAEF,QAAQL,GAAG,EAAE;oBAC5Be,SAAS;wBAAE,gBAAgB;oBAAmB;oBAC9CC,MAAM;wBAAEH,WAAWxB,gBAAgBwB;oBAAW;gBAChD;gBACA,uEAAuE;gBACvE,+DAA+D;gBAC/D,MAAMI,YAAWH,iBAAAA,SAASE,IAAI,qBAAbF,eAAeI,IAAI;gBACpC,IAAI,CAACC,MAAMC,OAAO,CAACH,WAAW;oBAC5B,OAAO,OAAO,IAAI7B,gBAAgB;wBAChCiC,QACE;oBACJ;gBACF;gBACA,OAAOJ;YACT;IACJ;AACF,EAAE"}
1
+ {"version":3,"sources":["../../../src/adapters/entity-use-case/make-entity-rest-use-case-runner.ts"],"sourcesContent":["import {\n type BulkOutcome,\n type Entity,\n type EntityConstructor,\n type EntityUseCaseRunner,\n extractMetaEntity,\n ProtocolFailure,\n toWireSelection,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\nimport { performHttpRequestThroughFetch } from '../../clients/fetch/index.js';\nimport { buildEntityRestAdapterMixins as adapterMixins } from '../entity-rest/build-entity-rest-adapter-mixins/index.js';\nimport { readWriteResponse } from '../entity-rest/read-write-response.js';\nimport type { BuildEntityRestOptions } from '../entity-rest/types.js';\n\n/**\n * Runs an entity's verbs against the service that serves them, at the path the\n * shell derives from each verb's key and binding — under the same base URL the\n * entity's other adapters use, so a same-origin proxy is followed the same way\n * ([ADR 0020](../../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)).\n *\n * A refusal arrives as the failure the service sent, decoded from its problem\n * body, so a screen shows the coded message rather than a generic error. A\n * verb that starts a flow answers `202 flowAccepted` and runs as pending; any\n * other success is settled with the `data` of what the service answered.\n */\nexport const makeEntityRestUseCaseRunner = <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n { uriConfig, configuration }: BuildEntityRestOptions,\n): EntityUseCaseRunner => {\n const metaEntity = extractMetaEntity(entityConstructor);\n const name = metaEntity.key ?? metaEntity.name;\n const baseUrl = (id?: string) =>\n adapterMixins.buildEntityBaseUrl(configuration, { uriConfig }, name, id);\n\n return {\n run: (useCase, id) =>\n Effect.gen(function* () {\n const url = yield* baseUrl(\n useCase.binding === 'entity' && id != null ? String(id) : undefined,\n );\n const response = yield* performHttpRequestThroughFetch<{\n readonly data?: unknown;\n }>({\n method: 'POST',\n url: `${url}/${useCase.key}`,\n });\n return yield* readWriteResponse(response, () =>\n Effect.succeed(response.body?.data),\n );\n }),\n bulk: (useCase, selection) =>\n Effect.gen(function* () {\n const url = yield* baseUrl();\n // The wire form, never the selection itself: a `Set` serializes to\n // `{}`, so a selection sent raw would lose its ids.\n const response = yield* performHttpRequestThroughFetch<{\n readonly data?: unknown;\n }>({\n method: 'POST',\n url: `${url}/${useCase.key}`,\n headers: { 'Content-Type': 'application/json' },\n body: { selection: toWireSelection(selection) },\n });\n // A bulk answer is a `bulkOutcome` envelope, one outcome per record in\n // its `data`; anything else is a peer speaking something else.\n const outcomes = response.body?.data;\n if (!Array.isArray(outcomes)) {\n return yield* new ProtocolFailure({\n reason:\n 'a bulk answer carries its outcomes in a bulkOutcome envelope',\n });\n }\n return outcomes as readonly BulkOutcome[];\n }),\n };\n};\n"],"names":["extractMetaEntity","ProtocolFailure","toWireSelection","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","readWriteResponse","makeEntityRestUseCaseRunner","entityConstructor","uriConfig","configuration","metaEntity","name","key","baseUrl","id","buildEntityBaseUrl","run","useCase","gen","url","binding","String","undefined","response","method","succeed","body","data","bulk","selection","headers","outcomes","Array","isArray","reason"],"mappings":"AAAA,SAKEA,iBAAiB,EACjBC,eAAe,EACfC,eAAe,QACV,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,8BAA8B,QAAQ,+BAA+B;AAC9E,SAASC,gCAAgCC,aAAa,QAAQ,2DAA2D;AACzH,SAASC,iBAAiB,QAAQ,wCAAwC;AAG1E;;;;;;;;;;CAUC,GACD,OAAO,MAAMC,8BAA8B,CACzCC,mBACA,EAAEC,SAAS,EAAEC,aAAa,EAA0B;QAGvCC;IADb,MAAMA,aAAaZ,kBAAkBS;IACrC,MAAMI,QAAOD,kBAAAA,WAAWE,GAAG,YAAdF,kBAAkBA,WAAWC,IAAI;IAC9C,MAAME,UAAU,CAACC,KACfV,cAAcW,kBAAkB,CAACN,eAAe;YAAED;QAAU,GAAGG,MAAMG;IAEvE,OAAO;QACLE,KAAK,CAACC,SAASH,KACbb,OAAOiB,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAON,QACjBI,QAAQG,OAAO,KAAK,YAAYN,MAAM,OAAOO,OAAOP,MAAMQ;gBAE5D,MAAMC,WAAW,OAAOrB,+BAErB;oBACDsB,QAAQ;oBACRL,KAAK,GAAGA,IAAI,CAAC,EAAEF,QAAQL,GAAG,EAAE;gBAC9B;gBACA,OAAO,OAAOP,kBAAkBkB,UAAU;wBACzBA;2BAAftB,OAAOwB,OAAO,EAACF,iBAAAA,SAASG,IAAI,qBAAbH,eAAeI,IAAI;;YAEtC;QACFC,MAAM,CAACX,SAASY,YACd5B,OAAOiB,GAAG,CAAC;oBAcQK;gBAbjB,MAAMJ,MAAM,OAAON;gBACnB,mEAAmE;gBACnE,oDAAoD;gBACpD,MAAMU,WAAW,OAAOrB,+BAErB;oBACDsB,QAAQ;oBACRL,KAAK,GAAGA,IAAI,CAAC,EAAEF,QAAQL,GAAG,EAAE;oBAC5BkB,SAAS;wBAAE,gBAAgB;oBAAmB;oBAC9CJ,MAAM;wBAAEG,WAAW7B,gBAAgB6B;oBAAW;gBAChD;gBACA,uEAAuE;gBACvE,+DAA+D;gBAC/D,MAAME,YAAWR,iBAAAA,SAASG,IAAI,qBAAbH,eAAeI,IAAI;gBACpC,IAAI,CAACK,MAAMC,OAAO,CAACF,WAAW;oBAC5B,OAAO,OAAO,IAAIhC,gBAAgB;wBAChCmC,QACE;oBACJ;gBACF;gBACA,OAAOH;YACT;IACJ;AACF,EAAE"}
@@ -0,0 +1,19 @@
1
+ import '@entifix/transactions';
2
+ import { type EntifixFailure } from '@entifix/core';
3
+ import type { FlowStatusBody } from '@entifix/transactions';
4
+ import { Effect } from 'effect';
5
+ /** Reads where a flow stands from the `status` link its `202` named. */
6
+ export type FlowStatusReader = (href: string) => Effect.Effect<FlowStatusBody | undefined, EntifixFailure>;
7
+ /**
8
+ * Reads a flow's `status` link over HTTP, for a browser settling a write whose
9
+ * outcome it did not hear pushed.
10
+ *
11
+ * ⚠️ **A `404` answers `undefined`, and that is not a failure.** A status read
12
+ * is a projection of the flow's events, so a flow that has just started may
13
+ * not be in it yet; the caller keeps the write pending, up to its own bound
14
+ * ([ADR 0022](../../../../../../docs/adr/0022-two-settle-modes-and-the-pending-flow-store.md)).
15
+ * Every other failure stays a failure, so an unreachable service is not read
16
+ * as "no such flow".
17
+ */
18
+ export declare const buildFlowStatusReader: () => FlowStatusReader;
19
+ //# sourceMappingURL=build-flow-status-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-flow-status-reader.d.ts","sourceRoot":"","sources":["../../../../src/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.ts"],"names":[],"mappings":"AACA,OAAO,uBAAuB,CAAC;AAE/B,OAAO,EAAkB,KAAK,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKhC,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,CAC7B,IAAI,EAAE,MAAM,KACT,MAAM,CAAC,MAAM,CAAC,cAAc,GAAG,SAAS,EAAE,cAAc,CAAC,CAAC;AAE/D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,QAAO,gBAatC,CAAC"}
@@ -0,0 +1,27 @@
1
+ // Loaded for its registration of `flowStatus`, which the decode below reads.
2
+ import '@entifix/transactions';
3
+ import { decodeEnvelope } from '@entifix/core';
4
+ import { Effect } from 'effect';
5
+ import { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';
6
+ import { isNotFound } from '../../is-not-found.js';
7
+ /**
8
+ * Reads a flow's `status` link over HTTP, for a browser settling a write whose
9
+ * outcome it did not hear pushed.
10
+ *
11
+ * ⚠️ **A `404` answers `undefined`, and that is not a failure.** A status read
12
+ * is a projection of the flow's events, so a flow that has just started may
13
+ * not be in it yet; the caller keeps the write pending, up to its own bound
14
+ * ([ADR 0022](../../../../../../docs/adr/0022-two-settle-modes-and-the-pending-flow-store.md)).
15
+ * Every other failure stays a failure, so an unreachable service is not read
16
+ * as "no such flow".
17
+ */ export const buildFlowStatusReader = ()=>(href)=>Effect.gen(function*() {
18
+ const response = yield* performHttpRequestThroughFetch({
19
+ method: 'GET',
20
+ url: href
21
+ });
22
+ const envelope = yield* decodeEnvelope(response.body, 'flowStatus');
23
+ // Decoded against the registered `flowStatus` schema just above.
24
+ return envelope.data;
25
+ }).pipe(Effect.catch((error)=>isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error)));
26
+
27
+ //# sourceMappingURL=build-flow-status-reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/adapters/flow-status/build-flow-status-reader/build-flow-status-reader.ts"],"sourcesContent":["// Loaded for its registration of `flowStatus`, which the decode below reads.\nimport '@entifix/transactions';\n\nimport { decodeEnvelope, type EntifixFailure } from '@entifix/core';\nimport type { FlowStatusBody } from '@entifix/transactions';\nimport { Effect } from 'effect';\n\nimport { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';\nimport { isNotFound } from '../../is-not-found.js';\n\n/** Reads where a flow stands from the `status` link its `202` named. */\nexport type FlowStatusReader = (\n href: string,\n) => Effect.Effect<FlowStatusBody | undefined, EntifixFailure>;\n\n/**\n * Reads a flow's `status` link over HTTP, for a browser settling a write whose\n * outcome it did not hear pushed.\n *\n * ⚠️ **A `404` answers `undefined`, and that is not a failure.** A status read\n * is a projection of the flow's events, so a flow that has just started may\n * not be in it yet; the caller keeps the write pending, up to its own bound\n * ([ADR 0022](../../../../../../docs/adr/0022-two-settle-modes-and-the-pending-flow-store.md)).\n * Every other failure stays a failure, so an unreachable service is not read\n * as \"no such flow\".\n */\nexport const buildFlowStatusReader = (): FlowStatusReader => (href: string) =>\n Effect.gen(function* () {\n const response = yield* performHttpRequestThroughFetch({\n method: 'GET',\n url: href,\n });\n const envelope = yield* decodeEnvelope(response.body, 'flowStatus');\n // Decoded against the registered `flowStatus` schema just above.\n return envelope.data as FlowStatusBody;\n }).pipe(\n Effect.catch(error =>\n isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error),\n ),\n );\n"],"names":["decodeEnvelope","Effect","performHttpRequestThroughFetch","isNotFound","buildFlowStatusReader","href","gen","response","method","url","envelope","body","data","pipe","catch","error","succeed","undefined","fail"],"mappings":"AAAA,6EAA6E;AAC7E,OAAO,wBAAwB;AAE/B,SAASA,cAAc,QAA6B,gBAAgB;AAEpE,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,8BAA8B,QAAQ,kCAAkC;AACjF,SAASC,UAAU,QAAQ,wBAAwB;AAOnD;;;;;;;;;;CAUC,GACD,OAAO,MAAMC,wBAAwB,IAAwB,CAACC,OAC5DJ,OAAOK,GAAG,CAAC;YACT,MAAMC,WAAW,OAAOL,+BAA+B;gBACrDM,QAAQ;gBACRC,KAAKJ;YACP;YACA,MAAMK,WAAW,OAAOV,eAAeO,SAASI,IAAI,EAAE;YACtD,iEAAiE;YACjE,OAAOD,SAASE,IAAI;QACtB,GAAGC,IAAI,CACLZ,OAAOa,KAAK,CAACC,CAAAA,QACXZ,WAAWY,SAASd,OAAOe,OAAO,CAACC,aAAahB,OAAOiB,IAAI,CAACH,SAE9D"}
@@ -0,0 +1,2 @@
1
+ export * from './build-flow-status-reader.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/flow-status/build-flow-status-reader/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from './build-flow-status-reader.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/adapters/flow-status/build-flow-status-reader/index.ts"],"sourcesContent":["export * from './build-flow-status-reader.js';\n"],"names":[],"mappings":"AAAA,cAAc,gCAAgC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A `404`, however it arrived: a service that answers a problem body says
3
+ * `NotFound`; one that answers no body at all is a protocol failure that
4
+ * still carries the status.
5
+ */
6
+ export declare const isNotFound: (error: unknown) => boolean;
7
+ //# sourceMappingURL=is-not-found.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-not-found.d.ts","sourceRoot":"","sources":["../../src/adapters/is-not-found.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,OAGgC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { isTag } from '@entifix/core';
2
+ /**
3
+ * A `404`, however it arrived: a service that answers a problem body says
4
+ * `NotFound`; one that answers no body at all is a protocol failure that
5
+ * still carries the status.
6
+ */ export const isNotFound = (error)=>isTag('NotFound')(error) || isTag('ProtocolFailure')(error) && error.upstreamStatus === 404;
7
+
8
+ //# sourceMappingURL=is-not-found.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/adapters/is-not-found.ts"],"sourcesContent":["import { isTag } from '@entifix/core';\n\n/**\n * A `404`, however it arrived: a service that answers a problem body says\n * `NotFound`; one that answers no body at all is a protocol failure that\n * still carries the status.\n */\nexport const isNotFound = (error: unknown): boolean =>\n isTag('NotFound')(error) ||\n (isTag('ProtocolFailure')(error) &&\n (error as { readonly upstreamStatus?: unknown }).upstreamStatus === 404);\n"],"names":["isTag","isNotFound","error","upstreamStatus"],"mappings":"AAAA,SAASA,KAAK,QAAQ,gBAAgB;AAEtC;;;;CAIC,GACD,OAAO,MAAMC,aAAa,CAACC,QACzBF,MAAM,YAAYE,UACjBF,MAAM,mBAAmBE,UACxB,AAACA,MAAgDC,cAAc,KAAK,IAAK"}
@@ -1,4 +1,4 @@
1
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
1
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
2
2
  export type HttpHeaders = Record<string, string>;
3
3
  export interface HttpRequest<TBody = never> {
4
4
  method: HttpMethod;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/clients/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAE3D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,KAAK;IACxC,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,YAAY,CAAC,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,KAAK,CAAC;CACb"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/clients/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAErE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjD,MAAM,WAAW,WAAW,CAAC,KAAK,GAAG,KAAK;IACxC,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,IAAI,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,YAAY,CAAC,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,WAAW,CAAC;IACrB,IAAI,EAAE,KAAK,CAAC;CACb"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/clients/types.ts"],"sourcesContent":["export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';\n\nexport type HttpHeaders = Record<string, string>;\n\nexport interface HttpRequest<TBody = never> {\n method: HttpMethod;\n url: string;\n headers?: HttpHeaders;\n body?: TBody;\n}\n\nexport interface HttpResponse<TBody> {\n status: number;\n headers: HttpHeaders;\n body: TBody;\n}\n"],"names":[],"mappings":"AAWA,WAIC"}
1
+ {"version":3,"sources":["../../src/clients/types.ts"],"sourcesContent":["export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n\nexport type HttpHeaders = Record<string, string>;\n\nexport interface HttpRequest<TBody = never> {\n method: HttpMethod;\n url: string;\n headers?: HttpHeaders;\n body?: TBody;\n}\n\nexport interface HttpResponse<TBody> {\n status: number;\n headers: HttpHeaders;\n body: TBody;\n}\n"],"names":[],"mappings":"AAWA,WAIC"}
@@ -1 +1 @@
1
- {"version":3,"file":"configuration-client-rest-client.d.ts","sourceRoot":"","sources":["../../src/configuration-client/configuration-client-rest-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EAKzB,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,oCAAoC;IACnD,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5C;AAuED;;;;;;;;;;;;GAYG;AACH,qBAAa,6BAA8B,YAAW,mBAAmB;;gBAG3D,OAAO,CAAC,EAAE,oCAAoC;IA+B1D,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB;CAG5C"}
1
+ {"version":3,"file":"configuration-client-rest-client.d.ts","sourceRoot":"","sources":["../../src/configuration-client/configuration-client-rest-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EAKzB,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,oCAAoC;IACnD,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAC5C;AAoED;;;;;;;;;;;;GAYG;AACH,qBAAa,6BAA8B,YAAW,mBAAmB;;gBAG3D,OAAO,CAAC,EAAE,oCAAoC;IAgC1D,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,wBAAwB;CAG5C"}
@@ -1,6 +1,6 @@
1
1
  import { _ as _class_private_field_loose_base } from "@swc/helpers/_/_class_private_field_loose_base";
2
2
  import { _ as _class_private_field_loose_key } from "@swc/helpers/_/_class_private_field_loose_key";
3
- import { ConfigurationClientInMemory, EntifixBuildError } from '@entifix/core';
3
+ import { ConfigurationClientInMemory, Unavailable } from '@entifix/core';
4
4
  import { Effect } from 'effect';
5
5
  const DEFAULT_CONFIG_URL = '/api/config';
6
6
  var _pending = /*#__PURE__*/ _class_private_field_loose_key("_pending");
@@ -102,8 +102,10 @@ var _loadPlain = /*#__PURE__*/ _class_private_field_loose_key("_loadPlain");
102
102
  };
103
103
  _class_private_field_loose_base(this, _loadPlain)[_loadPlain] = Effect.tryPromise({
104
104
  try: load,
105
- catch: (error)=>new EntifixBuildError('Failed to load configuration', error, {
106
- url
105
+ // The configuration source could not be reached or did not answer: a
106
+ // dependency, so the next read asks again.
107
+ catch: ()=>new Unavailable({
108
+ dependency: url
107
109
  })
108
110
  });
109
111
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/configuration-client/configuration-client-rest-client.ts"],"sourcesContent":["import {\n ConfigurationClient,\n ConfigurationClientGroup,\n ConfigurationClientInMemory,\n ConfigurationExtractMode,\n ConfigurationPlain,\n EntifixBuildError,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\nexport interface ConfigurationClientRestClientOptions {\n /** Endpoint returning this service's `ConfigurationPlain`. */\n url?: string;\n /**\n * Headers to send with the lookup.\n *\n * Empty for the browser, which reads its own app's same-origin `/api/config`\n * and needs nothing. A **server** component reads config-service directly —\n * there is no origin to proxy through and no browser to hide an address from\n * — and that endpoint is gated on the shared fleet token, so the caller has\n * to carry it. The header itself stays out of this package: the token is a\n * Next server concern and lives in `@entifix/next-shell`.\n */\n headers?: Readonly<Record<string, string>>;\n}\n\nconst DEFAULT_CONFIG_URL = '/api/config';\n\n/**\n * {@link ConfigurationClientGroup} view over an HTTP-sourced\n * {@link ConfigurationPlain}. Resolution is delegated to an in-memory store\n * once the plain configuration has been fetched.\n */\nclass ConfigurationClientGroupRestClient implements ConfigurationClientGroup {\n constructor(\n private readonly loadPlain: Effect.Effect<\n ConfigurationPlain,\n EntifixBuildError\n >,\n private readonly group: string,\n ) {}\n\n #pending() {\n return Effect.die(\n new Error(\n 'ConfigurationClientGroupRestClient getter not implemented yet',\n ),\n );\n }\n\n getString(key: string, extractMode?: ConfigurationExtractMode) {\n return this.loadPlain.pipe(\n Effect.flatMap(plain =>\n new ConfigurationClientInMemory(plain)\n .in(this.group)\n .getString(key, extractMode),\n ),\n );\n }\n\n getNumber() {\n return this.#pending();\n }\n getDate() {\n return this.#pending();\n }\n getArrayNumber() {\n return this.#pending();\n }\n getArrayString() {\n return this.#pending();\n }\n getArrayDate() {\n return this.#pending();\n }\n getOptionalNumber() {\n return this.#pending();\n }\n getOptionalString() {\n return this.#pending();\n }\n getOptionalDate() {\n return this.#pending();\n }\n getOptionalArrayNumber() {\n return this.#pending();\n }\n getOptionalArrayString() {\n return this.#pending();\n }\n getOptionalArrayDate() {\n return this.#pending();\n }\n}\n\n/**\n * {@link ConfigurationClient} that sources its `ConfigurationPlain` over HTTP\n * (by default the same-origin `/api/config` route, which proxies\n * marketplace-config-api). The fetched configuration is memoized so it is\n * requested at most once per client instance — **once it has arrived**.\n *\n * ⚠️ A failed load is not memoized. Clients are usually built once per process\n * (a server component's module scope), and in a fleet that starts together the\n * first read routinely races config-service's boot. Caching that rejection made\n * the failure permanent: a storefront rendered an empty catalog until it was\n * restarted, while every service it reads from was healthy. Callers that arrive\n * while a request is in flight still share it.\n */\nexport class ConfigurationClientRestClient implements ConfigurationClient {\n #loadPlain: Effect.Effect<ConfigurationPlain, EntifixBuildError>;\n\n constructor(options?: ConfigurationClientRestClientOptions) {\n const url = options?.url ?? DEFAULT_CONFIG_URL;\n const headers = options?.headers;\n let cache: Promise<ConfigurationPlain> | undefined;\n\n const load = (): Promise<ConfigurationPlain> => {\n if (cache !== undefined) return cache;\n const pending = fetch(url, { headers }).then(response => {\n if (!response.ok) {\n throw new Error(\n `Configuration request to ${url} failed with status ${response.status}`,\n );\n }\n return response.json() as Promise<ConfigurationPlain>;\n });\n cache = pending;\n // Forget a failure so the next read asks again. Nothing else clears the\n // cache, so the entry being cleared is always this request's own.\n pending.catch(() => {\n cache = undefined;\n });\n return pending;\n };\n\n this.#loadPlain = Effect.tryPromise({\n try: load,\n catch: error =>\n new EntifixBuildError('Failed to load configuration', error, { url }),\n });\n }\n\n in(group: string): ConfigurationClientGroup {\n return new ConfigurationClientGroupRestClient(this.#loadPlain, group);\n }\n}\n"],"names":["ConfigurationClientInMemory","EntifixBuildError","Effect","DEFAULT_CONFIG_URL","ConfigurationClientGroupRestClient","getString","key","extractMode","loadPlain","pipe","flatMap","plain","in","group","getNumber","getDate","getArrayNumber","getArrayString","getArrayDate","getOptionalNumber","getOptionalString","getOptionalDate","getOptionalArrayNumber","getOptionalArrayString","getOptionalArrayDate","die","Error","ConfigurationClientRestClient","options","url","headers","cache","load","undefined","pending","fetch","then","response","ok","status","json","catch","tryPromise","try","error"],"mappings":";;AAAA,SAGEA,2BAA2B,EAG3BC,iBAAiB,QACZ,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAkBhC,MAAMC,qBAAqB;IAgBzB;AAdF;;;;CAIC,GACD,IAAA,AAAMC,qCAAN,MAAMA;IAiBJC,UAAUC,GAAW,EAAEC,WAAsC,EAAE;QAC7D,OAAO,IAAI,CAACC,SAAS,CAACC,IAAI,CACxBP,OAAOQ,OAAO,CAACC,CAAAA,QACb,IAAIX,4BAA4BW,OAC7BC,EAAE,CAAC,IAAI,CAACC,KAAK,EACbR,SAAS,CAACC,KAAKC;IAGxB;IAEAO,YAAY;QACV,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,UAAU;QACR,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,iBAAiB;QACf,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,iBAAiB;QACf,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,eAAe;QACb,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,oBAAoB;QAClB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,oBAAoB;QAClB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,kBAAkB;QAChB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,yBAAyB;QACvB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,yBAAyB;QACvB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,uBAAuB;QACrB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IA1DA,YACE,AAAiBhB,SAGhB,EACD,AAAiBK,KAAa,CAC9B;QAEF,4BAAA;mBAAA;;aAPmBL,YAAAA;aAIAK,QAAAA;IAChB;AAqDL;AAnDE,SAAA;IACE,OAAOX,OAAOuB,GAAG,CACf,IAAIC,MACF;AAGN;IA6DA;AAdF;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC;IAkCXf,GAAGC,KAAa,EAA4B;QAC1C,OAAO,IAAIT,mCAAmC,gCAAA,IAAI,EAAC,YAAA,aAAYS;IACjE;IAjCA,YAAYe,OAA8C,CAAE;;QAF5D,4BAAA;;mBAAA,KAAA;;QAGE,MAAMC,cAAMD,2BAAAA,QAASC,GAAG,mBAAI1B;QAC5B,MAAM2B,UAAUF,2BAAAA,QAASE,OAAO;QAChC,IAAIC;QAEJ,MAAMC,OAAO;YACX,IAAID,UAAUE,WAAW,OAAOF;YAChC,MAAMG,UAAUC,MAAMN,KAAK;gBAAEC;YAAQ,GAAGM,IAAI,CAACC,CAAAA;gBAC3C,IAAI,CAACA,SAASC,EAAE,EAAE;oBAChB,MAAM,IAAIZ,MACR,CAAC,yBAAyB,EAAEG,IAAI,oBAAoB,EAAEQ,SAASE,MAAM,EAAE;gBAE3E;gBACA,OAAOF,SAASG,IAAI;YACtB;YACAT,QAAQG;YACR,wEAAwE;YACxE,kEAAkE;YAClEA,QAAQO,KAAK,CAAC;gBACZV,QAAQE;YACV;YACA,OAAOC;QACT;QAEA,gCAAA,IAAI,EAAC,YAAA,cAAahC,OAAOwC,UAAU,CAAC;YAClCC,KAAKX;YACLS,OAAOG,CAAAA,QACL,IAAI3C,kBAAkB,gCAAgC2C,OAAO;oBAAEf;gBAAI;QACvE;IACF;AAKF"}
1
+ {"version":3,"sources":["../../src/configuration-client/configuration-client-rest-client.ts"],"sourcesContent":["import {\n ConfigurationClient,\n ConfigurationClientGroup,\n ConfigurationClientInMemory,\n ConfigurationExtractMode,\n ConfigurationPlain,\n Unavailable,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\nexport interface ConfigurationClientRestClientOptions {\n /** Endpoint returning this service's `ConfigurationPlain`. */\n url?: string;\n /**\n * Headers to send with the lookup.\n *\n * Empty for the browser, which reads its own app's same-origin `/api/config`\n * and needs nothing. A **server** component reads config-service directly —\n * there is no origin to proxy through and no browser to hide an address from\n * — and that endpoint is gated on the shared fleet token, so the caller has\n * to carry it. The header itself stays out of this package: the token is a\n * Next server concern and lives in `@entifix/next-shell`.\n */\n headers?: Readonly<Record<string, string>>;\n}\n\nconst DEFAULT_CONFIG_URL = '/api/config';\n\n/**\n * {@link ConfigurationClientGroup} view over an HTTP-sourced\n * {@link ConfigurationPlain}. Resolution is delegated to an in-memory store\n * once the plain configuration has been fetched.\n */\nclass ConfigurationClientGroupRestClient implements ConfigurationClientGroup {\n constructor(\n private readonly loadPlain: Effect.Effect<ConfigurationPlain, Unavailable>,\n private readonly group: string,\n ) {}\n\n #pending() {\n return Effect.die(\n new Error(\n 'ConfigurationClientGroupRestClient getter not implemented yet',\n ),\n );\n }\n\n getString(key: string, extractMode?: ConfigurationExtractMode) {\n return this.loadPlain.pipe(\n Effect.flatMap(plain =>\n new ConfigurationClientInMemory(plain)\n .in(this.group)\n .getString(key, extractMode),\n ),\n );\n }\n\n getNumber() {\n return this.#pending();\n }\n getDate() {\n return this.#pending();\n }\n getArrayNumber() {\n return this.#pending();\n }\n getArrayString() {\n return this.#pending();\n }\n getArrayDate() {\n return this.#pending();\n }\n getOptionalNumber() {\n return this.#pending();\n }\n getOptionalString() {\n return this.#pending();\n }\n getOptionalDate() {\n return this.#pending();\n }\n getOptionalArrayNumber() {\n return this.#pending();\n }\n getOptionalArrayString() {\n return this.#pending();\n }\n getOptionalArrayDate() {\n return this.#pending();\n }\n}\n\n/**\n * {@link ConfigurationClient} that sources its `ConfigurationPlain` over HTTP\n * (by default the same-origin `/api/config` route, which proxies\n * marketplace-config-api). The fetched configuration is memoized so it is\n * requested at most once per client instance — **once it has arrived**.\n *\n * ⚠️ A failed load is not memoized. Clients are usually built once per process\n * (a server component's module scope), and in a fleet that starts together the\n * first read routinely races config-service's boot. Caching that rejection made\n * the failure permanent: a storefront rendered an empty catalog until it was\n * restarted, while every service it reads from was healthy. Callers that arrive\n * while a request is in flight still share it.\n */\nexport class ConfigurationClientRestClient implements ConfigurationClient {\n #loadPlain: Effect.Effect<ConfigurationPlain, Unavailable>;\n\n constructor(options?: ConfigurationClientRestClientOptions) {\n const url = options?.url ?? DEFAULT_CONFIG_URL;\n const headers = options?.headers;\n let cache: Promise<ConfigurationPlain> | undefined;\n\n const load = (): Promise<ConfigurationPlain> => {\n if (cache !== undefined) return cache;\n const pending = fetch(url, { headers }).then(response => {\n if (!response.ok) {\n throw new Error(\n `Configuration request to ${url} failed with status ${response.status}`,\n );\n }\n return response.json() as Promise<ConfigurationPlain>;\n });\n cache = pending;\n // Forget a failure so the next read asks again. Nothing else clears the\n // cache, so the entry being cleared is always this request's own.\n pending.catch(() => {\n cache = undefined;\n });\n return pending;\n };\n\n this.#loadPlain = Effect.tryPromise({\n try: load,\n // The configuration source could not be reached or did not answer: a\n // dependency, so the next read asks again.\n catch: () => new Unavailable({ dependency: url }),\n });\n }\n\n in(group: string): ConfigurationClientGroup {\n return new ConfigurationClientGroupRestClient(this.#loadPlain, group);\n }\n}\n"],"names":["ConfigurationClientInMemory","Unavailable","Effect","DEFAULT_CONFIG_URL","ConfigurationClientGroupRestClient","getString","key","extractMode","loadPlain","pipe","flatMap","plain","in","group","getNumber","getDate","getArrayNumber","getArrayString","getArrayDate","getOptionalNumber","getOptionalString","getOptionalDate","getOptionalArrayNumber","getOptionalArrayString","getOptionalArrayDate","die","Error","ConfigurationClientRestClient","options","url","headers","cache","load","undefined","pending","fetch","then","response","ok","status","json","catch","tryPromise","try","dependency"],"mappings":";;AAAA,SAGEA,2BAA2B,EAG3BC,WAAW,QACN,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAkBhC,MAAMC,qBAAqB;IAazB;AAXF;;;;CAIC,GACD,IAAA,AAAMC,qCAAN,MAAMA;IAcJC,UAAUC,GAAW,EAAEC,WAAsC,EAAE;QAC7D,OAAO,IAAI,CAACC,SAAS,CAACC,IAAI,CACxBP,OAAOQ,OAAO,CAACC,CAAAA,QACb,IAAIX,4BAA4BW,OAC7BC,EAAE,CAAC,IAAI,CAACC,KAAK,EACbR,SAAS,CAACC,KAAKC;IAGxB;IAEAO,YAAY;QACV,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,UAAU;QACR,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,iBAAiB;QACf,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,iBAAiB;QACf,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,eAAe;QACb,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,oBAAoB;QAClB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,oBAAoB;QAClB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,kBAAkB;QAChB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,yBAAyB;QACvB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,yBAAyB;QACvB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IACAC,uBAAuB;QACrB,OAAO,gCAAA,IAAI,EAAC,UAAA;IACd;IAvDA,YACE,AAAiBhB,SAAyD,EAC1E,AAAiBK,KAAa,CAC9B;QAEF,4BAAA;mBAAA;;aAJmBL,YAAAA;aACAK,QAAAA;IAChB;AAqDL;AAnDE,SAAA;IACE,OAAOX,OAAOuB,GAAG,CACf,IAAIC,MACF;AAGN;IA6DA;AAdF;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC;IAmCXf,GAAGC,KAAa,EAA4B;QAC1C,OAAO,IAAIT,mCAAmC,gCAAA,IAAI,EAAC,YAAA,aAAYS;IACjE;IAlCA,YAAYe,OAA8C,CAAE;;QAF5D,4BAAA;;mBAAA,KAAA;;QAGE,MAAMC,cAAMD,2BAAAA,QAASC,GAAG,mBAAI1B;QAC5B,MAAM2B,UAAUF,2BAAAA,QAASE,OAAO;QAChC,IAAIC;QAEJ,MAAMC,OAAO;YACX,IAAID,UAAUE,WAAW,OAAOF;YAChC,MAAMG,UAAUC,MAAMN,KAAK;gBAAEC;YAAQ,GAAGM,IAAI,CAACC,CAAAA;gBAC3C,IAAI,CAACA,SAASC,EAAE,EAAE;oBAChB,MAAM,IAAIZ,MACR,CAAC,yBAAyB,EAAEG,IAAI,oBAAoB,EAAEQ,SAASE,MAAM,EAAE;gBAE3E;gBACA,OAAOF,SAASG,IAAI;YACtB;YACAT,QAAQG;YACR,wEAAwE;YACxE,kEAAkE;YAClEA,QAAQO,KAAK,CAAC;gBACZV,QAAQE;YACV;YACA,OAAOC;QACT;QAEA,gCAAA,IAAI,EAAC,YAAA,cAAahC,OAAOwC,UAAU,CAAC;YAClCC,KAAKX;YACL,qEAAqE;YACrE,2CAA2C;YAC3CS,OAAO,IAAM,IAAIxC,YAAY;oBAAE2C,YAAYf;gBAAI;QACjD;IACF;AAKF"}
@@ -11,8 +11,9 @@ export interface ParticipantAddress {
11
11
  * Calls participants over HTTP, as the slip pinned each step's call
12
12
  * ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):
13
13
  * the crossing token the participant takes, the step's `Idempotency-Key`, the
14
- * flow's remaining deadline, and a body of the step's parameters and what
15
- * earlier steps answered. A refusal decodes into the failure the participant
14
+ * flow's remaining deadline, the organization the inputs name, and the inputs
15
+ * themselves as a flat JSON body — what the participant's own route declares,
16
+ * never a wrapper of the flow's. A refusal decodes into the failure the participant
16
17
  * sent, by its code; a body that is not a problem is `protocol`; no answer at
17
18
  * all is `unavailable`. A step with no call — the commit point that names no
18
19
  * participant — answers at once.
@@ -1 +1 @@
1
- {"version":3,"file":"participant-client.d.ts","sourceRoot":"","sources":["../../src/endpoint/participant-client.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,iBAAiB,EACtB,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAU,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAE5D,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACnD;AA8BD;;;;;;;;;GASG;AACH,eAAO,MAAM,yBAAyB,GACpC,QAAQ,UAAU,CAAC,UAAU,EAC7B,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,KACzD,iBA+DD,CAAC;AAEH,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,GACrC,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,oEAOzD,CAAC"}
1
+ {"version":3,"file":"participant-client.d.ts","sourceRoot":"","sources":["../../src/endpoint/participant-client.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,iBAAiB,EACtB,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAU,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AACjD,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAE5D,gEAAgE;AAChE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CACnD;AAqBD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,GACpC,QAAQ,UAAU,CAAC,UAAU,EAC7B,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,KACzD,iBA8DD,CAAC;AAEH,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,GACrC,cAAc,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,oEAOzD,CAAC"}
@@ -12,27 +12,24 @@ const parse = (text)=>{
12
12
  return undefined;
13
13
  }
14
14
  };
15
- /**
16
- * The step's path with each `:name` taken from its parameters, then from what
17
- * earlier steps answered — the order a flow learns ids in.
18
- */ const pathOf = (call, path)=>path.replace(/:([A-Za-z0-9_]+)/g, (_match, name)=>{
19
- var _ref, _ref1;
20
- var _call_step_parameters, _call_outputs_name;
21
- return encodeURIComponent(String((_ref = (_ref1 = (_call_step_parameters = call.step.parameters) == null ? void 0 : _call_step_parameters[name]) != null ? _ref1 : (_call_outputs_name = call.outputs[name]) == null ? void 0 : _call_outputs_name['id']) != null ? _ref : ''));
15
+ /** The step's path with each `:name` taken from the call's inputs. */ const pathOf = (call, path)=>path.replace(/:([A-Za-z0-9_]+)/g, (_match, name)=>{
16
+ var _call_inputs_name;
17
+ return encodeURIComponent(String((_call_inputs_name = call.inputs[name]) != null ? _call_inputs_name : ''));
22
18
  });
23
19
  /**
24
20
  * Calls participants over HTTP, as the slip pinned each step's call
25
21
  * ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):
26
22
  * the crossing token the participant takes, the step's `Idempotency-Key`, the
27
- * flow's remaining deadline, and a body of the step's parameters and what
28
- * earlier steps answered. A refusal decodes into the failure the participant
23
+ * flow's remaining deadline, the organization the inputs name, and the inputs
24
+ * themselves as a flat JSON body — what the participant's own route declares,
25
+ * never a wrapper of the flow's. A refusal decodes into the failure the participant
29
26
  * sent, by its code; a body that is not a problem is `protocol`; no answer at
30
27
  * all is `unavailable`. A step with no call — the commit point that names no
31
28
  * participant — answers at once.
32
29
  */ export const makeHttpParticipantClient = (client, participants)=>({
33
30
  call: (request)=>Effect.gen(function*() {
34
- var _request_step_participant, _request_step_parameters;
35
- var _request_step_parameters1, _response_headers_contenttype;
31
+ var _request_step_participant;
32
+ var _response_headers_contenttype;
36
33
  const target = request.kind === 'action' ? request.step.call : request.step.compensation;
37
34
  if (target === undefined) return undefined;
38
35
  const name = (_request_step_participant = request.step.participant) != null ? _request_step_participant : '';
@@ -43,7 +40,7 @@ const parse = (text)=>{
43
40
  if (remaining === 0) return yield* Effect.fail(new Timeout({
44
41
  dependency
45
42
  }));
46
- const organizationId = (_request_step_parameters1 = request.step.parameters) == null ? void 0 : _request_step_parameters1['organizationId'];
43
+ const organizationId = request.inputs['organizationId'];
47
44
  const outgoing = HttpClientRequest.make(target.method)(`${address.baseUrl}${pathOf(request, target.path)}`).pipe(HttpClientRequest.setHeaders(_extends({
48
45
  accept: 'application/json',
49
46
  [CROSSING_TOKEN_HEADER]: Redacted.value(address.crossingToken),
@@ -52,10 +49,7 @@ const parse = (text)=>{
52
49
  [ORGANIZATION_HEADER]: organizationId
53
50
  } : {}, remaining === undefined ? {} : {
54
51
  [DEADLINE_HEADER]: String(remaining)
55
- })), HttpClientRequest.bodyText(JSON.stringify({
56
- parameters: (_request_step_parameters = request.step.parameters) != null ? _request_step_parameters : {},
57
- outputs: request.outputs
58
- }), 'application/json'));
52
+ })), target.method === 'GET' ? (outgoing)=>outgoing : HttpClientRequest.bodyText(JSON.stringify(request.inputs), 'application/json'));
59
53
  const response = yield* client.execute(outgoing).pipe(Effect.mapError(()=>new Unavailable({
60
54
  dependency
61
55
  })));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/endpoint/participant-client.ts"],"sourcesContent":["import {\n currentRemainingMs,\n DEADLINE_HEADER,\n decodeProblem,\n type EntifixFailure,\n formatIdempotencyKey,\n IDEMPOTENCY_KEY_HEADER,\n Internal,\n PROBLEM_CONTENT_TYPE,\n ProtocolFailure,\n Timeout,\n Unavailable,\n} from '@entifix/core';\nimport {\n type ParticipantCall,\n type ParticipantClient,\n ParticipantClientTag,\n} from '@entifix/transactions';\nimport { Effect, Layer, Redacted } from 'effect';\nimport { HttpClient, HttpClientRequest } from 'effect/http';\n\n/** Where one participant is reached, and the token it takes. */\nexport interface ParticipantAddress {\n readonly baseUrl: string;\n /** The crossing token this caller presents to that participant. */\n readonly crossingToken: Redacted.Redacted<string>;\n}\n\n/** The header a crossing token travels in. */\nconst CROSSING_TOKEN_HEADER = 'x-crossing-token';\n/** The header a crossing names the organization it acts for in. */\nconst ORGANIZATION_HEADER = 'x-organization-id';\n\nconst parse = (text: string): unknown => {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * The step's path with each `:name` taken from its parameters, then from what\n * earlier steps answered — the order a flow learns ids in.\n */\nconst pathOf = (call: ParticipantCall, path: string) =>\n path.replace(/:([A-Za-z0-9_]+)/g, (_match, name: string) =>\n encodeURIComponent(\n String(\n call.step.parameters?.[name] ??\n (call.outputs[name] as Record<string, unknown> | undefined)?.['id'] ??\n '',\n ),\n ),\n );\n\n/**\n * Calls participants over HTTP, as the slip pinned each step's call\n * ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):\n * the crossing token the participant takes, the step's `Idempotency-Key`, the\n * flow's remaining deadline, and a body of the step's parameters and what\n * earlier steps answered. A refusal decodes into the failure the participant\n * sent, by its code; a body that is not a problem is `protocol`; no answer at\n * all is `unavailable`. A step with no call — the commit point that names no\n * participant — answers at once.\n */\nexport const makeHttpParticipantClient = (\n client: HttpClient.HttpClient,\n participants: Readonly<Record<string, ParticipantAddress>>,\n): ParticipantClient => ({\n call: request =>\n Effect.gen(function* () {\n const target =\n request.kind === 'action'\n ? request.step.call\n : request.step.compensation;\n if (target === undefined) return undefined;\n const name = request.step.participant ?? '';\n const address = participants[name];\n if (address === undefined)\n return yield* Effect.fail(new Internal() as EntifixFailure);\n const dependency = `${name} ${target.method} ${target.path}`;\n\n const remaining = yield* currentRemainingMs;\n if (remaining === 0)\n return yield* Effect.fail(new Timeout({ dependency }));\n\n const organizationId = request.step.parameters?.['organizationId'];\n const outgoing = HttpClientRequest.make(target.method)(\n `${address.baseUrl}${pathOf(request, target.path)}`,\n ).pipe(\n HttpClientRequest.setHeaders({\n accept: 'application/json',\n [CROSSING_TOKEN_HEADER]: Redacted.value(address.crossingToken),\n [IDEMPOTENCY_KEY_HEADER]: formatIdempotencyKey(\n request.idempotencyKey,\n ),\n ...(typeof organizationId === 'string'\n ? { [ORGANIZATION_HEADER]: organizationId }\n : {}),\n ...(remaining === undefined\n ? {}\n : { [DEADLINE_HEADER]: String(remaining) }),\n }),\n HttpClientRequest.bodyText(\n JSON.stringify({\n parameters: request.step.parameters ?? {},\n outputs: request.outputs,\n }),\n 'application/json',\n ),\n );\n\n const response = yield* client\n .execute(outgoing)\n .pipe(Effect.mapError(() => new Unavailable({ dependency })));\n const text = yield* response.text.pipe(\n Effect.mapError(() => new Unavailable({ dependency })),\n );\n if (response.status < 400) return text === '' ? undefined : parse(text);\n const isProblem =\n response.headers['content-type']?.startsWith(PROBLEM_CONTENT_TYPE) ===\n true;\n return yield* Effect.fail(\n isProblem\n ? decodeProblem(parse(text), response.status)\n : new ProtocolFailure({\n reason: `${dependency} answered ${String(response.status)} without a problem body`,\n upstreamStatus: response.status,\n }),\n );\n }),\n});\n\n/** The HTTP participant client over the host's `HttpClient`. */\nexport const HttpParticipantClientLayer = (\n participants: Readonly<Record<string, ParticipantAddress>>,\n) =>\n Layer.effect(\n ParticipantClientTag,\n Effect.map(HttpClient.HttpClient, client =>\n makeHttpParticipantClient(client, participants),\n ),\n );\n"],"names":["currentRemainingMs","DEADLINE_HEADER","decodeProblem","formatIdempotencyKey","IDEMPOTENCY_KEY_HEADER","Internal","PROBLEM_CONTENT_TYPE","ProtocolFailure","Timeout","Unavailable","ParticipantClientTag","Effect","Layer","Redacted","HttpClient","HttpClientRequest","CROSSING_TOKEN_HEADER","ORGANIZATION_HEADER","parse","text","JSON","undefined","pathOf","call","path","replace","_match","name","encodeURIComponent","String","step","parameters","outputs","makeHttpParticipantClient","client","participants","request","gen","response","target","kind","compensation","participant","address","fail","dependency","method","remaining","organizationId","outgoing","make","baseUrl","pipe","setHeaders","accept","value","crossingToken","idempotencyKey","bodyText","stringify","execute","mapError","status","isProblem","headers","startsWith","reason","upstreamStatus","HttpParticipantClientLayer","effect","map"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/endpoint/participant-client.ts"],"sourcesContent":["import {\n currentRemainingMs,\n DEADLINE_HEADER,\n decodeProblem,\n type EntifixFailure,\n formatIdempotencyKey,\n IDEMPOTENCY_KEY_HEADER,\n Internal,\n PROBLEM_CONTENT_TYPE,\n ProtocolFailure,\n Timeout,\n Unavailable,\n} from '@entifix/core';\nimport {\n type ParticipantCall,\n type ParticipantClient,\n ParticipantClientTag,\n} from '@entifix/transactions';\nimport { Effect, Layer, Redacted } from 'effect';\nimport { HttpClient, HttpClientRequest } from 'effect/http';\n\n/** Where one participant is reached, and the token it takes. */\nexport interface ParticipantAddress {\n readonly baseUrl: string;\n /** The crossing token this caller presents to that participant. */\n readonly crossingToken: Redacted.Redacted<string>;\n}\n\n/** The header a crossing token travels in. */\nconst CROSSING_TOKEN_HEADER = 'x-crossing-token';\n/** The header a crossing names the organization it acts for in. */\nconst ORGANIZATION_HEADER = 'x-organization-id';\n\nconst parse = (text: string): unknown => {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/** The step's path with each `:name` taken from the call's inputs. */\nconst pathOf = (call: ParticipantCall, path: string) =>\n path.replace(/:([A-Za-z0-9_]+)/g, (_match, name: string) =>\n encodeURIComponent(String(call.inputs[name] ?? '')),\n );\n\n/**\n * Calls participants over HTTP, as the slip pinned each step's call\n * ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):\n * the crossing token the participant takes, the step's `Idempotency-Key`, the\n * flow's remaining deadline, the organization the inputs name, and the inputs\n * themselves as a flat JSON body — what the participant's own route declares,\n * never a wrapper of the flow's. A refusal decodes into the failure the participant\n * sent, by its code; a body that is not a problem is `protocol`; no answer at\n * all is `unavailable`. A step with no call — the commit point that names no\n * participant — answers at once.\n */\nexport const makeHttpParticipantClient = (\n client: HttpClient.HttpClient,\n participants: Readonly<Record<string, ParticipantAddress>>,\n): ParticipantClient => ({\n call: request =>\n Effect.gen(function* () {\n const target =\n request.kind === 'action'\n ? request.step.call\n : request.step.compensation;\n if (target === undefined) return undefined;\n const name = request.step.participant ?? '';\n const address = participants[name];\n if (address === undefined)\n return yield* Effect.fail(new Internal() as EntifixFailure);\n const dependency = `${name} ${target.method} ${target.path}`;\n\n const remaining = yield* currentRemainingMs;\n if (remaining === 0)\n return yield* Effect.fail(new Timeout({ dependency }));\n\n const organizationId = request.inputs['organizationId'];\n const outgoing = HttpClientRequest.make(target.method)(\n `${address.baseUrl}${pathOf(request, target.path)}`,\n ).pipe(\n HttpClientRequest.setHeaders({\n accept: 'application/json',\n [CROSSING_TOKEN_HEADER]: Redacted.value(address.crossingToken),\n [IDEMPOTENCY_KEY_HEADER]: formatIdempotencyKey(\n request.idempotencyKey,\n ),\n ...(typeof organizationId === 'string'\n ? { [ORGANIZATION_HEADER]: organizationId }\n : {}),\n ...(remaining === undefined\n ? {}\n : { [DEADLINE_HEADER]: String(remaining) }),\n }),\n target.method === 'GET'\n ? (outgoing: HttpClientRequest.HttpClientRequest) => outgoing\n : HttpClientRequest.bodyText(\n JSON.stringify(request.inputs),\n 'application/json',\n ),\n );\n\n const response = yield* client\n .execute(outgoing)\n .pipe(Effect.mapError(() => new Unavailable({ dependency })));\n const text = yield* response.text.pipe(\n Effect.mapError(() => new Unavailable({ dependency })),\n );\n if (response.status < 400) return text === '' ? undefined : parse(text);\n const isProblem =\n response.headers['content-type']?.startsWith(PROBLEM_CONTENT_TYPE) ===\n true;\n return yield* Effect.fail(\n isProblem\n ? decodeProblem(parse(text), response.status)\n : new ProtocolFailure({\n reason: `${dependency} answered ${String(response.status)} without a problem body`,\n upstreamStatus: response.status,\n }),\n );\n }),\n});\n\n/** The HTTP participant client over the host's `HttpClient`. */\nexport const HttpParticipantClientLayer = (\n participants: Readonly<Record<string, ParticipantAddress>>,\n) =>\n Layer.effect(\n ParticipantClientTag,\n Effect.map(HttpClient.HttpClient, client =>\n makeHttpParticipantClient(client, participants),\n ),\n );\n"],"names":["currentRemainingMs","DEADLINE_HEADER","decodeProblem","formatIdempotencyKey","IDEMPOTENCY_KEY_HEADER","Internal","PROBLEM_CONTENT_TYPE","ProtocolFailure","Timeout","Unavailable","ParticipantClientTag","Effect","Layer","Redacted","HttpClient","HttpClientRequest","CROSSING_TOKEN_HEADER","ORGANIZATION_HEADER","parse","text","JSON","undefined","pathOf","call","path","replace","_match","name","encodeURIComponent","String","inputs","makeHttpParticipantClient","client","participants","request","gen","response","target","kind","step","compensation","participant","address","fail","dependency","method","remaining","organizationId","outgoing","make","baseUrl","pipe","setHeaders","accept","value","crossingToken","idempotencyKey","bodyText","stringify","execute","mapError","status","isProblem","headers","startsWith","reason","upstreamStatus","HttpParticipantClientLayer","effect","map"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,9 +4,10 @@ export * from './adapters/entity-rest/build-entity-rest-adapter-facets/index.js'
4
4
  export * from './adapters/entity-rest/build-entity-rest-adapter-get/index.js';
5
5
  export * from './adapters/entity-rest/build-entity-rest-adapter-load/index.js';
6
6
  export * from './adapters/entity-rest/build-entity-rest-adapter-save/index.js';
7
+ export * from './adapters/entity-rest/build-entity-rest-adapter-write/index.js';
7
8
  export * from './adapters/entity-rest/types.js';
8
9
  export * from './adapters/entity-use-case/index.js';
9
- export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
10
+ export * from './adapters/flow-status/build-flow-status-reader/index.js';
10
11
  export * from './clients/fetch/index.js';
11
12
  export * from './clients/types.js';
12
13
  export * from './configuration-client/index.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iEAAiE,CAAC;AAChF,cAAc,kEAAkE,CAAC;AACjF,cAAc,kEAAkE,CAAC;AACjF,cAAc,+DAA+D,CAAC;AAC9E,cAAc,gEAAgE,CAAC;AAC/E,cAAc,gEAAgE,CAAC;AAC/E,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,wEAAwE,CAAC;AACvF,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iEAAiE,CAAC;AAChF,cAAc,kEAAkE,CAAC;AACjF,cAAc,kEAAkE,CAAC;AACjF,cAAc,+DAA+D,CAAC;AAC9E,cAAc,gEAAgE,CAAC;AAC/E,cAAc,gEAAgE,CAAC;AAC/E,cAAc,iEAAiE,CAAC;AAChF,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0DAA0D,CAAC;AACzE,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC"}
package/dist/index.js CHANGED
@@ -4,9 +4,10 @@ export * from './adapters/entity-rest/build-entity-rest-adapter-facets/index.js'
4
4
  export * from './adapters/entity-rest/build-entity-rest-adapter-get/index.js';
5
5
  export * from './adapters/entity-rest/build-entity-rest-adapter-load/index.js';
6
6
  export * from './adapters/entity-rest/build-entity-rest-adapter-save/index.js';
7
+ export * from './adapters/entity-rest/build-entity-rest-adapter-write/index.js';
7
8
  export * from './adapters/entity-rest/types.js';
8
9
  export * from './adapters/entity-use-case/index.js';
9
- export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
10
+ export * from './adapters/flow-status/build-flow-status-reader/index.js';
10
11
  export * from './clients/fetch/index.js';
11
12
  export * from './clients/types.js';
12
13
  export * from './configuration-client/index.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './adapters/entity-metadata/make-entity-metadata-source/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-delete/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-facets/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-get/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-load/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-save/index.js';\nexport * from './adapters/entity-rest/types.js';\nexport * from './adapters/entity-use-case/index.js';\nexport * from './adapters/transaction-status/build-transaction-status-reader/index.js';\nexport * from './clients/fetch/index.js';\nexport * from './clients/types.js';\nexport * from './configuration-client/index.js';\n"],"names":[],"mappings":"AAAA,cAAc,kEAAkE;AAChF,cAAc,mEAAmE;AACjF,cAAc,mEAAmE;AACjF,cAAc,gEAAgE;AAC9E,cAAc,iEAAiE;AAC/E,cAAc,iEAAiE;AAC/E,cAAc,kCAAkC;AAChD,cAAc,sCAAsC;AACpD,cAAc,yEAAyE;AACvF,cAAc,2BAA2B;AACzC,cAAc,qBAAqB;AACnC,cAAc,kCAAkC"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './adapters/entity-metadata/make-entity-metadata-source/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-delete/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-facets/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-get/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-load/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-save/index.js';\nexport * from './adapters/entity-rest/build-entity-rest-adapter-write/index.js';\nexport * from './adapters/entity-rest/types.js';\nexport * from './adapters/entity-use-case/index.js';\nexport * from './adapters/flow-status/build-flow-status-reader/index.js';\nexport * from './clients/fetch/index.js';\nexport * from './clients/types.js';\nexport * from './configuration-client/index.js';\n"],"names":[],"mappings":"AAAA,cAAc,kEAAkE;AAChF,cAAc,mEAAmE;AACjF,cAAc,mEAAmE;AACjF,cAAc,gEAAgE;AAC9E,cAAc,iEAAiE;AAC/E,cAAc,iEAAiE;AAC/E,cAAc,kEAAkE;AAChF,cAAc,kCAAkC;AAChD,cAAc,sCAAsC;AACpD,cAAc,2DAA2D;AACzE,cAAc,2BAA2B;AACzC,cAAc,qBAAqB;AACnC,cAAc,kCAAkC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entifix/rest",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "HTTP entity adapter for the browser: get, load, save and delete against an entifix service, plus served metadata and transaction status.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -56,14 +56,14 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@swc/helpers": "0.5.23",
59
- "@entifix/core": "0.13.0",
60
- "@entifix/transactions": "0.13.0"
59
+ "@entifix/core": "0.15.0",
60
+ "@entifix/transactions": "0.15.0"
61
61
  },
62
62
  "devDependencies": {
63
- "effect": "4.0.6",
64
- "@entifix/testing-unit": "0.13.0"
63
+ "effect": "4.0.7",
64
+ "@entifix/testing-unit": "0.15.0"
65
65
  },
66
66
  "peerDependencies": {
67
- "effect": "^4.0.6"
67
+ "effect": "^4.0.7"
68
68
  }
69
69
  }
@@ -1,4 +0,0 @@
1
- import { type TransactionStatusReader } from '@entifix/transactions';
2
- import type { BuildEntityRestOptions } from '../../entity-rest/types.js';
3
- export declare const buildTransactionStatusReader: (options: Pick<BuildEntityRestOptions, "uriConfig" | "configuration">) => TransactionStatusReader;
4
- //# sourceMappingURL=build-transaction-status-reader.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-transaction-status-reader.d.ts","sourceRoot":"","sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,uBAAuB,CAAC;AAK/B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AAqCzE,eAAO,MAAM,4BAA4B,GACvC,SAAS,IAAI,CAAC,sBAAsB,EAAE,WAAW,GAAG,eAAe,CAAC,KACnE,uBA0BF,CAAC"}
@@ -1,47 +0,0 @@
1
- import { isTag } from '@entifix/core';
2
- import { readTransactionRecordEnvelope } from '@entifix/transactions';
3
- import { Effect } from 'effect';
4
- import { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';
5
- import { buildEntityRestAdapterMixins as adapterMixins } from '../../entity-rest/build-entity-rest-adapter-mixins/index.js';
6
- /**
7
- * The tracker's own path segment. It is not an entity — nothing decorates a
8
- * `TransactionRecord` — but the URL is composed the same way every entity URL
9
- * is, so `buildEntityBaseUrl` (already generic over an arbitrary name) needs no
10
- * transaction-shaped sibling.
11
- */ const TRANSACTION_PATH = 'transaction';
12
- /**
13
- * Reads one transaction's record over HTTP, for a browser reconciling a write it
14
- * started.
15
- *
16
- * ⚠️ **A `404` answers `undefined`, and that is not a failure.** `accepted`
17
- * reaches the tracker over the bus, so with the broker down the entity write
18
- * commits — the outbox is in the same Mongo transaction — while no event is ever
19
- * published and the tracker holds no record at all. A caller that treated this
20
- * as terminal would un-render a healthy write during a broker outage, which is
21
- * the moment nobody can tell a UI bug from an outage. The route also
22
- * answers `404` for another tenant's record, deliberately, so it cannot be
23
- * walked to probe ids that are also primary keys — indistinguishable here, and
24
- * correctly so: neither is a record this caller may reconcile against.
25
- *
26
- * Every other status stays an error, so an unreachable service is not quietly
27
- * read as "no such transaction".
28
- */ /**
29
- * A `404`, however it arrived: a tracker that answers a problem body says
30
- * `NotFound`; one that answers no body at all is a protocol failure that
31
- * still carries the status.
32
- */ const isNotFound = (error)=>isTag('NotFound')(error) || isTag('ProtocolFailure')(error) && error.upstreamStatus === 404;
33
- export const buildTransactionStatusReader = (options)=>{
34
- const { configuration } = options;
35
- return {
36
- read: (transactionId)=>Effect.gen(function*() {
37
- const url = yield* adapterMixins.buildEntityBaseUrl(configuration, options, TRANSACTION_PATH, transactionId);
38
- const response = yield* performHttpRequestThroughFetch(adapterMixins.buildEntityRequest({
39
- method: 'GET',
40
- url
41
- }));
42
- return yield* readTransactionRecordEnvelope(response.body);
43
- }).pipe(Effect.catch((error)=>isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error)))
44
- };
45
- };
46
-
47
- //# sourceMappingURL=build-transaction-status-reader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.ts"],"sourcesContent":["import { isTag, type TransitionalFailure } from '@entifix/core';\nimport {\n readTransactionRecordEnvelope,\n type TransactionRecord,\n type TransactionStatusReader,\n} from '@entifix/transactions';\nimport { Effect } from 'effect';\n\nimport { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';\nimport { buildEntityRestAdapterMixins as adapterMixins } from '../../entity-rest/build-entity-rest-adapter-mixins/index.js';\nimport type { BuildEntityRestOptions } from '../../entity-rest/types.js';\n\n/**\n * The tracker's own path segment. It is not an entity — nothing decorates a\n * `TransactionRecord` — but the URL is composed the same way every entity URL\n * is, so `buildEntityBaseUrl` (already generic over an arbitrary name) needs no\n * transaction-shaped sibling.\n */\nconst TRANSACTION_PATH = 'transaction';\n\n/**\n * Reads one transaction's record over HTTP, for a browser reconciling a write it\n * started.\n *\n * ⚠️ **A `404` answers `undefined`, and that is not a failure.** `accepted`\n * reaches the tracker over the bus, so with the broker down the entity write\n * commits — the outbox is in the same Mongo transaction — while no event is ever\n * published and the tracker holds no record at all. A caller that treated this\n * as terminal would un-render a healthy write during a broker outage, which is\n * the moment nobody can tell a UI bug from an outage. The route also\n * answers `404` for another tenant's record, deliberately, so it cannot be\n * walked to probe ids that are also primary keys — indistinguishable here, and\n * correctly so: neither is a record this caller may reconcile against.\n *\n * Every other status stays an error, so an unreachable service is not quietly\n * read as \"no such transaction\".\n */\n/**\n * A `404`, however it arrived: a tracker that answers a problem body says\n * `NotFound`; one that answers no body at all is a protocol failure that\n * still carries the status.\n */\nconst isNotFound = (error: unknown): boolean =>\n isTag('NotFound')(error) ||\n (isTag('ProtocolFailure')(error) &&\n (error as { readonly upstreamStatus?: unknown }).upstreamStatus === 404);\n\nexport const buildTransactionStatusReader = (\n options: Pick<BuildEntityRestOptions, 'uriConfig' | 'configuration'>,\n): TransactionStatusReader => {\n const { configuration } = options;\n\n return {\n read: (\n transactionId: string,\n ): Effect.Effect<TransactionRecord | undefined, TransitionalFailure> =>\n Effect.gen(function* () {\n const url = yield* adapterMixins.buildEntityBaseUrl(\n configuration,\n options,\n TRANSACTION_PATH,\n transactionId,\n );\n\n const response = yield* performHttpRequestThroughFetch(\n adapterMixins.buildEntityRequest({ method: 'GET', url }),\n );\n\n return yield* readTransactionRecordEnvelope(response.body);\n }).pipe(\n Effect.catch(error =>\n isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error),\n ),\n ),\n };\n};\n"],"names":["isTag","readTransactionRecordEnvelope","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","TRANSACTION_PATH","isNotFound","error","upstreamStatus","buildTransactionStatusReader","options","configuration","read","transactionId","gen","url","buildEntityBaseUrl","response","buildEntityRequest","method","body","pipe","catch","succeed","undefined","fail"],"mappings":"AAAA,SAASA,KAAK,QAAkC,gBAAgB;AAChE,SACEC,6BAA6B,QAGxB,wBAAwB;AAC/B,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,8BAA8B,QAAQ,kCAAkC;AACjF,SAASC,gCAAgCC,aAAa,QAAQ,8DAA8D;AAG5H;;;;;CAKC,GACD,MAAMC,mBAAmB;AAEzB;;;;;;;;;;;;;;;;CAgBC,GACD;;;;CAIC,GACD,MAAMC,aAAa,CAACC,QAClBR,MAAM,YAAYQ,UACjBR,MAAM,mBAAmBQ,UACxB,AAACA,MAAgDC,cAAc,KAAK;AAExE,OAAO,MAAMC,+BAA+B,CAC1CC;IAEA,MAAM,EAAEC,aAAa,EAAE,GAAGD;IAE1B,OAAO;QACLE,MAAM,CACJC,gBAEAZ,OAAOa,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAOX,cAAcY,kBAAkB,CACjDL,eACAD,SACAL,kBACAQ;gBAGF,MAAMI,WAAW,OAAOf,+BACtBE,cAAcc,kBAAkB,CAAC;oBAAEC,QAAQ;oBAAOJ;gBAAI;gBAGxD,OAAO,OAAOf,8BAA8BiB,SAASG,IAAI;YAC3D,GAAGC,IAAI,CACLpB,OAAOqB,KAAK,CAACf,CAAAA,QACXD,WAAWC,SAASN,OAAOsB,OAAO,CAACC,aAAavB,OAAOwB,IAAI,CAAClB;IAGpE;AACF,EAAE"}
@@ -1,2 +0,0 @@
1
- export * from './build-transaction-status-reader.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/index.ts"],"names":[],"mappings":"AAAA,cAAc,sCAAsC,CAAC"}
@@ -1,3 +0,0 @@
1
- export * from './build-transaction-status-reader.js';
2
-
3
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/index.ts"],"sourcesContent":["export * from './build-transaction-status-reader.js';\n"],"names":[],"mappings":"AAAA,cAAc,uCAAuC"}