@entifix/rest 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,7 @@ import { BuildEntityRestOptions } from '../types.js';
16
16
  * `202` describing a transaction rather than an entity, so there is no stored
17
17
  * entity to read back yet: the client mints the transaction id, which *is* the
18
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 (ADR 0028) — before it, this
19
+ * That is the point of the client owning the id — before it, this
20
20
  * adapter parsed the `202` as an entity envelope, failed, and reported an error
21
21
  * for a create that was in fact about to succeed.
22
22
  */
@@ -18,7 +18,7 @@ import { buildEntityRestAdapterMixins as adapterMixins } from '../build-entity-r
18
18
  * `202` describing a transaction rather than an entity, so there is no stored
19
19
  * entity to read back yet: the client mints the transaction id, which *is* the
20
20
  * id the entity will be stored under, and returns the entity it already holds.
21
- * That is the point of the client owning the id (ADR 0028) — before it, this
21
+ * That is the point of the client owning the id — before it, this
22
22
  * adapter parsed the `202` as an entity envelope, failed, and reported an error
23
23
  * for a create that was in fact about to succeed.
24
24
  */ export const buildEntityRestAdapterSave = (entityConstructor, { uriConfig, create = 'entity', configuration })=>// Deliberately not shadowing `TEntity` here (as the read builders do): the
@@ -57,7 +57,7 @@ import { buildEntityRestAdapterMixins as adapterMixins } from '../build-entity-r
57
57
  // point in the system that knows it — the entity returned below is
58
58
  // indistinguishable from a plain REST create, so without this the
59
59
  // browser navigates away at the `202` and a later failure leaves no row,
60
- // no message and nothing to retry from (ADR 0043).
60
+ // no message and nothing to retry from.
61
61
  //
62
62
  // `serviceOption`, deliberately: reading the tag this way keeps it out
63
63
  // of this function's `R`, so the storefront, the plain REST adapters and
