@entifix/rest 0.11.0 → 0.12.1
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.
- package/dist/adapters/entity-use-case/index.d.ts +2 -0
- package/dist/adapters/entity-use-case/index.d.ts.map +1 -0
- package/dist/adapters/entity-use-case/index.js +3 -0
- package/dist/adapters/entity-use-case/index.js.map +1 -0
- package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.d.ts +13 -0
- package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.d.ts.map +1 -0
- package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.js +56 -0
- package/dist/adapters/entity-use-case/make-entity-rest-use-case-runner.js.map +1 -0
- package/dist/endpoint/endpoint-client.d.ts +39 -0
- package/dist/endpoint/endpoint-client.d.ts.map +1 -0
- package/dist/endpoint/endpoint-client.js +95 -0
- package/dist/endpoint/endpoint-client.js.map +1 -0
- package/dist/endpoint/index.d.ts +3 -0
- package/dist/endpoint/index.d.ts.map +1 -0
- package/dist/endpoint/index.js +4 -0
- package/dist/endpoint/index.js.map +1 -0
- package/dist/endpoint/participant-client.d.ts +23 -0
- package/dist/endpoint/participant-client.d.ts.map +1 -0
- package/dist/endpoint/participant-client.js +75 -0
- package/dist/endpoint/participant-client.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/entity-use-case/index.ts"],"names":[],"mappings":"AAAA,cAAc,uCAAuC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/entity-use-case/index.ts"],"sourcesContent":["export * from './make-entity-rest-use-case-runner.js';\n"],"names":[],"mappings":"AAAA,cAAc,wCAAwC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Entity, type EntityConstructor, type EntityUseCaseRunner } from '@entifix/core';
|
|
2
|
+
import type { BuildEntityRestOptions } from '../entity-rest/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Runs an entity's verbs against the service that serves them, at the path the
|
|
5
|
+
* shell derives from each verb's key and binding — under the same base URL the
|
|
6
|
+
* entity's other adapters use, so a same-origin proxy is followed the same way
|
|
7
|
+
* ([ADR 0020](../../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)).
|
|
8
|
+
*
|
|
9
|
+
* A refusal arrives as the failure the service sent, decoded from its problem
|
|
10
|
+
* body, so a screen shows the coded message rather than a generic error.
|
|
11
|
+
*/
|
|
12
|
+
export declare const makeEntityRestUseCaseRunner: <TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, { uriConfig, configuration }: BuildEntityRestOptions) => EntityUseCaseRunner;
|
|
13
|
+
//# sourceMappingURL=make-entity-rest-use-case-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make-entity-rest-use-case-runner.d.ts","sourceRoot":"","sources":["../../../src/adapters/entity-use-case/make-entity-rest-use-case-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EAIzB,MAAM,eAAe,CAAC;AAKvB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEtE;;;;;;;;GAQG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,SAAS,MAAM,EAChE,mBAAmB,iBAAiB,CAAC,OAAO,CAAC,EAC7C,8BAA8B,sBAAsB,KACnD,mBA0CF,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { extractMetaEntity, ProtocolFailure, toWireSelection } from '@entifix/core';
|
|
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
|
+
/**
|
|
6
|
+
* Runs an entity's verbs against the service that serves them, at the path the
|
|
7
|
+
* shell derives from each verb's key and binding — under the same base URL the
|
|
8
|
+
* entity's other adapters use, so a same-origin proxy is followed the same way
|
|
9
|
+
* ([ADR 0020](../../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)).
|
|
10
|
+
*
|
|
11
|
+
* A refusal arrives as the failure the service sent, decoded from its problem
|
|
12
|
+
* body, so a screen shows the coded message rather than a generic error.
|
|
13
|
+
*/ export const makeEntityRestUseCaseRunner = (entityConstructor, { uriConfig, configuration })=>{
|
|
14
|
+
var _metaEntity_key;
|
|
15
|
+
const metaEntity = extractMetaEntity(entityConstructor);
|
|
16
|
+
const name = (_metaEntity_key = metaEntity.key) != null ? _metaEntity_key : metaEntity.name;
|
|
17
|
+
const baseUrl = (id)=>adapterMixins.buildEntityBaseUrl(configuration, {
|
|
18
|
+
uriConfig
|
|
19
|
+
}, name, id);
|
|
20
|
+
return {
|
|
21
|
+
run: (useCase, id)=>Effect.gen(function*() {
|
|
22
|
+
const url = yield* baseUrl(useCase.binding === 'entity' && id != null ? String(id) : undefined);
|
|
23
|
+
yield* performHttpRequestThroughFetch({
|
|
24
|
+
method: 'POST',
|
|
25
|
+
url: `${url}/${useCase.key}`
|
|
26
|
+
});
|
|
27
|
+
}),
|
|
28
|
+
bulk: (useCase, selection)=>Effect.gen(function*() {
|
|
29
|
+
var _response_body;
|
|
30
|
+
const url = yield* baseUrl();
|
|
31
|
+
// The wire form, never the selection itself: a `Set` serializes to
|
|
32
|
+
// `{}`, so a selection sent raw would lose its ids.
|
|
33
|
+
const response = yield* performHttpRequestThroughFetch({
|
|
34
|
+
method: 'POST',
|
|
35
|
+
url: `${url}/${useCase.key}`,
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json'
|
|
38
|
+
},
|
|
39
|
+
body: {
|
|
40
|
+
selection: toWireSelection(selection)
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// A bulk answer is a `bulkOutcome` envelope, one outcome per record in
|
|
44
|
+
// its `data`; anything else is a peer speaking something else.
|
|
45
|
+
const outcomes = (_response_body = response.body) == null ? void 0 : _response_body.data;
|
|
46
|
+
if (!Array.isArray(outcomes)) {
|
|
47
|
+
return yield* new ProtocolFailure({
|
|
48
|
+
reason: 'a bulk answer carries its outcomes in a bulkOutcome envelope'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return outcomes;
|
|
52
|
+
})
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=make-entity-rest-use-case-runner.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type AnyEndpoint, type DeclaredFailure, type EndpointRequest, type EndpointSuccess, type EntifixFailure, type TechnicalFailure } from '@entifix/core';
|
|
2
|
+
import { Effect } from 'effect';
|
|
3
|
+
import { HttpClient } from 'effect/http';
|
|
4
|
+
/**
|
|
5
|
+
* A refusal any guarded endpoint may answer without declaring it: the caller
|
|
6
|
+
* is not signed in, may not, or sent something the endpoint cannot read.
|
|
7
|
+
*/
|
|
8
|
+
export type GuardFailure = EntifixFailure<string, string, 'unauthenticated' | 'forbidden' | 'invalid'>;
|
|
9
|
+
/** Everything a call may fail with: what it declares, and what any call may. */
|
|
10
|
+
export type EndpointCallFailure<D> = DeclaredFailure<D> | GuardFailure | TechnicalFailure;
|
|
11
|
+
/** What a caller sends: the endpoint's decoded parts, and its key when idempotent. */
|
|
12
|
+
export type EndpointCallRequest<D> = EndpointRequest<D> & (D extends {
|
|
13
|
+
readonly idempotent?: infer Mode;
|
|
14
|
+
} ? [NonNullable<Mode>] extends [never] ? unknown : {
|
|
15
|
+
readonly idempotencyKey: string;
|
|
16
|
+
} : unknown);
|
|
17
|
+
/** One call per endpoint, by its name. */
|
|
18
|
+
export type EndpointClient<Endpoints extends ReadonlyArray<AnyEndpoint>> = {
|
|
19
|
+
readonly [D in Endpoints[number] as D['name']]: (request: EndpointCallRequest<D>) => Effect.Effect<EndpointSuccess<D>, EndpointCallFailure<D>, HttpClient.HttpClient>;
|
|
20
|
+
};
|
|
21
|
+
export interface EndpointClientOptions {
|
|
22
|
+
/** The origin and any prefix the endpoints' paths are joined to. */
|
|
23
|
+
readonly baseUrl: string;
|
|
24
|
+
/**
|
|
25
|
+
* Shapes the client before each call — forwarding a cookie from a Next
|
|
26
|
+
* server component, or adding a crossing token for a participant call.
|
|
27
|
+
*/
|
|
28
|
+
readonly transformClient?: (client: HttpClient.HttpClient) => HttpClient.HttpClient;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A typed client over a set of endpoint descriptors
|
|
32
|
+
* ([ADR 0020](../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)):
|
|
33
|
+
* one call per endpoint, by name, typed from its descriptor. It runs wherever
|
|
34
|
+
* an `HttpClient` does — `FetchHttpClient` in a browser, a Next server with a
|
|
35
|
+
* forwarded cookie, an Effect service calling a participant — and needs
|
|
36
|
+
* nothing of the shell that serves the endpoints, its status table included.
|
|
37
|
+
*/
|
|
38
|
+
export declare const makeEndpointClient: <const Endpoints extends ReadonlyArray<AnyEndpoint>>(endpoints: Endpoints, options: EndpointClientOptions) => EndpointClient<Endpoints>;
|
|
39
|
+
//# sourceMappingURL=endpoint-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"endpoint-client.d.ts","sourceRoot":"","sources":["../../src/endpoint/endpoint-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAGhB,KAAK,eAAe,EAEpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,cAAc,EAMnB,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EACL,UAAU,EAGX,MAAM,aAAa,CAAC;AAErB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,cAAc,CACvC,MAAM,EACN,MAAM,EACN,iBAAiB,GAAG,WAAW,GAAG,SAAS,CAC5C,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAC/B,eAAe,CAAC,CAAC,CAAC,GAAG,YAAY,GAAG,gBAAgB,CAAC;AAEvD,sFAAsF;AACtF,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,GACrD,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;CAAE,GAC3C,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACjC,OAAO,GACP;IAAE,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GACrC,OAAO,CAAC,CAAC;AAEf,0CAA0C;AAC1C,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,aAAa,CAAC,WAAW,CAAC,IAAI;IACzE,QAAQ,EAAE,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,GAAG,CAC9C,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAC5B,MAAM,CAAC,MAAM,CAChB,eAAe,CAAC,CAAC,CAAC,EAClB,mBAAmB,CAAC,CAAC,CAAC,EACtB,UAAU,CAAC,UAAU,CACtB;CACF,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,CACzB,MAAM,EAAE,UAAU,CAAC,UAAU,KAC1B,UAAU,CAAC,UAAU,CAAC;CAC5B;AAgLD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAC7B,KAAK,CAAC,SAAS,SAAS,aAAa,CAAC,WAAW,CAAC,EAElD,WAAW,SAAS,EACpB,SAAS,qBAAqB,KAC7B,cAAc,CAAC,SAAS,CAMgB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { currentRemainingMs, DEADLINE_HEADER, decodeProblem, formatIdempotencyKey, IDEMPOTENCY_KEY_HEADER, Overloaded, PROBLEM_CONTENT_TYPE, ProtocolFailure, Timeout, Unavailable } from '@entifix/core';
|
|
2
|
+
import { Effect, Schema } from 'effect';
|
|
3
|
+
import { HttpClient, HttpClientRequest } from 'effect/http';
|
|
4
|
+
const toSchema = (schema)=>Schema.isSchema(schema) ? schema : Schema.Struct(schema);
|
|
5
|
+
const encode = (schema, value)=>schema === undefined ? Effect.succeed(undefined) : Schema.encodeUnknownEffect(toSchema(schema))(value).pipe(Effect.mapError((error)=>new ProtocolFailure({
|
|
6
|
+
reason: `the request does not encode: ${error.message}`
|
|
7
|
+
})));
|
|
8
|
+
/**
|
|
9
|
+
* The path with every `:param` replaced by its encoded value. A path that
|
|
10
|
+
* names a parameter is one whose descriptor declares it, so the encoded
|
|
11
|
+
* params carry every name the path does.
|
|
12
|
+
*/ const pathOf = (path, params)=>path.replace(/:([A-Za-z0-9_]+)/g, (_match, name)=>encodeURIComponent(String(params[name])));
|
|
13
|
+
const queryOf = (query)=>Object.fromEntries(Object.entries(query != null ? query : {}).filter(([, value])=>value !== undefined).map(([key, value])=>[
|
|
14
|
+
key,
|
|
15
|
+
Array.isArray(value) ? value.map(String) : String(value)
|
|
16
|
+
]));
|
|
17
|
+
const ALWAYS = new Set([
|
|
18
|
+
'unauthenticated',
|
|
19
|
+
'forbidden',
|
|
20
|
+
'invalid'
|
|
21
|
+
]);
|
|
22
|
+
/**
|
|
23
|
+
* The failure a response names, decoded by its `code` against what the
|
|
24
|
+
* endpoint may answer — never by its status, which is the shell's to choose.
|
|
25
|
+
* A business failure the endpoint does not declare, a code nobody registered
|
|
26
|
+
* and a body that is not a problem are all `protocol`: the peer said something
|
|
27
|
+
* this contract does not admit.
|
|
28
|
+
*/ const failureOf = (descriptor, response)=>Effect.gen(function*() {
|
|
29
|
+
var _response_headers_contenttype;
|
|
30
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(()=>''));
|
|
31
|
+
const isProblem = ((_response_headers_contenttype = response.headers['content-type']) == null ? void 0 : _response_headers_contenttype.startsWith(PROBLEM_CONTENT_TYPE)) === true;
|
|
32
|
+
if (!isProblem) return new ProtocolFailure({
|
|
33
|
+
reason: `${descriptor.method} ${descriptor.path} answered ${String(response.status)} without a problem body`,
|
|
34
|
+
upstreamStatus: response.status
|
|
35
|
+
});
|
|
36
|
+
const failure = decodeProblem(parse(text), response.status);
|
|
37
|
+
const declared = descriptor.errors.some((failureClass)=>failureClass.identifier === failure._tag);
|
|
38
|
+
if (failure.family === 'business' && !declared && !ALWAYS.has(failure.category)) return new ProtocolFailure({
|
|
39
|
+
reason: `${descriptor.method} ${descriptor.path} answered '${failure.code}', which it does not declare`,
|
|
40
|
+
upstreamStatus: response.status
|
|
41
|
+
});
|
|
42
|
+
const retryAfter = Number(response.headers['retry-after']);
|
|
43
|
+
return failure._tag === 'Overloaded' && !('retryAfterSeconds' in failure) && Number.isFinite(retryAfter) && retryAfter > 0 ? new Overloaded({
|
|
44
|
+
retryAfterSeconds: retryAfter
|
|
45
|
+
}) : failure;
|
|
46
|
+
});
|
|
47
|
+
const parse = (text)=>{
|
|
48
|
+
try {
|
|
49
|
+
return JSON.parse(text);
|
|
50
|
+
} catch (unused) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const call = (descriptor, options, request)=>Effect.gen(function*() {
|
|
55
|
+
var _ref;
|
|
56
|
+
// The caller's remaining budget travels on, and a budget already spent is
|
|
57
|
+
// not worth a round trip.
|
|
58
|
+
const remaining = yield* currentRemainingMs;
|
|
59
|
+
if (remaining === 0) return yield* Effect.fail(new Timeout({
|
|
60
|
+
dependency: `${descriptor.method} ${descriptor.path}`
|
|
61
|
+
}));
|
|
62
|
+
const params = yield* encode(descriptor.params, request['params']);
|
|
63
|
+
const query = yield* encode(descriptor.query, request['query']);
|
|
64
|
+
const payload = yield* encode(descriptor.payload, request['payload']);
|
|
65
|
+
let outgoing = HttpClientRequest.make(descriptor.method)(`${options.baseUrl}${pathOf(descriptor.path, params)}`).pipe(HttpClientRequest.setUrlParams(queryOf(query)), HttpClientRequest.setHeader('accept', 'application/json'));
|
|
66
|
+
if (descriptor.payload !== undefined) outgoing = HttpClientRequest.bodyText(outgoing, JSON.stringify(payload), 'application/json');
|
|
67
|
+
if (descriptor.idempotent !== undefined) outgoing = HttpClientRequest.setHeader(outgoing, IDEMPOTENCY_KEY_HEADER, formatIdempotencyKey(request['idempotencyKey']));
|
|
68
|
+
if (remaining !== undefined) outgoing = HttpClientRequest.setHeader(outgoing, DEADLINE_HEADER, String(remaining));
|
|
69
|
+
const base = yield* HttpClient.HttpClient;
|
|
70
|
+
const client = (_ref = options.transformClient == null ? void 0 : options.transformClient.call(options, base)) != null ? _ref : base;
|
|
71
|
+
const response = yield* client.execute(outgoing).pipe(Effect.mapError(()=>new Unavailable({
|
|
72
|
+
dependency: `${descriptor.method} ${descriptor.path}`
|
|
73
|
+
})));
|
|
74
|
+
if (response.status >= 400) return yield* Effect.fail((yield* failureOf(descriptor, response)));
|
|
75
|
+
const text = yield* response.text.pipe(Effect.mapError(()=>new Unavailable({
|
|
76
|
+
dependency: `${descriptor.method} ${descriptor.path}`
|
|
77
|
+
})));
|
|
78
|
+
return yield* Schema.decodeUnknownEffect(descriptor.success)(text === '' ? undefined : parse(text)).pipe(Effect.mapError((error)=>new ProtocolFailure({
|
|
79
|
+
reason: `${descriptor.method} ${descriptor.path} answered a body that does not decode: ${error.message}`,
|
|
80
|
+
upstreamStatus: response.status
|
|
81
|
+
})));
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* A typed client over a set of endpoint descriptors
|
|
85
|
+
* ([ADR 0020](../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)):
|
|
86
|
+
* one call per endpoint, by name, typed from its descriptor. It runs wherever
|
|
87
|
+
* an `HttpClient` does — `FetchHttpClient` in a browser, a Next server with a
|
|
88
|
+
* forwarded cookie, an Effect service calling a participant — and needs
|
|
89
|
+
* nothing of the shell that serves the endpoints, its status table included.
|
|
90
|
+
*/ export const makeEndpointClient = (endpoints, options)=>Object.fromEntries(endpoints.map((descriptor)=>[
|
|
91
|
+
descriptor.name,
|
|
92
|
+
(request)=>call(descriptor, options, request)
|
|
93
|
+
]));
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=endpoint-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoint/endpoint-client.ts"],"sourcesContent":["import {\n type AnyEndpoint,\n currentRemainingMs,\n DEADLINE_HEADER,\n type DeclaredFailure,\n decodeProblem,\n type EndpointRequest,\n type EndpointSchema,\n type EndpointSuccess,\n type EntifixFailure,\n formatIdempotencyKey,\n IDEMPOTENCY_KEY_HEADER,\n Overloaded,\n PROBLEM_CONTENT_TYPE,\n ProtocolFailure,\n type TechnicalFailure,\n Timeout,\n Unavailable,\n} from '@entifix/core';\nimport { Effect, Schema } from 'effect';\nimport {\n HttpClient,\n HttpClientRequest,\n type HttpClientResponse,\n} from 'effect/http';\n\n/**\n * A refusal any guarded endpoint may answer without declaring it: the caller\n * is not signed in, may not, or sent something the endpoint cannot read.\n */\nexport type GuardFailure = EntifixFailure<\n string,\n string,\n 'unauthenticated' | 'forbidden' | 'invalid'\n>;\n\n/** Everything a call may fail with: what it declares, and what any call may. */\nexport type EndpointCallFailure<D> =\n DeclaredFailure<D> | GuardFailure | TechnicalFailure;\n\n/** What a caller sends: the endpoint's decoded parts, and its key when idempotent. */\nexport type EndpointCallRequest<D> = EndpointRequest<D> &\n (D extends { readonly idempotent?: infer Mode }\n ? [NonNullable<Mode>] extends [never]\n ? unknown\n : { readonly idempotencyKey: string }\n : unknown);\n\n/** One call per endpoint, by its name. */\nexport type EndpointClient<Endpoints extends ReadonlyArray<AnyEndpoint>> = {\n readonly [D in Endpoints[number] as D['name']]: (\n request: EndpointCallRequest<D>,\n ) => Effect.Effect<\n EndpointSuccess<D>,\n EndpointCallFailure<D>,\n HttpClient.HttpClient\n >;\n};\n\nexport interface EndpointClientOptions {\n /** The origin and any prefix the endpoints' paths are joined to. */\n readonly baseUrl: string;\n /**\n * Shapes the client before each call — forwarding a cookie from a Next\n * server component, or adding a crossing token for a participant call.\n */\n readonly transformClient?: (\n client: HttpClient.HttpClient,\n ) => HttpClient.HttpClient;\n}\n\nconst toSchema = (schema: EndpointSchema): Schema.Top =>\n Schema.isSchema(schema) ? schema : Schema.Struct(schema);\n\nconst encode = (schema: EndpointSchema | undefined, value: unknown) =>\n schema === undefined\n ? Effect.succeed(undefined)\n : Schema.encodeUnknownEffect(\n toSchema(schema) as Schema.Codec<unknown, unknown>,\n )(value).pipe(\n Effect.mapError(\n error =>\n new ProtocolFailure({\n reason: `the request does not encode: ${error.message}`,\n }),\n ),\n );\n\n/**\n * The path with every `:param` replaced by its encoded value. A path that\n * names a parameter is one whose descriptor declares it, so the encoded\n * params carry every name the path does.\n */\nconst pathOf = (path: string, params: unknown): string =>\n path.replace(/:([A-Za-z0-9_]+)/g, (_match, name: string) =>\n encodeURIComponent(String((params as Record<string, unknown>)[name])),\n );\n\nconst queryOf = (query: unknown): Record<string, string | string[]> =>\n Object.fromEntries(\n Object.entries((query ?? {}) as Record<string, unknown>)\n .filter(([, value]) => value !== undefined)\n .map(([key, value]) => [\n key,\n Array.isArray(value) ? value.map(String) : String(value),\n ]),\n );\n\nconst ALWAYS: ReadonlySet<string> = new Set([\n 'unauthenticated',\n 'forbidden',\n 'invalid',\n]);\n\n/**\n * The failure a response names, decoded by its `code` against what the\n * endpoint may answer — never by its status, which is the shell's to choose.\n * A business failure the endpoint does not declare, a code nobody registered\n * and a body that is not a problem are all `protocol`: the peer said something\n * this contract does not admit.\n */\nconst failureOf = (\n descriptor: AnyEndpoint,\n response: HttpClientResponse.HttpClientResponse,\n) =>\n Effect.gen(function* () {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => ''));\n const isProblem =\n response.headers['content-type']?.startsWith(PROBLEM_CONTENT_TYPE) ===\n true;\n if (!isProblem)\n return new ProtocolFailure({\n reason: `${descriptor.method} ${descriptor.path} answered ${String(response.status)} without a problem body`,\n upstreamStatus: response.status,\n }) as EntifixFailure;\n\n const failure = decodeProblem(parse(text), response.status);\n const declared = descriptor.errors.some(\n failureClass => failureClass.identifier === failure._tag,\n );\n if (\n failure.family === 'business' &&\n !declared &&\n !ALWAYS.has(failure.category)\n )\n return new ProtocolFailure({\n reason: `${descriptor.method} ${descriptor.path} answered '${failure.code}', which it does not declare`,\n upstreamStatus: response.status,\n });\n\n const retryAfter = Number(response.headers['retry-after']);\n return failure._tag === 'Overloaded' &&\n !('retryAfterSeconds' in failure) &&\n Number.isFinite(retryAfter) &&\n retryAfter > 0\n ? new Overloaded({ retryAfterSeconds: retryAfter })\n : failure;\n });\n\nconst parse = (text: string): unknown => {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return undefined;\n }\n};\n\nconst call = (\n descriptor: AnyEndpoint,\n options: EndpointClientOptions,\n request: Record<string, unknown>,\n) =>\n Effect.gen(function* () {\n // The caller's remaining budget travels on, and a budget already spent is\n // not worth a round trip.\n const remaining = yield* currentRemainingMs;\n if (remaining === 0)\n return yield* Effect.fail(\n new Timeout({ dependency: `${descriptor.method} ${descriptor.path}` }),\n );\n\n const params = yield* encode(descriptor.params, request['params']);\n const query = yield* encode(descriptor.query, request['query']);\n const payload = yield* encode(descriptor.payload, request['payload']);\n\n let outgoing = HttpClientRequest.make(descriptor.method)(\n `${options.baseUrl}${pathOf(descriptor.path, params)}`,\n ).pipe(\n HttpClientRequest.setUrlParams(queryOf(query)),\n HttpClientRequest.setHeader('accept', 'application/json'),\n );\n if (descriptor.payload !== undefined)\n outgoing = HttpClientRequest.bodyText(\n outgoing,\n JSON.stringify(payload),\n 'application/json',\n );\n if (descriptor.idempotent !== undefined)\n outgoing = HttpClientRequest.setHeader(\n outgoing,\n IDEMPOTENCY_KEY_HEADER,\n formatIdempotencyKey(request['idempotencyKey'] as string),\n );\n if (remaining !== undefined)\n outgoing = HttpClientRequest.setHeader(\n outgoing,\n DEADLINE_HEADER,\n String(remaining),\n );\n\n const base = yield* HttpClient.HttpClient;\n const client = options.transformClient?.(base) ?? base;\n const response = yield* client.execute(outgoing).pipe(\n Effect.mapError(\n () =>\n new Unavailable({\n dependency: `${descriptor.method} ${descriptor.path}`,\n }),\n ),\n );\n\n if (response.status >= 400)\n return yield* Effect.fail(yield* failureOf(descriptor, response));\n\n const text = yield* response.text.pipe(\n Effect.mapError(\n () =>\n new Unavailable({\n dependency: `${descriptor.method} ${descriptor.path}`,\n }),\n ),\n );\n return yield* Schema.decodeUnknownEffect(\n descriptor.success as Schema.Codec<unknown, unknown>,\n )(text === '' ? undefined : parse(text)).pipe(\n Effect.mapError(\n error =>\n new ProtocolFailure({\n reason: `${descriptor.method} ${descriptor.path} answered a body that does not decode: ${error.message}`,\n upstreamStatus: response.status,\n }),\n ),\n );\n });\n\n/**\n * A typed client over a set of endpoint descriptors\n * ([ADR 0020](../../../../../docs/adr/0020-endpoints-are-descriptors-and-the-shell-builds-an-httpapi.md)):\n * one call per endpoint, by name, typed from its descriptor. It runs wherever\n * an `HttpClient` does — `FetchHttpClient` in a browser, a Next server with a\n * forwarded cookie, an Effect service calling a participant — and needs\n * nothing of the shell that serves the endpoints, its status table included.\n */\nexport const makeEndpointClient = <\n const Endpoints extends ReadonlyArray<AnyEndpoint>,\n>(\n endpoints: Endpoints,\n options: EndpointClientOptions,\n): EndpointClient<Endpoints> =>\n Object.fromEntries(\n endpoints.map(descriptor => [\n descriptor.name,\n (request: Record<string, unknown>) => call(descriptor, options, request),\n ]),\n ) as unknown as EndpointClient<Endpoints>;\n"],"names":["currentRemainingMs","DEADLINE_HEADER","decodeProblem","formatIdempotencyKey","IDEMPOTENCY_KEY_HEADER","Overloaded","PROBLEM_CONTENT_TYPE","ProtocolFailure","Timeout","Unavailable","Effect","Schema","HttpClient","HttpClientRequest","toSchema","schema","isSchema","Struct","encode","value","undefined","succeed","encodeUnknownEffect","pipe","mapError","error","reason","message","pathOf","path","params","replace","_match","name","encodeURIComponent","String","queryOf","query","Object","fromEntries","entries","filter","map","key","Array","isArray","ALWAYS","Set","failureOf","descriptor","response","gen","text","orElseSucceed","isProblem","headers","startsWith","method","status","upstreamStatus","failure","parse","declared","errors","some","failureClass","identifier","_tag","family","has","category","code","retryAfter","Number","isFinite","retryAfterSeconds","JSON","call","options","request","remaining","fail","dependency","payload","outgoing","make","baseUrl","setUrlParams","setHeader","bodyText","stringify","idempotent","base","client","transformClient","execute","decodeUnknownEffect","success","makeEndpointClient","endpoints"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/endpoint/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/endpoint/index.ts"],"sourcesContent":["export * from './endpoint-client.js';\nexport * from './participant-client.js';\n"],"names":[],"mappings":"AAAA,cAAc,uBAAuB;AACrC,cAAc,0BAA0B"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ParticipantClient, ParticipantClientTag } from '@entifix/transactions';
|
|
2
|
+
import { Layer, Redacted } from 'effect';
|
|
3
|
+
import { HttpClient } from 'effect/http';
|
|
4
|
+
/** Where one participant is reached, and the token it takes. */
|
|
5
|
+
export interface ParticipantAddress {
|
|
6
|
+
readonly baseUrl: string;
|
|
7
|
+
/** The crossing token this caller presents to that participant. */
|
|
8
|
+
readonly crossingToken: Redacted.Redacted<string>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Calls participants over HTTP, as the slip pinned each step's call
|
|
12
|
+
* ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):
|
|
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
|
|
16
|
+
* sent, by its code; a body that is not a problem is `protocol`; no answer at
|
|
17
|
+
* all is `unavailable`. A step with no call — the commit point that names no
|
|
18
|
+
* participant — answers at once.
|
|
19
|
+
*/
|
|
20
|
+
export declare const makeHttpParticipantClient: (client: HttpClient.HttpClient, participants: Readonly<Record<string, ParticipantAddress>>) => ParticipantClient;
|
|
21
|
+
/** The HTTP participant client over the host's `HttpClient`. */
|
|
22
|
+
export declare const HttpParticipantClientLayer: (participants: Readonly<Record<string, ParticipantAddress>>) => Layer.Layer<ParticipantClientTag, never, HttpClient.HttpClient>;
|
|
23
|
+
//# sourceMappingURL=participant-client.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
2
|
+
import { currentRemainingMs, DEADLINE_HEADER, decodeProblem, formatIdempotencyKey, IDEMPOTENCY_KEY_HEADER, Internal, PROBLEM_CONTENT_TYPE, ProtocolFailure, Timeout, Unavailable } from '@entifix/core';
|
|
3
|
+
import { ParticipantClientTag } from '@entifix/transactions';
|
|
4
|
+
import { Effect, Layer, Redacted } from 'effect';
|
|
5
|
+
import { HttpClient, HttpClientRequest } from 'effect/http';
|
|
6
|
+
/** The header a crossing token travels in. */ const CROSSING_TOKEN_HEADER = 'x-crossing-token';
|
|
7
|
+
/** The header a crossing names the organization it acts for in. */ const ORGANIZATION_HEADER = 'x-organization-id';
|
|
8
|
+
const parse = (text)=>{
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(text);
|
|
11
|
+
} catch (unused) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
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 : ''));
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Calls participants over HTTP, as the slip pinned each step's call
|
|
25
|
+
* ([ADR 0016](../../../../../docs/adr/0016-a-flow-engine-port-and-a-durable-workflow-adapter.md)):
|
|
26
|
+
* 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
|
|
29
|
+
* sent, by its code; a body that is not a problem is `protocol`; no answer at
|
|
30
|
+
* all is `unavailable`. A step with no call — the commit point that names no
|
|
31
|
+
* participant — answers at once.
|
|
32
|
+
*/ export const makeHttpParticipantClient = (client, participants)=>({
|
|
33
|
+
call: (request)=>Effect.gen(function*() {
|
|
34
|
+
var _request_step_participant, _request_step_parameters;
|
|
35
|
+
var _request_step_parameters1, _response_headers_contenttype;
|
|
36
|
+
const target = request.kind === 'action' ? request.step.call : request.step.compensation;
|
|
37
|
+
if (target === undefined) return undefined;
|
|
38
|
+
const name = (_request_step_participant = request.step.participant) != null ? _request_step_participant : '';
|
|
39
|
+
const address = participants[name];
|
|
40
|
+
if (address === undefined) return yield* Effect.fail(new Internal());
|
|
41
|
+
const dependency = `${name} ${target.method} ${target.path}`;
|
|
42
|
+
const remaining = yield* currentRemainingMs;
|
|
43
|
+
if (remaining === 0) return yield* Effect.fail(new Timeout({
|
|
44
|
+
dependency
|
|
45
|
+
}));
|
|
46
|
+
const organizationId = (_request_step_parameters1 = request.step.parameters) == null ? void 0 : _request_step_parameters1['organizationId'];
|
|
47
|
+
const outgoing = HttpClientRequest.make(target.method)(`${address.baseUrl}${pathOf(request, target.path)}`).pipe(HttpClientRequest.setHeaders(_extends({
|
|
48
|
+
accept: 'application/json',
|
|
49
|
+
[CROSSING_TOKEN_HEADER]: Redacted.value(address.crossingToken),
|
|
50
|
+
[IDEMPOTENCY_KEY_HEADER]: formatIdempotencyKey(request.idempotencyKey)
|
|
51
|
+
}, typeof organizationId === 'string' ? {
|
|
52
|
+
[ORGANIZATION_HEADER]: organizationId
|
|
53
|
+
} : {}, remaining === undefined ? {} : {
|
|
54
|
+
[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'));
|
|
59
|
+
const response = yield* client.execute(outgoing).pipe(Effect.mapError(()=>new Unavailable({
|
|
60
|
+
dependency
|
|
61
|
+
})));
|
|
62
|
+
const text = yield* response.text.pipe(Effect.mapError(()=>new Unavailable({
|
|
63
|
+
dependency
|
|
64
|
+
})));
|
|
65
|
+
if (response.status < 400) return text === '' ? undefined : parse(text);
|
|
66
|
+
const isProblem = ((_response_headers_contenttype = response.headers['content-type']) == null ? void 0 : _response_headers_contenttype.startsWith(PROBLEM_CONTENT_TYPE)) === true;
|
|
67
|
+
return yield* Effect.fail(isProblem ? decodeProblem(parse(text), response.status) : new ProtocolFailure({
|
|
68
|
+
reason: `${dependency} answered ${String(response.status)} without a problem body`,
|
|
69
|
+
upstreamStatus: response.status
|
|
70
|
+
}));
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
/** The HTTP participant client over the host's `HttpClient`. */ export const HttpParticipantClientLayer = (participants)=>Layer.effect(ParticipantClientTag, Effect.map(HttpClient.HttpClient, (client)=>makeHttpParticipantClient(client, participants)));
|
|
74
|
+
|
|
75
|
+
//# sourceMappingURL=participant-client.js.map
|
|
@@ -0,0 +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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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ 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
7
|
export * from './adapters/entity-rest/types.js';
|
|
8
|
+
export * from './adapters/entity-use-case/index.js';
|
|
8
9
|
export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
|
|
9
10
|
export * from './clients/fetch/index.js';
|
|
10
11
|
export * from './clients/types.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,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,iCAAiC,CAAC;AAChD,cAAc,qCAAqC,CAAC;AACpD,cAAc,wEAAwE,CAAC;AACvF,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iCAAiC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ 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
7
|
export * from './adapters/entity-rest/types.js';
|
|
8
|
+
export * from './adapters/entity-use-case/index.js';
|
|
8
9
|
export * from './adapters/transaction-status/build-transaction-status-reader/index.js';
|
|
9
10
|
export * from './clients/fetch/index.js';
|
|
10
11
|
export * from './clients/types.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/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,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/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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entifix/rest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
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": {
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"types": "./dist/index.d.ts",
|
|
22
22
|
"import": "./dist/index.js",
|
|
23
23
|
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./endpoint": {
|
|
26
|
+
"types": "./dist/endpoint/index.d.ts",
|
|
27
|
+
"import": "./dist/endpoint/index.js",
|
|
28
|
+
"default": "./dist/endpoint/index.js"
|
|
24
29
|
}
|
|
25
30
|
},
|
|
26
31
|
"files": [
|
|
@@ -51,12 +56,12 @@
|
|
|
51
56
|
},
|
|
52
57
|
"dependencies": {
|
|
53
58
|
"@swc/helpers": "0.5.23",
|
|
54
|
-
"@entifix/core": "0.
|
|
55
|
-
"@entifix/transactions": "0.
|
|
59
|
+
"@entifix/core": "0.12.1",
|
|
60
|
+
"@entifix/transactions": "0.12.1"
|
|
56
61
|
},
|
|
57
62
|
"devDependencies": {
|
|
58
63
|
"effect": "4.0.6",
|
|
59
|
-
"@entifix/testing-unit": "0.
|
|
64
|
+
"@entifix/testing-unit": "0.12.1"
|
|
60
65
|
},
|
|
61
66
|
"peerDependencies": {
|
|
62
67
|
"effect": "^4.0.6"
|