@entifix/rest 0.14.0 → 0.16.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 (24) hide show
  1. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.d.ts +3 -7
  2. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.d.ts.map +1 -1
  3. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.js +5 -72
  4. package/dist/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.js.map +1 -1
  5. package/dist/adapters/entity-rest/types.d.ts +0 -15
  6. package/dist/adapters/entity-rest/types.d.ts.map +1 -1
  7. package/dist/adapters/entity-rest/types.js.map +1 -1
  8. package/dist/endpoint/participant-client.d.ts +3 -2
  9. package/dist/endpoint/participant-client.d.ts.map +1 -1
  10. package/dist/endpoint/participant-client.js +10 -16
  11. package/dist/endpoint/participant-client.js.map +1 -1
  12. package/dist/index.d.ts +0 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +0 -1
  15. package/dist/index.js.map +1 -1
  16. package/package.json +4 -4
  17. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.d.ts +0 -21
  18. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.d.ts.map +0 -1
  19. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.js +0 -42
  20. package/dist/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.js.map +0 -1
  21. package/dist/adapters/transaction-status/build-transaction-status-reader/index.d.ts +0 -2
  22. package/dist/adapters/transaction-status/build-transaction-status-reader/index.d.ts.map +0 -1
  23. package/dist/adapters/transaction-status/build-transaction-status-reader/index.js +0 -3
  24. package/dist/adapters/transaction-status/build-transaction-status-reader/index.js.map +0 -1
@@ -12,13 +12,9 @@ import { BuildEntityRestOptions } from '../types.js';
12
12
  * deserialized and returned, because the service is the authority on the stored
13
13
  * entity (it assigns ids and may normalize fields).
14
14
  *
15
- * A **transactional** create inverts that, and deliberately. The service answers
16
- * `202` describing a transaction rather than an entity, so there is no stored
17
- * entity to read back yet: the client mints the transaction id, which *is* the
18
- * id the entity will be stored under, and returns the entity it already holds.
19
- * That is the point of the client owning the id — before it, this
20
- * adapter parsed the `202` as an entity envelope, failed, and reported an error
21
- * for a create that was in fact about to succeed.
15
+ * A write the service hands to a flow answers `202`; a repository save has no
16
+ * stored entity to return for it, so such an entity is written through
17
+ * `buildEntityRestAdapterWrite`, which answers the flow to follow.
22
18
  */
23
19
  export declare const buildEntityRestAdapterSave: <TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, options: BuildEntityRestOptions) => <TInput extends Entity>(entity: TInput) => Effect.Effect<TInput, import("@entifix/core").EntifixFailure<string, string, import("@entifix/core").FailureCategory>, never>;
24
20
  //# sourceMappingURL=build-entity-rest-adapter-save.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build-entity-rest-adapter-save.d.ts","sourceRoot":"","sources":["../../../../src/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EAIlB,MAAM,eAAe,CAAC;AAOvB,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAMxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAKrD;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,0BAA0B,GACpC,OAAO,SAAS,MAAM,EACrB,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,SAAS,sBAAsB,MAKhC,MAAM,SAAS,MAAM,EAAE,QAAQ,MAAM,kIAwFlC,CAAC"}