@@ -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 makeEntityEnvelope,\n readEntityEnvelope,\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 { 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 **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 (ADR 0028) — 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 { uriConfig, create = 'entity', configuration }: 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 isCreate = entity.id == null;\n\n const url = yield* adapterMixins.buildEntityBaseUrl(\n configuration,\n { uriConfig },\n key,\n isCreate ? undefined : String(entity.id),\n );\n\n if (isCreate && create === 'command') {\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 const transactionId = crypto.randomUUID();\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 }),\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\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 (ADR 0043).\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 httpRequest = adapterMixins.buildEntityRequest({\n method: isCreate ? 'POST' : 'PUT',\n url,\n envelope: makeEntityEnvelope(\n entityConstructor,\n entity as unknown as TEntity,\n ),\n });\n\n const response = yield* performHttpRequestThroughFetch(httpRequest);\n const saved = yield* readEntityEnvelope(entityConstructor, response.body);\n\n return saved as unknown as TInput;\n });\n"],"names":["extractMetaEntity","makeEntityEnvelope","readEntityEnvelope","serializeEntity","makeCommandEnvelope","readTransactionAcceptedEnvelope","TransactionSinkTag","Effect","Option","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","buildEntityRestAdapterSave","entityConstructor","uriConfig","create","configuration","entity","gen","metaEntity","key","name","isCreate","id","url","buildEntityBaseUrl","undefined","String","transactionId","crypto","randomUUID","command","type","payload","accepted","buildEntityRequest","method","envelope","body","sink","serviceOption","isSome","value","began","at","Date","toISOString","httpRequest","response","saved"],"mappings":"AAAA,SAGEA,iBAAiB,EACjBC,kBAAkB,EAClBC,kBAAkB,EAClBC,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;AAG7G;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,MAAMC,6BACX,CACEC,mBACA,EAAEC,SAAS,EAAEC,SAAS,QAAQ,EAAEC,aAAa,EAA0B,GAEzE,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,CAAwBC,SACtBV,OAAOW,GAAG,CAAC;gBAEGC;YADZ,MAAMA,aAAanB,kBAAkBa;YACrC,MAAMO,OAAMD,kBAAAA,WAAWC,GAAG,YAAdD,kBAAkBA,WAAWE,IAAI;YAC7C,MAAMC,WAAWL,OAAOM,EAAE,IAAI;YAE9B,MAAMC,MAAM,OAAOb,cAAcc,kBAAkB,CACjDT,eACA;gBAAEF;YAAU,GACZM,KACAE,WAAWI,YAAYC,OAAOV,OAAOM,EAAE;YAGzC,IAAID,YAAYP,WAAW,WAAW;gBACpC,yEAAyE;gBACzE,uEAAuE;gBACvE,kCAAkC;gBAClC,MAAMa,gBAAgBC,OAAOC,UAAU;gBACvCb,OAAOM,EAAE,GAAGK;gBAEZ,MAAMG,UAA8B;oBAClCH;oBACAI,MAAM;oBACNf,QAAQG;oBACRa,SAAS9B,gBACPU,mBACAI;gBAEJ;gBAEA,MAAMiB,WAAW,OAAOzB,+BACtBE,cAAcwB,kBAAkB,CAAC;oBAC/BC,QAAQ;oBACRZ;oBACAa,UAAUjC,oBAAoB2B;gBAChC;gBAEF,wEAAwE;gBACxE,qEAAqE;gBACrE,qEAAqE;gBACrE,OAAO1B,gCAAgC6B,SAASI,IAAI;gBAEpD,uEAAuE;gBACvE,mEAAmE;gBACnE,kEAAkE;gBAClE,yEAAyE;gBACzE,mDAAmD;gBACnD,EAAE;gBACF,uEAAuE;gBACvE,yEAAyE;gBACzE,yEAAyE;gBACzE,sEAAsE;gBACtE,sDAAsD;gBACtD,MAAMC,OAAO,OAAOhC,OAAOiC,aAAa,CAAClC;gBACzC,IAAIE,OAAOiC,MAAM,CAACF,OAAO;oBACvB,iEAAiE;oBACjE,uEAAuE;oBACvE,kEAAkE;oBAClEA,KAAKG,KAAK,CAACC,KAAK,CAAC;wBACff;wBACAX,QAAQG;wBACRwB,IAAI,IAAIC,OAAOC,WAAW;oBAC5B;gBACF;gBAEA,OAAO7B;YACT;YAEA,MAAM8B,cAAcpC,cAAcwB,kBAAkB,CAAC;gBACnDC,QAAQd,WAAW,SAAS;gBAC5BE;gBACAa,UAAUpC,mBACRY,mBACAI;YAEJ;YAEA,MAAM+B,WAAW,OAAOvC,+BAA+BsC;YACvD,MAAME,QAAQ,OAAO/C,mBAAmBW,mBAAmBmC,SAASV,IAAI;YAExE,OAAOW;QACT,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 makeEntityEnvelope,\n readEntityEnvelope,\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 { 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 **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 { uriConfig, create = 'entity', configuration }: 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 isCreate = entity.id == null;\n\n const url = yield* adapterMixins.buildEntityBaseUrl(\n configuration,\n { uriConfig },\n key,\n isCreate ? undefined : String(entity.id),\n );\n\n if (isCreate && create === 'command') {\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 const transactionId = crypto.randomUUID();\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 }),\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\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 httpRequest = adapterMixins.buildEntityRequest({\n method: isCreate ? 'POST' : 'PUT',\n url,\n envelope: makeEntityEnvelope(\n entityConstructor,\n entity as unknown as TEntity,\n ),\n });\n\n const response = yield* performHttpRequestThroughFetch(httpRequest);\n const saved = yield* readEntityEnvelope(entityConstructor, response.body);\n\n return saved as unknown as TInput;\n });\n"],"names":["extractMetaEntity","makeEntityEnvelope","readEntityEnvelope","serializeEntity","makeCommandEnvelope","readTransactionAcceptedEnvelope","TransactionSinkTag","Effect","Option","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","buildEntityRestAdapterSave","entityConstructor","uriConfig","create","configuration","entity","gen","metaEntity","key","name","isCreate","id","url","buildEntityBaseUrl","undefined","String","transactionId","crypto","randomUUID","command","type","payload","accepted","buildEntityRequest","method","envelope","body","sink","serviceOption","isSome","value","began","at","Date","toISOString","httpRequest","response","saved"],"mappings":"AAAA,SAGEA,iBAAiB,EACjBC,kBAAkB,EAClBC,kBAAkB,EAClBC,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;AAG7G;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,MAAMC,6BACX,CACEC,mBACA,EAAEC,SAAS,EAAEC,SAAS,QAAQ,EAAEC,aAAa,EAA0B,GAEzE,2EAA2E;IAC3E,4EAA4E;IAC5E,6CAA6C;IAC7C,CAAwBC,SACtBV,OAAOW,GAAG,CAAC;gBAEGC;YADZ,MAAMA,aAAanB,kBAAkBa;YACrC,MAAMO,OAAMD,kBAAAA,WAAWC,GAAG,YAAdD,kBAAkBA,WAAWE,IAAI;YAC7C,MAAMC,WAAWL,OAAOM,EAAE,IAAI;YAE9B,MAAMC,MAAM,OAAOb,cAAcc,kBAAkB,CACjDT,eACA;gBAAEF;YAAU,GACZM,KACAE,WAAWI,YAAYC,OAAOV,OAAOM,EAAE;YAGzC,IAAID,YAAYP,WAAW,WAAW;gBACpC,yEAAyE;gBACzE,uEAAuE;gBACvE,kCAAkC;gBAClC,MAAMa,gBAAgBC,OAAOC,UAAU;gBACvCb,OAAOM,EAAE,GAAGK;gBAEZ,MAAMG,UAA8B;oBAClCH;oBACAI,MAAM;oBACNf,QAAQG;oBACRa,SAAS9B,gBACPU,mBACAI;gBAEJ;gBAEA,MAAMiB,WAAW,OAAOzB,+BACtBE,cAAcwB,kBAAkB,CAAC;oBAC/BC,QAAQ;oBACRZ;oBACAa,UAAUjC,oBAAoB2B;gBAChC;gBAEF,wEAAwE;gBACxE,qEAAqE;gBACrE,qEAAqE;gBACrE,OAAO1B,gCAAgC6B,SAASI,IAAI;gBAEpD,uEAAuE;gBACvE,mEAAmE;gBACnE,kEAAkE;gBAClE,yEAAyE;gBACzE,wCAAwC;gBACxC,EAAE;gBACF,uEAAuE;gBACvE,yEAAyE;gBACzE,yEAAyE;gBACzE,sEAAsE;gBACtE,sDAAsD;gBACtD,MAAMC,OAAO,OAAOhC,OAAOiC,aAAa,CAAClC;gBACzC,IAAIE,OAAOiC,MAAM,CAACF,OAAO;oBACvB,iEAAiE;oBACjE,uEAAuE;oBACvE,kEAAkE;oBAClEA,KAAKG,KAAK,CAACC,KAAK,CAAC;wBACff;wBACAX,QAAQG;wBACRwB,IAAI,IAAIC,OAAOC,WAAW;oBAC5B;gBACF;gBAEA,OAAO7B;YACT;YAEA,MAAM8B,cAAcpC,cAAcwB,kBAAkB,CAAC;gBACnDC,QAAQd,WAAW,SAAS;gBAC5BE;gBACAa,UAAUpC,mBACRY,mBACAI;YAEJ;YAEA,MAAM+B,WAAW,OAAOvC,+BAA+BsC;YACvD,MAAME,QAAQ,OAAO/C,mBAAmBW,mBAAmBmC,SAASV,IAAI;YAExE,OAAOW;QACT,GAAG"}
@@ -9,7 +9,7 @@ import type { BuildEntityRestOptions } from '../../entity-rest/types.js';
9
9
  * commits — the outbox is in the same Mongo transaction — while no event is ever