1
+ {"version":3,"file":"build-entity-rest-adapter-save.d.ts","sourceRoot":"","sources":["../../../../src/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,iBAAiB,EAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhC,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,0BAA0B,GACpC,OAAO,SAAS,MAAM,EACrB,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,SAAS,sBAAsB,MAKhC,MAAM,SAAS,MAAM,EAAE,QAAQ,MAAM,kIAgBlC,CAAC"}
@@ -1,11 +1,6 @@
1
- import { extractMetaEntity, ProtocolFailure, serializeEntity } from '@entifix/core';
2
- import { makeCommandEnvelope, readTransactionAcceptedEnvelope, TransactionSinkTag } from '@entifix/transactions';
3
- import { Effect, Option } from 'effect';
4
- import { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';
5
- import { buildEntityRestAdapterMixins as adapterMixins } from '../build-entity-rest-adapter-mixins/index.js';
1
+ import { extractMetaEntity, ProtocolFailure } from '@entifix/core';
2
+ import { Effect } from 'effect';
6
3
  import { buildEntityRestAdapterWrite } from '../build-entity-rest-adapter-write/index.js';
7
- import { pendingSaveKeys } from '../pending-save-keys.js';
8
- /** A transactional create whose `202` never arrived: same command, same id. */ const pendingCreates = new WeakSet();
9
4
  /**
10
5
  * Persists an entity over HTTP.
11
6
  *
@@ -17,78 +12,16 @@ import { pendingSaveKeys } from '../pending-save-keys.js';
17
12
  * deserialized and returned, because the service is the authority on the stored
18
13
  * entity (it assigns ids and may normalize fields).
19
14
  *
20
- * A **transactional** create inverts that, and deliberately. The service answers
21
- * `202` describing a transaction rather than an entity, so there is no stored
22
- * entity to read back yet: the client mints the transaction id, which *is* the
23
- * id the entity will be stored under, and returns the entity it already holds.
24
- * That is the point of the client owning the id — before it, this
25
- * adapter parsed the `202` as an entity envelope, failed, and reported an error
26
- * for a create that was in fact about to succeed.
15
+ * A write the service hands to a flow answers `202`; a repository save has no
16
+ * stored entity to return for it, so such an entity is written through
17
+ * `buildEntityRestAdapterWrite`, which answers the flow to follow.
27
18
  */ export const buildEntityRestAdapterSave = (entityConstructor, options)=>// Deliberately not shadowing `TEntity` here (as the read builders do): the
28
19
  // envelope has to be built against the *constructor's* entity type, which a
29
20
  // shadowed parameter would make unreachable.
30
21
  (entity)=>Effect.gen(function*() {
31
22
  var _metaEntity_key;
32
- const { uriConfig, create = 'entity', configuration } = options;
33
23
  const metaEntity = extractMetaEntity(entityConstructor);
34
24
  const key = (_metaEntity_key = metaEntity.key) != null ? _metaEntity_key : metaEntity.name;
35
- const isCreate = entity.id == null || pendingCreates.has(entity);
36
- if (isCreate && create === 'command') {
37
- var _pendingSaveKeys_get;
38
- const url = yield* adapterMixins.buildEntityBaseUrl(configuration, {
39
- uriConfig
40
- }, key);
41
- // `crypto.randomUUID` is available in every browser this ships to and in
42
- // Node — and the id must be a UUID, because the service constrains the
43
- // key space a caller may address.
44
- // A retried create reuses the id it was first sent with: that id *is*
45
- // the idempotency key, so a new one would write a second record.
46
- const transactionId = (_pendingSaveKeys_get = pendingSaveKeys.get(entity)) != null ? _pendingSaveKeys_get : crypto.randomUUID();
47
- pendingSaveKeys.set(entity, transactionId);
48
- pendingCreates.add(entity);
49
- entity.id = transactionId;
50
- const command = {
51
- transactionId,
52
- type: 'create',
53
- entity: key,
54
- payload: serializeEntity(entityConstructor, entity)
55
- };
56
- const accepted = yield* performHttpRequestThroughFetch(adapterMixins.buildEntityRequest({
57
- method: 'POST',
58
- url,
59
- envelope: makeCommandEnvelope(command),
60
- idempotencyKey: transactionId
61
- }));
62
- // Read for its shape, not its contents: this asserts the service really
63
- // accepted a transaction rather than answering something else with a
64
- // 2xx, which is the failure the old code path could not distinguish.
65
- yield* readTransactionAcceptedEnvelope(accepted.body);
66
- pendingSaveKeys.delete(entity);
67
- pendingCreates.delete(entity);
68
- // Tell whoever is watching that a write is in flight. This is the only
69
- // point in the system that knows it — the entity returned below is
70
- // indistinguishable from a plain REST create, so without this the
71
- // browser navigates away at the `202` and a later failure leaves no row,
72
- // no message and nothing to retry from.
73
- //
74
- // `serviceOption`, deliberately: reading the tag this way keeps it out
75
- // of this function's `R`, so the storefront, the plain REST adapters and
76
- // every existing spec compile and run with no layer to provide. The cost
77
- // is that nothing *forces* a composition root to provide it, which is
78
- // why `mergeContext` has a spec pinning that it does.
79
- const sink = yield* Effect.serviceOption(TransactionSinkTag);
80
- if (Option.isSome(sink)) {
81
- // After the assertion above, never before: a `2xx` that is not a
82
- // transaction envelope must not register a pending id, because nothing
83
- // would ever settle it and the entry becomes a permanent phantom.
84
- sink.value.began({
85
- transactionId,
86
- entity: key,
87
- at: new Date().toISOString()
88
- });
89
- }
90
- return entity;
91
- }
92
25
  const result = yield* buildEntityRestAdapterWrite(entityConstructor, options).save(entity);
93
26
  // A repository save answers the stored entity, so it has nothing to
94
27
  // return for a write the service handed to a flow; a screen writes such
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.ts"],"sourcesContent":["import {\n Entity,\n EntityConstructor,\n extractMetaEntity,\n ProtocolFailure,\n serializeEntity,\n} from '@entifix/core';\nimport {\n makeCommandEnvelope,\n readTransactionAcceptedEnvelope,\n type TransactionCommand,\n TransactionSinkTag,\n} from '@entifix/transactions';\nimport { Effect, Option } from 'effect';\n\nimport { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';\nimport { buildEntityRestAdapterMixins as adapterMixins } from '../build-entity-rest-adapter-mixins/index.js';\nimport { buildEntityRestAdapterWrite } from '../build-entity-rest-adapter-write/index.js';\nimport { pendingSaveKeys } from '../pending-save-keys.js';\nimport { BuildEntityRestOptions } from '../types.js';\n\n/** A transactional create whose `202` never arrived: same command, same id. */\nconst pendingCreates = new WeakSet<object>();\n\n/**\n * Persists an entity over HTTP.\n *\n * An entity without an id has never been stored, so it is created with `POST`\n * against the collection; one with an id is replaced with `PUT` against its own\n * URL.\n *\n * For a plain create the response envelope — not the request — is what gets\n * deserialized and returned, because the service is the authority on the stored\n * entity (it assigns ids and may normalize fields).\n *\n * A **transactional** create inverts that, and deliberately. The service answers\n * `202` describing a transaction rather than an entity, so there is no stored\n * entity to read back yet: the client mints the transaction id, which *is* the\n * id the entity will be stored under, and returns the entity it already holds.\n * That is the point of the client owning the id — before it, this\n * adapter parsed the `202` as an entity envelope, failed, and reported an error\n * for a create that was in fact about to succeed.\n */\nexport const buildEntityRestAdapterSave =\n <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n options: BuildEntityRestOptions,\n ) =>\n // Deliberately not shadowing `TEntity` here (as the read builders do): the\n // envelope has to be built against the *constructor's* entity type, which a\n // shadowed parameter would make unreachable.\n <TInput extends Entity>(entity: TInput) =>\n Effect.gen(function* () {\n const { uriConfig, create = 'entity', configuration } = options;\n const metaEntity = extractMetaEntity(entityConstructor);\n const key = metaEntity.key ?? metaEntity.name;\n const isCreate = entity.id == null || pendingCreates.has(entity);\n\n if (isCreate && create === 'command') {\n const url = yield* adapterMixins.buildEntityBaseUrl(\n configuration,\n { uriConfig },\n key,\n );\n\n // `crypto.randomUUID` is available in every browser this ships to and in\n // Node — and the id must be a UUID, because the service constrains the\n // key space a caller may address.\n // A retried create reuses the id it was first sent with: that id *is*\n // the idempotency key, so a new one would write a second record.\n const transactionId =\n pendingSaveKeys.get(entity) ?? crypto.randomUUID();\n pendingSaveKeys.set(entity, transactionId);\n pendingCreates.add(entity);\n entity.id = transactionId;\n\n const command: TransactionCommand = {\n transactionId,\n type: 'create',\n entity: key,\n payload: serializeEntity(\n entityConstructor,\n entity as unknown as TEntity,\n ),\n };\n\n const accepted = yield* performHttpRequestThroughFetch(\n adapterMixins.buildEntityRequest({\n method: 'POST',\n url,\n envelope: makeCommandEnvelope(command),\n idempotencyKey: transactionId,\n }),\n );\n // Read for its shape, not its contents: this asserts the service really\n // accepted a transaction rather than answering something else with a\n // 2xx, which is the failure the old code path could not distinguish.\n yield* readTransactionAcceptedEnvelope(accepted.body);\n pendingSaveKeys.delete(entity);\n pendingCreates.delete(entity);\n\n // Tell whoever is watching that a write is in flight. This is the only\n // point in the system that knows it — the entity returned below is\n // indistinguishable from a plain REST create, so without this the\n // browser navigates away at the `202` and a later failure leaves no row,\n // no message and nothing to retry from.\n //\n // `serviceOption`, deliberately: reading the tag this way keeps it out\n // of this function's `R`, so the storefront, the plain REST adapters and\n // every existing spec compile and run with no layer to provide. The cost\n // is that nothing *forces* a composition root to provide it, which is\n // why `mergeContext` has a spec pinning that it does.\n const sink = yield* Effect.serviceOption(TransactionSinkTag);\n if (Option.isSome(sink)) {\n // After the assertion above, never before: a `2xx` that is not a\n // transaction envelope must not register a pending id, because nothing\n // would ever settle it and the entry becomes a permanent phantom.\n sink.value.began({\n transactionId,\n entity: key,\n at: new Date().toISOString(),\n });\n }\n\n return entity;\n }\n\n const result = yield* buildEntityRestAdapterWrite(\n entityConstructor,\n options,\n ).save(entity);\n // A repository save answers the stored entity, so it has nothing to\n // return for a write the service handed to a flow; a screen writes such\n // an entity through `buildEntityRestAdapterWrite` instead.\n if (result._tag === 'pending')\n return yield* new ProtocolFailure({\n reason: `'${key}' answered a save with 202 flowAccepted; write it through buildEntityRestAdapterWrite`,\n });\n return result.value;\n });\n"],"names":["extractMetaEntity","ProtocolFailure","serializeEntity","makeCommandEnvelope","readTransactionAcceptedEnvelope","TransactionSinkTag","Effect","Option","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","buildEntityRestAdapterWrite","pendingSaveKeys","pendingCreates","WeakSet","buildEntityRestAdapterSave","entityConstructor","options","entity","gen","metaEntity","uriConfig","create","configuration","key","name","isCreate","id","has","url","buildEntityBaseUrl","transactionId","get","crypto","randomUUID","set","add","command","type","payload","accepted","buildEntityRequest","method","envelope","idempotencyKey","body","delete","sink","serviceOption","isSome","value","began","at","Date","toISOString","result","save","_tag","reason"],"mappings":"AAAA,SAGEA,iBAAiB,EACjBC,eAAe,EACfC,eAAe,QACV,gBAAgB;AACvB,SACEC,mBAAmB,EACnBC,+BAA+B,EAE/BC,kBAAkB,QACb,wBAAwB;AAC/B,SAASC,MAAM,EAAEC,MAAM,QAAQ,SAAS;AAExC,SAASC,8BAA8B,QAAQ,kCAAkC;AACjF,SAASC,gCAAgCC,aAAa,QAAQ,+CAA+C;AAC7G,SAASC,2BAA2B,QAAQ,8CAA8C;AAC1F,SAASC,eAAe,QAAQ,0BAA0B;AAG1D,6EAA6E,GAC7E,MAAMC,iBAAiB,IAAIC;AAE3B;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,MAAMC,6BACX,CACEC,mBACAC,UAEF,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,CAAwBC,SACtBZ,OAAOa,GAAG,CAAC;gBAGGC;YAFZ,MAAM,EAAEC,SAAS,EAAEC,SAAS,QAAQ,EAAEC,aAAa,EAAE,GAAGN;YACxD,MAAMG,aAAapB,kBAAkBgB;YACrC,MAAMQ,OAAMJ,kBAAAA,WAAWI,GAAG,YAAdJ,kBAAkBA,WAAWK,IAAI;YAC7C,MAAMC,WAAWR,OAAOS,EAAE,IAAI,QAAQd,eAAee,GAAG,CAACV;YAEzD,IAAIQ,YAAYJ,WAAW,WAAW;oBAalCV;gBAZF,MAAMiB,MAAM,OAAOnB,cAAcoB,kBAAkB,CACjDP,eACA;oBAAEF;gBAAU,GACZG;gBAGF,yEAAyE;gBACzE,uEAAuE;gBACvE,kCAAkC;gBAClC,sEAAsE;gBACtE,iEAAiE;gBACjE,MAAMO,iBACJnB,uBAAAA,gBAAgBoB,GAAG,CAACd,mBAApBN,uBAA+BqB,OAAOC,UAAU;gBAClDtB,gBAAgBuB,GAAG,CAACjB,QAAQa;gBAC5BlB,eAAeuB,GAAG,CAAClB;gBACnBA,OAAOS,EAAE,GAAGI;gBAEZ,MAAMM,UAA8B;oBAClCN;oBACAO,MAAM;oBACNpB,QAAQM;oBACRe,SAASrC,gBACPc,mBACAE;gBAEJ;gBAEA,MAAMsB,WAAW,OAAOhC,+BACtBE,cAAc+B,kBAAkB,CAAC;oBAC/BC,QAAQ;oBACRb;oBACAc,UAAUxC,oBAAoBkC;oBAC9BO,gBAAgBb;gBAClB;gBAEF,wEAAwE;gBACxE,qEAAqE;gBACrE,qEAAqE;gBACrE,OAAO3B,gCAAgCoC,SAASK,IAAI;gBACpDjC,gBAAgBkC,MAAM,CAAC5B;gBACvBL,eAAeiC,MAAM,CAAC5B;gBAEtB,uEAAuE;gBACvE,mEAAmE;gBACnE,kEAAkE;gBAClE,yEAAyE;gBACzE,wCAAwC;gBACxC,EAAE;gBACF,uEAAuE;gBACvE,yEAAyE;gBACzE,yEAAyE;gBACzE,sEAAsE;gBACtE,sDAAsD;gBACtD,MAAM6B,OAAO,OAAOzC,OAAO0C,aAAa,CAAC3C;gBACzC,IAAIE,OAAO0C,MAAM,CAACF,OAAO;oBACvB,iEAAiE;oBACjE,uEAAuE;oBACvE,kEAAkE;oBAClEA,KAAKG,KAAK,CAACC,KAAK,CAAC;wBACfpB;wBACAb,QAAQM;wBACR4B,IAAI,IAAIC,OAAOC,WAAW;oBAC5B;gBACF;gBAEA,OAAOpC;YACT;YAEA,MAAMqC,SAAS,OAAO5C,4BACpBK,mBACAC,SACAuC,IAAI,CAACtC;YACP,oEAAoE;YACpE,wEAAwE;YACxE,2DAA2D;YAC3D,IAAIqC,OAAOE,IAAI,KAAK,WAClB,OAAO,OAAO,IAAIxD,gBAAgB;gBAChCyD,QAAQ,CAAC,CAAC,EAAElC,IAAI,qFAAqF,CAAC;YACxG;YACF,OAAO+B,OAAOL,KAAK;QACrB,GAAG"}
1
+ {"version":3,"sources":["../../../../src/adapters/entity-rest/build-entity-rest-adapter-save/build-entity-rest-adapter-save.ts"],"sourcesContent":["import {\n Entity,\n EntityConstructor,\n extractMetaEntity,\n ProtocolFailure,\n} from '@entifix/core';\nimport { Effect } from 'effect';\n\nimport { buildEntityRestAdapterWrite } from '../build-entity-rest-adapter-write/index.js';\nimport { BuildEntityRestOptions } from '../types.js';\n\n/**\n * Persists an entity over HTTP.\n *\n * An entity without an id has never been stored, so it is created with `POST`\n * against the collection; one with an id is replaced with `PUT` against its own\n * URL.\n *\n * For a plain create the response envelope — not the request — is what gets\n * deserialized and returned, because the service is the authority on the stored\n * entity (it assigns ids and may normalize fields).\n *\n * A write the service hands to a flow answers `202`; a repository save has no\n * stored entity to return for it, so such an entity is written through\n * `buildEntityRestAdapterWrite`, which answers the flow to follow.\n */\nexport const buildEntityRestAdapterSave =\n <TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n options: BuildEntityRestOptions,\n ) =>\n // Deliberately not shadowing `TEntity` here (as the read builders do): the\n // envelope has to be built against the *constructor's* entity type, which a\n // shadowed parameter would make unreachable.\n <TInput extends Entity>(entity: TInput) =>\n Effect.gen(function* () {\n const metaEntity = extractMetaEntity(entityConstructor);\n const key = metaEntity.key ?? metaEntity.name;\n const result = yield* buildEntityRestAdapterWrite(\n entityConstructor,\n options,\n ).save(entity);\n // A repository save answers the stored entity, so it has nothing to\n // return for a write the service handed to a flow; a screen writes such\n // an entity through `buildEntityRestAdapterWrite` instead.\n if (result._tag === 'pending')\n return yield* new ProtocolFailure({\n reason: `'${key}' answered a save with 202 flowAccepted; write it through buildEntityRestAdapterWrite`,\n });\n return result.value;\n });\n"],"names":["extractMetaEntity","ProtocolFailure","Effect","buildEntityRestAdapterWrite","buildEntityRestAdapterSave","entityConstructor","options","entity","gen","metaEntity","key","name","result","save","_tag","reason","value"],"mappings":"AAAA,SAGEA,iBAAiB,EACjBC,eAAe,QACV,gBAAgB;AACvB,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,2BAA2B,QAAQ,8CAA8C;AAG1F;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,6BACX,CACEC,mBACAC,UAEF,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,CAAwBC,SACtBL,OAAOM,GAAG,CAAC;gBAEGC;YADZ,MAAMA,aAAaT,kBAAkBK;YACrC,MAAMK,OAAMD,kBAAAA,WAAWC,GAAG,YAAdD,kBAAkBA,WAAWE,IAAI;YAC7C,MAAMC,SAAS,OAAOT,4BACpBE,mBACAC,SACAO,IAAI,CAACN;YACP,oEAAoE;YACpE,wEAAwE;YACxE,2DAA2D;YAC3D,IAAIK,OAAOE,IAAI,KAAK,WAClB,OAAO,OAAO,IAAIb,gBAAgB;gBAChCc,QAAQ,CAAC,CAAC,EAAEL,IAAI,qFAAqF,CAAC;YACxG;YACF,OAAOE,OAAOI,KAAK;QACrB,GAAG"}
@@ -1,25 +1,10 @@
1
1
  import type { ConfigurationClient, ConfigurationExtractMode } from '@entifix/core';
2
- /**
3
- * How a create is sent to this entity's service.
4
- *
5
- * `entity` is the plain REST write: `POST` an entity envelope, get the stored
6
- * entity back. `command` is the CQRS write path — the client mints the
7
- * transaction id, `POST`s a `command` envelope and gets a `202` naming the
8
- * transaction, because the write completes asynchronously behind a saga.
9
- *
10
- * It is per entity rather than global because the two live side by side today:
11
- * `ProductSpecification` is transactional, while `ProductBrand` and
12
- * `ProductCategory` are plain writes on another service.
13
- */
14
- export type EntityCreateProtocol = 'entity' | 'command';
15
2
  export interface BuildEntityRestOptions {
16
3
  uriConfig: {
17
4
  key: string;
18
5
  group?: string;
19
6
  extractionMode?: ConfigurationExtractMode;
20
7
  };
21
- /** Defaults to `'entity'` — the plain REST write. */
22
- create?: EntityCreateProtocol;
23
8
  /**
24
9
  * The method an update is sent with: `PUT` replaces the stored entity,
25
10
  * `PATCH` merges into it. Defaults to `'PUT'`.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/entity-rest/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,wBAAwB,CAAC;KAC3C,CAAC;IACF,qDAAqD;IACrD,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,EAAE,mBAAmB,CAAC;CACpC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/entity-rest/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,sBAAsB;IACrC,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,cAAc,CAAC,EAAE,wBAAwB,CAAC;KAC3C,CAAC;IACF;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,EAAE,mBAAmB,CAAC;CACpC"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/adapters/entity-rest/types.ts"],"sourcesContent":["import type {\n ConfigurationClient,\n ConfigurationExtractMode,\n} from '@entifix/core';\n\n/**\n * How a create is sent to this entity's service.\n *\n * `entity` is the plain REST write: `POST` an entity envelope, get the stored\n * entity back. `command` is the CQRS write path — the client mints the\n * transaction id, `POST`s a `command` envelope and gets a `202` naming the\n * transaction, because the write completes asynchronously behind a saga.\n *\n * It is per entity rather than global because the two live side by side today:\n * `ProductSpecification` is transactional, while `ProductBrand` and\n * `ProductCategory` are plain writes on another service.\n */\nexport type EntityCreateProtocol = 'entity' | 'command';\n\nexport interface BuildEntityRestOptions {\n uriConfig: {\n key: string;\n group?: string;\n extractionMode?: ConfigurationExtractMode;\n };\n /** Defaults to `'entity'` — the plain REST write. */\n create?: EntityCreateProtocol;\n /**\n * The method an update is sent with: `PUT` replaces the stored entity,\n * `PATCH` merges into it. Defaults to `'PUT'`.\n */\n update?: 'PUT' | 'PATCH';\n /**\n * Where `uriConfig` is looked up, bound when the adapter is built.\n *\n * A value rather than a requirement on each call: the URL lookup is this\n * adapter's mechanism, and putting it in the repository port's environment\n * made every use case — and every static or database-backed caller — provide\n * a configuration it never read.\n */\n configuration: ConfigurationClient;\n}\n"],"names":[],"mappings":"AAmBA,WAsBC"}
1
+ {"version":3,"sources":["../../../src/adapters/entity-rest/types.ts"],"sourcesContent":["import type {\n ConfigurationClient,\n ConfigurationExtractMode,\n} from '@entifix/core';\n\nexport interface BuildEntityRestOptions {\n uriConfig: {\n key: string;\n group?: string;\n extractionMode?: ConfigurationExtractMode;\n };\n /**\n * The method an update is sent with: `PUT` replaces the stored entity,\n * `PATCH` merges into it. Defaults to `'PUT'`.\n */\n update?: 'PUT' | 'PATCH';\n /**\n * Where `uriConfig` is looked up, bound when the adapter is built.\n *\n * A value rather than a requirement on each call: the URL lookup is this\n * adapter's mechanism, and putting it in the repository port's environment\n * made every use case — and every static or database-backed caller — provide\n * a configuration it never read.\n */\n configuration: ConfigurationClient;\n}\n"],"names":[],"mappings":"AAKA,WAoBC"}
@@ -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
@@ -8,7 +8,6 @@ export * from './adapters/entity-rest/build-entity-rest-adapter-write/index.js';
8
8
  export * from './adapters/entity-rest/types.js';
9
9
  export * from './adapters/entity-use-case/index.js';
10
10
  export * from './adapters/flow-status/build-flow-status-reader/index.js';
11
- export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
12
11
  export * from './clients/fetch/index.js';
13
12
  export * from './clients/types.js';
14
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,iEAAiE,CAAC;AAChF,cAAc,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0DAA0D,CAAC;AACzE,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
@@ -8,7 +8,6 @@ export * from './adapters/entity-rest/build-entity-rest-adapter-write/index.js';
8
8
  export * from './adapters/entity-rest/types.js';
9
9
  export * from './adapters/entity-use-case/index.js';
10
10
  export * from './adapters/flow-status/build-flow-status-reader/index.js';
11
- export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
12
11
  export * from './clients/fetch/index.js';
13
12
  export * from './clients/types.js';
14
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/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 './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,kEAAkE;AAChF,cAAc,kCAAkC;AAChD,cAAc,sCAAsC;AACpD,cAAc,2DAA2D;AACzE,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.14.0",
3
+ "version": "0.16.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,12 +56,12 @@
56
56
  },
57
57
  "dependencies": {
58
58
  "@swc/helpers": "0.5.23",
59
- "@entifix/core": "0.14.0",
60
- "@entifix/transactions": "0.14.0"
59
+ "@entifix/transactions": "0.16.0",
60
+ "@entifix/core": "0.16.0"
61
61
  },
62
62
  "devDependencies": {
63
63
  "effect": "4.0.7",
64
- "@entifix/testing-unit": "0.14.0"
64
+ "@entifix/testing-unit": "0.16.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "effect": "^4.0.7"
@@ -1,21 +0,0 @@
1
- import { type TransactionStatusReader } from '@entifix/transactions';
2
- import type { BuildEntityRestOptions } from '../../entity-rest/types.js';
3
- /**
4
- * Reads one transaction's record over HTTP, for a browser reconciling a write it
5
- * started.
6
- *
7
- * ⚠️ **A `404` answers `undefined`, and that is not a failure.** `accepted`
8
- * reaches the tracker over the bus, so with the broker down the entity write
9
- * commits — the outbox is in the same Mongo transaction — while no event is ever
10
- * published and the tracker holds no record at all. A caller that treated this
11
- * as terminal would un-render a healthy write during a broker outage, which is
12
- * the moment nobody can tell a UI bug from an outage. The route also
13
- * answers `404` for another tenant's record, deliberately, so it cannot be
14
- * walked to probe ids that are also primary keys — indistinguishable here, and
15
- * correctly so: neither is a record this caller may reconcile against.
16
- *
17
- * Every other status stays an error, so an unreachable service is not quietly
18
- * read as "no such transaction".
19
- */
20
- export declare const buildTransactionStatusReader: (options: Pick<BuildEntityRestOptions, "uriConfig" | "configuration">) => TransactionStatusReader;
21
- //# 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;AAWzE;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,4BAA4B,GACvC,SAAS,IAAI,CAAC,sBAAsB,EAAE,WAAW,GAAG,eAAe,CAAC,KACnE,uBA0BF,CAAC"}
@@ -1,42 +0,0 @@
1
- import { readTransactionRecordEnvelope } from '@entifix/transactions';
2
- import { Effect } from 'effect';
3
- import { performHttpRequestThroughFetch } from '../../../clients/fetch/index.js';
4
- import { buildEntityRestAdapterMixins as adapterMixins } from '../../entity-rest/build-entity-rest-adapter-mixins/index.js';
5
- import { isNotFound } from '../../is-not-found.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
- */ export const buildTransactionStatusReader = (options)=>{
29
- const { configuration } = options;
30
- return {
31
- read: (transactionId)=>Effect.gen(function*() {
32
- const url = yield* adapterMixins.buildEntityBaseUrl(configuration, options, TRANSACTION_PATH, transactionId);
33
- const response = yield* performHttpRequestThroughFetch(adapterMixins.buildEntityRequest({
34
- method: 'GET',
35
- url
36
- }));
37
- return yield* readTransactionRecordEnvelope(response.body);
38
- }).pipe(Effect.catch((error)=>isNotFound(error) ? Effect.succeed(undefined) : Effect.fail(error)))
39
- };
40
- };
41
-
42
- //# 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 type { EntifixFailure } 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';\nimport { isNotFound } from '../../is-not-found.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\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, EntifixFailure> =>\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":["readTransactionRecordEnvelope","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","isNotFound","TRANSACTION_PATH","buildTransactionStatusReader","options","configuration","read","transactionId","gen","url","buildEntityBaseUrl","response","buildEntityRequest","method","body","pipe","catch","error","succeed","undefined","fail"],"mappings":"AACA,SACEA,6BAA6B,QAGxB,wBAAwB;AAC/B,SAASC,MAAM,QAAQ,SAAS;AAEhC,SAASC,8BAA8B,QAAQ,kCAAkC;AACjF,SAASC,gCAAgCC,aAAa,QAAQ,8DAA8D;AAE5H,SAASC,UAAU,QAAQ,wBAAwB;AAEnD;;;;;CAKC,GACD,MAAMC,mBAAmB;AAEzB;;;;;;;;;;;;;;;;CAgBC,GAED,OAAO,MAAMC,+BAA+B,CAC1CC;IAEA,MAAM,EAAEC,aAAa,EAAE,GAAGD;IAE1B,OAAO;QACLE,MAAM,CACJC,gBAEAV,OAAOW,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAOT,cAAcU,kBAAkB,CACjDL,eACAD,SACAF,kBACAK;gBAGF,MAAMI,WAAW,OAAOb,+BACtBE,cAAcY,kBAAkB,CAAC;oBAAEC,QAAQ;oBAAOJ;gBAAI;gBAGxD,OAAO,OAAOb,8BAA8Be,SAASG,IAAI;YAC3D,GAAGC,IAAI,CACLlB,OAAOmB,KAAK,CAACC,CAAAA,QACXhB,WAAWgB,SAASpB,OAAOqB,OAAO,CAACC,aAAatB,OAAOuB,IAAI,CAACH;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"}