10
10
  * published and the tracker holds no record at all. A caller that treated this
11
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 (ADR 0043). The route also
12
+ * the moment nobody can tell a UI bug from an outage. The route also
13
13
  * answers `404` for another tenant's record, deliberately, so it cannot be
14
14
  * walked to probe ids that are also primary keys — indistinguishable here, and
15
15
  * correctly so: neither is a record this caller may reconcile against.
@@ -17,7 +17,7 @@ import { buildEntityRestAdapterMixins as adapterMixins } from '../../entity-rest
17
17
  * commits — the outbox is in the same Mongo transaction — while no event is ever
18
18
  * published and the tracker holds no record at all. A caller that treated this
19
19
  * as terminal would un-render a healthy write during a broker outage, which is
20
- * the moment nobody can tell a UI bug from an outage (ADR 0043). The route also
20
+ * the moment nobody can tell a UI bug from an outage. The route also
21
21
  * answers `404` for another tenant's record, deliberately, so it cannot be
22
22
  * walked to probe ids that are also primary keys — indistinguishable here, and
23
23
  * correctly so: neither is a record this caller may reconcile against.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.ts"],"sourcesContent":["import type { EntifixError } 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 (ADR 0043). 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 */\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, EntifixError> =>\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 error.details?.['status'] === 404\n ? Effect.succeed(undefined)\n : Effect.fail(error),\n ),\n ),\n };\n};\n"],"names":["readTransactionRecordEnvelope","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","TRANSACTION_PATH","buildTransactionStatusReader","options","configuration","read","transactionId","gen","url","buildEntityBaseUrl","response","buildEntityRequest","method","body","pipe","catch","error","details","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;AAG5H;;;;;CAKC,GACD,MAAMC,mBAAmB;AAEzB;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,MAAMC,+BAA+B,CAC1CC;IAEA,MAAM,EAAEC,aAAa,EAAE,GAAGD;IAE1B,OAAO;QACLE,MAAM,CACJC,gBAEAT,OAAOU,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAOR,cAAcS,kBAAkB,CACjDL,eACAD,SACAF,kBACAK;gBAGF,MAAMI,WAAW,OAAOZ,+BACtBE,cAAcW,kBAAkB,CAAC;oBAAEC,QAAQ;oBAAOJ;gBAAI;gBAGxD,OAAO,OAAOZ,8BAA8Bc,SAASG,IAAI;YAC3D,GAAGC,IAAI,CACLjB,OAAOkB,KAAK,CAACC,CAAAA;oBACXA;uBAAAA,EAAAA,iBAAAA,MAAMC,OAAO,qBAAbD,cAAe,CAAC,SAAS,MAAK,MAC1BnB,OAAOqB,OAAO,CAACC,aACftB,OAAOuB,IAAI,CAACJ;;IAGxB;AACF,EAAE"}
1
+ {"version":3,"sources":["../../../../src/adapters/transaction-status/build-transaction-status-reader/build-transaction-status-reader.ts"],"sourcesContent":["import type { EntifixError } 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 */\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, EntifixError> =>\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 error.details?.['status'] === 404\n ? Effect.succeed(undefined)\n : Effect.fail(error),\n ),\n ),\n };\n};\n"],"names":["readTransactionRecordEnvelope","Effect","performHttpRequestThroughFetch","buildEntityRestAdapterMixins","adapterMixins","TRANSACTION_PATH","buildTransactionStatusReader","options","configuration","read","transactionId","gen","url","buildEntityBaseUrl","response","buildEntityRequest","method","body","pipe","catch","error","details","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;AAG5H;;;;;CAKC,GACD,MAAMC,mBAAmB;AAEzB;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,MAAMC,+BAA+B,CAC1CC;IAEA,MAAM,EAAEC,aAAa,EAAE,GAAGD;IAE1B,OAAO;QACLE,MAAM,CACJC,gBAEAT,OAAOU,GAAG,CAAC;gBACT,MAAMC,MAAM,OAAOR,cAAcS,kBAAkB,CACjDL,eACAD,SACAF,kBACAK;gBAGF,MAAMI,WAAW,OAAOZ,+BACtBE,cAAcW,kBAAkB,CAAC;oBAAEC,QAAQ;oBAAOJ;gBAAI;gBAGxD,OAAO,OAAOZ,8BAA8Bc,SAASG,IAAI;YAC3D,GAAGC,IAAI,CACLjB,OAAOkB,KAAK,CAACC,CAAAA;oBACXA;uBAAAA,EAAAA,iBAAAA,MAAMC,OAAO,qBAAbD,cAAe,CAAC,SAAS,MAAK,MAC1BnB,OAAOqB,OAAO,CAACC,aACftB,OAAOuB,IAAI,CAACJ;;IAGxB;AACF,EAAE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@entifix/rest",
3
- "version": "0.5.0",
3
+ "version": "0.6.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": {
@@ -51,12 +51,12 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "@swc/helpers": "0.5.23",
54
- "@entifix/core": "0.5.0",
55
- "@entifix/transactions": "0.5.0"
54
+ "@entifix/core": "0.6.0",
55
+ "@entifix/transactions": "0.6.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "effect": "4.0.5",
59
- "@entifix/testing-unit": "0.5.0"
59
+ "@entifix/testing-unit": "0.6.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "effect": "^4.0.5"