@entifix/business 0.1.0-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.
- package/LICENSE +21 -0
- package/README.md +11 -0
- package/dist/health/health-registry.d.ts +87 -0
- package/dist/health/health-registry.d.ts.map +1 -0
- package/dist/health/health-registry.js +58 -0
- package/dist/health/health-registry.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/repository/config.repository.d.ts +7 -0
- package/dist/repository/config.repository.d.ts.map +1 -0
- package/dist/repository/config.repository.js +5 -0
- package/dist/repository/config.repository.js.map +1 -0
- package/dist/repository/entity-link-resolver.repository.d.ts +34 -0
- package/dist/repository/entity-link-resolver.repository.d.ts.map +1 -0
- package/dist/repository/entity-link-resolver.repository.js +42 -0
- package/dist/repository/entity-link-resolver.repository.js.map +1 -0
- package/dist/repository/entity-mixins.repository.d.ts +13 -0
- package/dist/repository/entity-mixins.repository.d.ts.map +1 -0
- package/dist/repository/entity-mixins.repository.js +9 -0
- package/dist/repository/entity-mixins.repository.js.map +1 -0
- package/dist/repository/entity.repository.d.ts +15 -0
- package/dist/repository/entity.repository.d.ts.map +1 -0
- package/dist/repository/entity.repository.js +5 -0
- package/dist/repository/entity.repository.js.map +1 -0
- package/dist/sessions/one-time-token-store.d.ts +40 -0
- package/dist/sessions/one-time-token-store.d.ts.map +1 -0
- package/dist/sessions/one-time-token-store.js +5 -0
- package/dist/sessions/one-time-token-store.js.map +1 -0
- package/dist/sessions/session-store.d.ts +145 -0
- package/dist/sessions/session-store.d.ts.map +1 -0
- package/dist/sessions/session-store.js +5 -0
- package/dist/sessions/session-store.js.map +1 -0
- package/dist/shutdown/shutdown-registry.d.ts +84 -0
- package/dist/shutdown/shutdown-registry.d.ts.map +1 -0
- package/dist/shutdown/shutdown-registry.js +88 -0
- package/dist/shutdown/shutdown-registry.js.map +1 -0
- package/dist/tenancy/index.d.ts +2 -0
- package/dist/tenancy/index.d.ts.map +1 -0
- package/dist/tenancy/index.js +3 -0
- package/dist/tenancy/index.js.map +1 -0
- package/dist/tenancy/tenant-database-resolver.d.ts +38 -0
- package/dist/tenancy/tenant-database-resolver.d.ts.map +1 -0
- package/dist/tenancy/tenant-database-resolver.js +9 -0
- package/dist/tenancy/tenant-database-resolver.js.map +1 -0
- package/dist/tokens/token-service.d.ts +101 -0
- package/dist/tokens/token-service.d.ts.map +1 -0
- package/dist/tokens/token-service.js +5 -0
- package/dist/tokens/token-service.js.map +1 -0
- package/dist/use-case/delete/delete.uc.d.ts +13 -0
- package/dist/use-case/delete/delete.uc.d.ts.map +1 -0
- package/dist/use-case/delete/delete.uc.js +18 -0
- package/dist/use-case/delete/delete.uc.js.map +1 -0
- package/dist/use-case/delete/index.d.ts +2 -0
- package/dist/use-case/delete/index.d.ts.map +1 -0
- package/dist/use-case/delete/index.js +3 -0
- package/dist/use-case/delete/index.js.map +1 -0
- package/dist/use-case/get/get.uc.d.ts +12 -0
- package/dist/use-case/get/get.uc.d.ts.map +1 -0
- package/dist/use-case/get/get.uc.js +18 -0
- package/dist/use-case/get/get.uc.js.map +1 -0
- package/dist/use-case/get/index.d.ts +2 -0
- package/dist/use-case/get/index.d.ts.map +1 -0
- package/dist/use-case/get/index.js +3 -0
- package/dist/use-case/get/index.js.map +1 -0
- package/dist/use-case/load/index.d.ts +2 -0
- package/dist/use-case/load/index.d.ts.map +1 -0
- package/dist/use-case/load/index.js +3 -0
- package/dist/use-case/load/index.js.map +1 -0
- package/dist/use-case/load/load.uc.d.ts +6 -0
- package/dist/use-case/load/load.uc.d.ts.map +1 -0
- package/dist/use-case/load/load.uc.js +16 -0
- package/dist/use-case/load/load.uc.js.map +1 -0
- package/dist/use-case/save/index.d.ts +2 -0
- package/dist/use-case/save/index.d.ts.map +1 -0
- package/dist/use-case/save/index.js +3 -0
- package/dist/use-case/save/index.js.map +1 -0
- package/dist/use-case/save/save.uc.d.ts +16 -0
- package/dist/use-case/save/save.uc.d.ts.map +1 -0
- package/dist/use-case/save/save.uc.js +24 -0
- package/dist/use-case/save/save.uc.js.map +1 -0
- package/dist/wiring/wiring-registry.d.ts +74 -0
- package/dist/wiring/wiring-registry.d.ts.map +1 -0
- package/dist/wiring/wiring-registry.js +49 -0
- package/dist/wiring/wiring-registry.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 r10c Technologies
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @entifix/business
|
|
2
|
+
|
|
3
|
+
This library was generated with [Nx](https://nx.dev).
|
|
4
|
+
|
|
5
|
+
## Building
|
|
6
|
+
|
|
7
|
+
Run `nx build @entifix/business` to build the library.
|
|
8
|
+
|
|
9
|
+
## Running unit tests
|
|
10
|
+
|
|
11
|
+
Run `nx test @entifix/business` to execute the unit tests via [Jest](https://jestjs.io).
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* What a probe is talking to. Read by `GET /api/$service` to sort the wiring
|
|
4
|
+
* document into stores, brokers and upstreams; readiness ignores it entirely.
|
|
5
|
+
*/
|
|
6
|
+
export type ProbeKind = 'datastore' | 'broker' | 'upstream';
|
|
7
|
+
/**
|
|
8
|
+
* One named readiness fact — "can this service still reach Mongo?".
|
|
9
|
+
*
|
|
10
|
+
* `check` is fully applied: whoever registers the probe has already closed over
|
|
11
|
+
* the client handle, so the readiness route can run every probe without knowing
|
|
12
|
+
* what any of them talk to. It must not fail — a probe that cannot answer
|
|
13
|
+
* answers `false`; an unhandled defect in a probe would take down the very
|
|
14
|
+
* endpoint that is supposed to report trouble.
|
|
15
|
+
*/
|
|
16
|
+
export interface HealthProbe {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/**
|
|
19
|
+
* What sort of thing is on the other end. `datastore` is a Store's backing
|
|
20
|
+
* engine, `broker` an exchange, `upstream` another service.
|
|
21
|
+
*/
|
|
22
|
+
readonly kind: ProbeKind;
|
|
23
|
+
/**
|
|
24
|
+
* The **logical** names this probe covers — a Store's register name
|
|
25
|
+
* (`catalog`, `saga`), an exchange (`entifix.events`), a service (`zitadel`).
|
|
26
|
+
* Never a URI, never a database name: `catalog` is physically
|
|
27
|
+
* `tenant_<organizationId>`, and a document naming those is an organization
|
|
28
|
+
* enumerator (ADR 0031).
|
|
29
|
+
*
|
|
30
|
+
* It is a list rather than one string because a single connection can back
|
|
31
|
+
* several Stores: marketplace-admin-service opens one Mongo client for the
|
|
32
|
+
* `catalog` store and the co-deployed `transaction` slice's `saga` store.
|
|
33
|
+
* Registering one probe per Store instead would put both names into the
|
|
34
|
+
* readiness response's `failing` array, which must not change.
|
|
35
|
+
*
|
|
36
|
+
* The client package that opens the connection cannot know a Store's register
|
|
37
|
+
* name, so the composition root passes it in — the one thing about a probe
|
|
38
|
+
* that is not derivable from the client.
|
|
39
|
+
*/
|
|
40
|
+
readonly targets: readonly string[];
|
|
41
|
+
readonly check: Effect.Effect<boolean>;
|
|
42
|
+
}
|
|
43
|
+
/** The outcome of running every registered probe. */
|
|
44
|
+
export interface HealthReport {
|
|
45
|
+
readonly ready: boolean;
|
|
46
|
+
/** Names of the probes that answered `false`, in registration order. */
|
|
47
|
+
readonly failing: readonly string[];
|
|
48
|
+
}
|
|
49
|
+
export interface HealthRegistry {
|
|
50
|
+
/** Add a probe. Called by an infrastructure layer as it constructs a client. */
|
|
51
|
+
readonly register: (probe: HealthProbe) => Effect.Effect<void>;
|
|
52
|
+
/** Run every probe and summarise. Never fails. */
|
|
53
|
+
readonly report: Effect.Effect<HealthReport>;
|
|
54
|
+
/**
|
|
55
|
+
* The registrations themselves, in registration order — what each probe is
|
|
56
|
+
* for, without running any of them.
|
|
57
|
+
*
|
|
58
|
+
* This is what makes the wiring document and readiness generate from **one**
|
|
59
|
+
* registration rather than from two lists that drift. Deliberately separate
|
|
60
|
+
* from {@link report}: describing a service must never cost a round trip to
|
|
61
|
+
* every datastore it has.
|
|
62
|
+
*/
|
|
63
|
+
readonly probes: Effect.Effect<readonly HealthProbe[]>;
|
|
64
|
+
}
|
|
65
|
+
declare const HealthRegistryTag_base: Context.TagClass<HealthRegistryTag, "HealthRegistryTag", HealthRegistry>;
|
|
66
|
+
/**
|
|
67
|
+
* DI tag for the readiness registry.
|
|
68
|
+
*
|
|
69
|
+
* The point of a registry rather than a hand-written list per service: a
|
|
70
|
+
* `-service` that gains a datastore gains its readiness probe by merging that
|
|
71
|
+
* client's probe layer, and nothing in the service's own code has to remember
|
|
72
|
+
* to describe it. A list in `main.ts` drifts the first time someone adds a
|
|
73
|
+
* dependency; this cannot, because the probe ships with the client.
|
|
74
|
+
*
|
|
75
|
+
* It lives here, in the contract layer, so `entifix-ts-*-client` packages can
|
|
76
|
+
* register into it without depending on a shell.
|
|
77
|
+
*/
|
|
78
|
+
export declare class HealthRegistryTag extends HealthRegistryTag_base {
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Live registry over a `Ref`. Provide it **once** per service and share the
|
|
82
|
+
* instance (`Layer.provideMerge`), or the probes register into a registry the
|
|
83
|
+
* readiness route never reads.
|
|
84
|
+
*/
|
|
85
|
+
export declare const HealthRegistryLayer: Layer.Layer<HealthRegistryTag>;
|
|
86
|
+
export {};
|
|
87
|
+
//# sourceMappingURL=health-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-registry.d.ts","sourceRoot":"","sources":["../../src/health/health-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAO,MAAM,QAAQ,CAAC;AAErD;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE5D;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;CACxC;AAED,qDAAqD;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAaD,MAAM,WAAW,cAAc;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/D,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC7C;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,WAAW,EAAE,CAAC,CAAC;CACxD;;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAkB,SAAQ,sBAGpC;CAAG;AAEN;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAAC,iBAAiB,CAgC9D,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Context, Effect, Layer, Ref } from 'effect';
|
|
2
|
+
/**
|
|
3
|
+
* How long a single probe may take before it counts as failing.
|
|
4
|
+
*
|
|
5
|
+
* This is not belt-and-braces. A driver that queues commands while its
|
|
6
|
+
* connection is down (ioredis with `maxRetriesPerRequest`, for one) leaves the
|
|
7
|
+
* probe pending instead of rejecting, so without a deadline `/api/health/ready`
|
|
8
|
+
* *hangs* when a datastore disappears — the one moment it has to answer. A
|
|
9
|
+
* probe that cannot answer in time is not ready, by definition.
|
|
10
|
+
*/ const PROBE_TIMEOUT = '2 seconds';
|
|
11
|
+
/**
|
|
12
|
+
* DI tag for the readiness registry.
|
|
13
|
+
*
|
|
14
|
+
* The point of a registry rather than a hand-written list per service: a
|
|
15
|
+
* `-service` that gains a datastore gains its readiness probe by merging that
|
|
16
|
+
* client's probe layer, and nothing in the service's own code has to remember
|
|
17
|
+
* to describe it. A list in `main.ts` drifts the first time someone adds a
|
|
18
|
+
* dependency; this cannot, because the probe ships with the client.
|
|
19
|
+
*
|
|
20
|
+
* It lives here, in the contract layer, so `entifix-ts-*-client` packages can
|
|
21
|
+
* register into it without depending on a shell.
|
|
22
|
+
*/ export class HealthRegistryTag extends Context.Tag('HealthRegistryTag')() {
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Live registry over a `Ref`. Provide it **once** per service and share the
|
|
26
|
+
* instance (`Layer.provideMerge`), or the probes register into a registry the
|
|
27
|
+
* readiness route never reads.
|
|
28
|
+
*/ export const HealthRegistryLayer = Layer.effect(HealthRegistryTag, Effect.gen(function*() {
|
|
29
|
+
const probes = yield* Ref.make([]);
|
|
30
|
+
return {
|
|
31
|
+
register: (probe)=>Ref.update(probes, (current)=>[
|
|
32
|
+
...current,
|
|
33
|
+
probe
|
|
34
|
+
]),
|
|
35
|
+
probes: Ref.get(probes),
|
|
36
|
+
report: Effect.gen(function*() {
|
|
37
|
+
const current = yield* Ref.get(probes);
|
|
38
|
+
const results = yield* Effect.forEach(current, (probe)=>probe.check.pipe(// A probe that throws is a failing probe, not a broken endpoint.
|
|
39
|
+
Effect.catchAllDefect(()=>Effect.succeed(false)), Effect.timeoutTo({
|
|
40
|
+
duration: PROBE_TIMEOUT,
|
|
41
|
+
onTimeout: ()=>false,
|
|
42
|
+
onSuccess: (ok)=>ok
|
|
43
|
+
}), Effect.map((ok)=>({
|
|
44
|
+
name: probe.name,
|
|
45
|
+
ok
|
|
46
|
+
}))), {
|
|
47
|
+
concurrency: 'unbounded'
|
|
48
|
+
});
|
|
49
|
+
const failing = results.filter((r)=>!r.ok).map((r)=>r.name);
|
|
50
|
+
return {
|
|
51
|
+
ready: failing.length === 0,
|
|
52
|
+
failing
|
|
53
|
+
};
|
|
54
|
+
})
|
|
55
|
+
};
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
//# sourceMappingURL=health-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/health/health-registry.ts"],"sourcesContent":["import { Context, Effect, Layer, Ref } from 'effect';\n\n/**\n * What a probe is talking to. Read by `GET /api/$service` to sort the wiring\n * document into stores, brokers and upstreams; readiness ignores it entirely.\n */\nexport type ProbeKind = 'datastore' | 'broker' | 'upstream';\n\n/**\n * One named readiness fact — \"can this service still reach Mongo?\".\n *\n * `check` is fully applied: whoever registers the probe has already closed over\n * the client handle, so the readiness route can run every probe without knowing\n * what any of them talk to. It must not fail — a probe that cannot answer\n * answers `false`; an unhandled defect in a probe would take down the very\n * endpoint that is supposed to report trouble.\n */\nexport interface HealthProbe {\n readonly name: string;\n /**\n * What sort of thing is on the other end. `datastore` is a Store's backing\n * engine, `broker` an exchange, `upstream` another service.\n */\n readonly kind: ProbeKind;\n /**\n * The **logical** names this probe covers — a Store's register name\n * (`catalog`, `saga`), an exchange (`entifix.events`), a service (`zitadel`).\n * Never a URI, never a database name: `catalog` is physically\n * `tenant_<organizationId>`, and a document naming those is an organization\n * enumerator (ADR 0031).\n *\n * It is a list rather than one string because a single connection can back\n * several Stores: marketplace-admin-service opens one Mongo client for the\n * `catalog` store and the co-deployed `transaction` slice's `saga` store.\n * Registering one probe per Store instead would put both names into the\n * readiness response's `failing` array, which must not change.\n *\n * The client package that opens the connection cannot know a Store's register\n * name, so the composition root passes it in — the one thing about a probe\n * that is not derivable from the client.\n */\n readonly targets: readonly string[];\n readonly check: Effect.Effect<boolean>;\n}\n\n/** The outcome of running every registered probe. */\nexport interface HealthReport {\n readonly ready: boolean;\n /** Names of the probes that answered `false`, in registration order. */\n readonly failing: readonly string[];\n}\n\n/**\n * How long a single probe may take before it counts as failing.\n *\n * This is not belt-and-braces. A driver that queues commands while its\n * connection is down (ioredis with `maxRetriesPerRequest`, for one) leaves the\n * probe pending instead of rejecting, so without a deadline `/api/health/ready`\n * *hangs* when a datastore disappears — the one moment it has to answer. A\n * probe that cannot answer in time is not ready, by definition.\n */\nconst PROBE_TIMEOUT = '2 seconds';\n\nexport interface HealthRegistry {\n /** Add a probe. Called by an infrastructure layer as it constructs a client. */\n readonly register: (probe: HealthProbe) => Effect.Effect<void>;\n /** Run every probe and summarise. Never fails. */\n readonly report: Effect.Effect<HealthReport>;\n /**\n * The registrations themselves, in registration order — what each probe is\n * for, without running any of them.\n *\n * This is what makes the wiring document and readiness generate from **one**\n * registration rather than from two lists that drift. Deliberately separate\n * from {@link report}: describing a service must never cost a round trip to\n * every datastore it has.\n */\n readonly probes: Effect.Effect<readonly HealthProbe[]>;\n}\n\n/**\n * DI tag for the readiness registry.\n *\n * The point of a registry rather than a hand-written list per service: a\n * `-service` that gains a datastore gains its readiness probe by merging that\n * client's probe layer, and nothing in the service's own code has to remember\n * to describe it. A list in `main.ts` drifts the first time someone adds a\n * dependency; this cannot, because the probe ships with the client.\n *\n * It lives here, in the contract layer, so `entifix-ts-*-client` packages can\n * register into it without depending on a shell.\n */\nexport class HealthRegistryTag extends Context.Tag('HealthRegistryTag')<\n HealthRegistryTag,\n HealthRegistry\n>() {}\n\n/**\n * Live registry over a `Ref`. Provide it **once** per service and share the\n * instance (`Layer.provideMerge`), or the probes register into a registry the\n * readiness route never reads.\n */\nexport const HealthRegistryLayer: Layer.Layer<HealthRegistryTag> = Layer.effect(\n HealthRegistryTag,\n Effect.gen(function* () {\n const probes = yield* Ref.make<readonly HealthProbe[]>([]);\n\n return {\n register: probe => Ref.update(probes, current => [...current, probe]),\n\n probes: Ref.get(probes),\n\n report: Effect.gen(function* () {\n const current = yield* Ref.get(probes);\n const results = yield* Effect.forEach(\n current,\n probe =>\n probe.check.pipe(\n // A probe that throws is a failing probe, not a broken endpoint.\n Effect.catchAllDefect(() => Effect.succeed(false)),\n Effect.timeoutTo({\n duration: PROBE_TIMEOUT,\n onTimeout: () => false,\n onSuccess: (ok: boolean) => ok,\n }),\n Effect.map(ok => ({ name: probe.name, ok })),\n ),\n { concurrency: 'unbounded' },\n );\n const failing = results.filter(r => !r.ok).map(r => r.name);\n return { ready: failing.length === 0, failing };\n }),\n };\n }),\n);\n"],"names":["Context","Effect","Layer","Ref","PROBE_TIMEOUT","HealthRegistryTag","Tag","HealthRegistryLayer","effect","gen","probes","make","register","probe","update","current","get","report","results","forEach","check","pipe","catchAllDefect","succeed","timeoutTo","duration","onTimeout","onSuccess","ok","map","name","concurrency","failing","filter","r","ready","length"],"mappings":"AAAA,SAASA,OAAO,EAAEC,MAAM,EAAEC,KAAK,EAAEC,GAAG,QAAQ,SAAS;AAoDrD;;;;;;;;CAQC,GACD,MAAMC,gBAAgB;AAmBtB;;;;;;;;;;;CAWC,GACD,OAAO,MAAMC,0BAA0BL,QAAQM,GAAG,CAAC;AAG9C;AAEL;;;;CAIC,GACD,OAAO,MAAMC,sBAAsDL,MAAMM,MAAM,CAC7EH,mBACAJ,OAAOQ,GAAG,CAAC;IACT,MAAMC,SAAS,OAAOP,IAAIQ,IAAI,CAAyB,EAAE;IAEzD,OAAO;QACLC,UAAUC,CAAAA,QAASV,IAAIW,MAAM,CAACJ,QAAQK,CAAAA,UAAW;uBAAIA;oBAASF;iBAAM;QAEpEH,QAAQP,IAAIa,GAAG,CAACN;QAEhBO,QAAQhB,OAAOQ,GAAG,CAAC;YACjB,MAAMM,UAAU,OAAOZ,IAAIa,GAAG,CAACN;YAC/B,MAAMQ,UAAU,OAAOjB,OAAOkB,OAAO,CACnCJ,SACAF,CAAAA,QACEA,MAAMO,KAAK,CAACC,IAAI,CACd,iEAAiE;gBACjEpB,OAAOqB,cAAc,CAAC,IAAMrB,OAAOsB,OAAO,CAAC,SAC3CtB,OAAOuB,SAAS,CAAC;oBACfC,UAAUrB;oBACVsB,WAAW,IAAM;oBACjBC,WAAW,CAACC,KAAgBA;gBAC9B,IACA3B,OAAO4B,GAAG,CAACD,CAAAA,KAAO,CAAA;wBAAEE,MAAMjB,MAAMiB,IAAI;wBAAEF;oBAAG,CAAA,KAE7C;gBAAEG,aAAa;YAAY;YAE7B,MAAMC,UAAUd,QAAQe,MAAM,CAACC,CAAAA,IAAK,CAACA,EAAEN,EAAE,EAAEC,GAAG,CAACK,CAAAA,IAAKA,EAAEJ,IAAI;YAC1D,OAAO;gBAAEK,OAAOH,QAAQI,MAAM,KAAK;gBAAGJ;YAAQ;QAChD;IACF;AACF,IACA"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './health/health-registry.js';
|
|
2
|
+
export * from './repository/config.repository.js';
|
|
3
|
+
export * from './repository/entity.repository.js';
|
|
4
|
+
export * from './repository/entity-link-resolver.repository.js';
|
|
5
|
+
export * from './repository/entity-mixins.repository.js';
|
|
6
|
+
export * from './sessions/one-time-token-store.js';
|
|
7
|
+
export * from './sessions/session-store.js';
|
|
8
|
+
export * from './shutdown/shutdown-registry.js';
|
|
9
|
+
export * from './tenancy/index.js';
|
|
10
|
+
export * from './tokens/token-service.js';
|
|
11
|
+
export * from './use-case/delete/index.js';
|
|
12
|
+
export * from './use-case/get/index.js';
|
|
13
|
+
export * from './use-case/load/index.js';
|
|
14
|
+
export * from './use-case/save/index.js';
|
|
15
|
+
export * from './wiring/wiring-registry.js';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,iDAAiD,CAAC;AAChE,cAAc,0CAA0C,CAAC;AACzD,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,iCAAiC,CAAC;AAChD,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,6BAA6B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export * from './health/health-registry.js';
|
|
2
|
+
export * from './repository/config.repository.js';
|
|
3
|
+
export * from './repository/entity.repository.js';
|
|
4
|
+
export * from './repository/entity-link-resolver.repository.js';
|
|
5
|
+
export * from './repository/entity-mixins.repository.js';
|
|
6
|
+
export * from './sessions/one-time-token-store.js';
|
|
7
|
+
export * from './sessions/session-store.js';
|
|
8
|
+
export * from './shutdown/shutdown-registry.js';
|
|
9
|
+
export * from './tenancy/index.js';
|
|
10
|
+
export * from './tokens/token-service.js';
|
|
11
|
+
export * from './use-case/delete/index.js';
|
|
12
|
+
export * from './use-case/get/index.js';
|
|
13
|
+
export * from './use-case/load/index.js';
|
|
14
|
+
export * from './use-case/save/index.js';
|
|
15
|
+
export * from './wiring/wiring-registry.js';
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './health/health-registry.js';\nexport * from './repository/config.repository.js';\nexport * from './repository/entity.repository.js';\nexport * from './repository/entity-link-resolver.repository.js';\nexport * from './repository/entity-mixins.repository.js';\nexport * from './sessions/one-time-token-store.js';\nexport * from './sessions/session-store.js';\nexport * from './shutdown/shutdown-registry.js';\nexport * from './tenancy/index.js';\nexport * from './tokens/token-service.js';\nexport * from './use-case/delete/index.js';\nexport * from './use-case/get/index.js';\nexport * from './use-case/load/index.js';\nexport * from './use-case/save/index.js';\nexport * from './wiring/wiring-registry.js';\n"],"names":[],"mappings":"AAAA,cAAc,8BAA8B;AAC5C,cAAc,oCAAoC;AAClD,cAAc,oCAAoC;AAClD,cAAc,kDAAkD;AAChE,cAAc,2CAA2C;AACzD,cAAc,qCAAqC;AACnD,cAAc,8BAA8B;AAC5C,cAAc,kCAAkC;AAChD,cAAc,qBAAqB;AACnC,cAAc,4BAA4B;AAC1C,cAAc,6BAA6B;AAC3C,cAAc,0BAA0B;AACxC,cAAc,2BAA2B;AACzC,cAAc,2BAA2B;AACzC,cAAc,8BAA8B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ConfigurationClient } from '@entifix/core';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
declare const ConfigurationRepositoryTag_base: Context.TagClass<ConfigurationRepositoryTag, "ConfigurationRepositoryTag", ConfigurationClient>;
|
|
4
|
+
export declare class ConfigurationRepositoryTag extends ConfigurationRepositoryTag_base {
|
|
5
|
+
}
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=config.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.repository.d.ts","sourceRoot":"","sources":["../../src/repository/config.repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;;AAEjC,qBAAa,0BAA2B,SAAQ,+BAEI;CAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/repository/config.repository.ts"],"sourcesContent":["import { ConfigurationClient } from '@entifix/core';\nimport { Context } from 'effect';\n\nexport class ConfigurationRepositoryTag extends Context.Tag(\n 'ConfigurationRepositoryTag',\n)<ConfigurationRepositoryTag, ConfigurationClient>() {}\n"],"names":["Context","ConfigurationRepositoryTag","Tag"],"mappings":"AACA,SAASA,OAAO,QAAQ,SAAS;AAEjC,OAAO,MAAMC,mCAAmCD,QAAQE,GAAG,CACzD;AACoD"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type Entity, type EntityConstructor, type EntityLinkResolver } from '@entifix/core';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
import { ConfigurationRepositoryTag } from './config.repository.js';
|
|
4
|
+
import { EntityRepositoryTag } from './entity.repository.js';
|
|
5
|
+
declare const EntityLinkResolverTag_base: Context.TagClass<EntityLinkResolverTag, "EntityLinkResolverTag", EntityLinkResolver>;
|
|
6
|
+
/**
|
|
7
|
+
* DI tag for the {@link EntityLinkResolver} an environment provides so entity
|
|
8
|
+
* links can materialize their targets. The composition root supplies a resolver
|
|
9
|
+
* backed by the appropriate adapters (REST on the web, Mongo on the backend),
|
|
10
|
+
* and use-cases that follow links yield this tag rather than depending on any
|
|
11
|
+
* concrete repository.
|
|
12
|
+
*/
|
|
13
|
+
export declare class EntityLinkResolverTag extends EntityLinkResolverTag_base {
|
|
14
|
+
}
|
|
15
|
+
/** Maps a link target constructor to the repository adapter that loads it. */
|
|
16
|
+
export type EntityLinkRegistration = readonly [
|
|
17
|
+
EntityConstructor<Entity>,
|
|
18
|
+
Context.Context<EntityRepositoryTag>
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Assembles an {@link EntityLinkResolverTag} context from repository adapters
|
|
22
|
+
* that already exist. Each registration's `get` is closed over the
|
|
23
|
+
* configuration context, so the resulting resolver effects have no outstanding
|
|
24
|
+
* requirements.
|
|
25
|
+
*
|
|
26
|
+
* It lives here, beside the tag, rather than with React's `useEntityLinkResolver`
|
|
27
|
+
* — which is only a `useMemo` around it — because nothing about it is React.
|
|
28
|
+
* A server component that imported it from the React package would pull that
|
|
29
|
+
* package's whole barrel, hooks included, and Next rejects `useState` reaching
|
|
30
|
+
* a server module. Framework-free code belongs at a framework-free layer.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createEntityLinkResolver(configurationStore: Context.Context<ConfigurationRepositoryTag>, registrations: ReadonlyArray<EntityLinkRegistration>): Context.Context<EntityLinkResolverTag>;
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=entity-link-resolver.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-link-resolver.repository.d.ts","sourceRoot":"","sources":["../../src/repository/entity-link-resolver.repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAU,MAAM,QAAQ,CAAC;AAEzC,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;;AAE7D;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,0BAGxC;CAAG;AAEN,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAAG,SAAS;IAC5C,iBAAiB,CAAC,MAAM,CAAC;IACzB,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC;CACrC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,kBAAkB,EAAE,OAAO,CAAC,OAAO,CAAC,0BAA0B,CAAC,EAC/D,aAAa,EAAE,aAAa,CAAC,sBAAsB,CAAC,GACnD,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAiCxC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { EntifixLogicError } from '@entifix/core';
|
|
2
|
+
import { Context, Effect } from 'effect';
|
|
3
|
+
import { EntityRepositoryTag } from './entity.repository.js';
|
|
4
|
+
/**
|
|
5
|
+
* DI tag for the {@link EntityLinkResolver} an environment provides so entity
|
|
6
|
+
* links can materialize their targets. The composition root supplies a resolver
|
|
7
|
+
* backed by the appropriate adapters (REST on the web, Mongo on the backend),
|
|
8
|
+
* and use-cases that follow links yield this tag rather than depending on any
|
|
9
|
+
* concrete repository.
|
|
10
|
+
*/ export class EntityLinkResolverTag extends Context.Tag('EntityLinkResolverTag')() {
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Assembles an {@link EntityLinkResolverTag} context from repository adapters
|
|
14
|
+
* that already exist. Each registration's `get` is closed over the
|
|
15
|
+
* configuration context, so the resulting resolver effects have no outstanding
|
|
16
|
+
* requirements.
|
|
17
|
+
*
|
|
18
|
+
* It lives here, beside the tag, rather than with React's `useEntityLinkResolver`
|
|
19
|
+
* — which is only a `useMemo` around it — because nothing about it is React.
|
|
20
|
+
* A server component that imported it from the React package would pull that
|
|
21
|
+
* package's whole barrel, hooks included, and Next rejects `useState` reaching
|
|
22
|
+
* a server module. Framework-free code belongs at a framework-free layer.
|
|
23
|
+
*/ export function createEntityLinkResolver(configurationStore, registrations) {
|
|
24
|
+
const repositoryByConstructor = new Map(registrations.map(([entityConstructor, repositoryContext])=>[
|
|
25
|
+
entityConstructor,
|
|
26
|
+
Context.get(repositoryContext, EntityRepositoryTag)
|
|
27
|
+
]));
|
|
28
|
+
const resolver = {
|
|
29
|
+
resolve (entityConstructor, id) {
|
|
30
|
+
const repository = repositoryByConstructor.get(entityConstructor);
|
|
31
|
+
if (!repository) {
|
|
32
|
+
return Effect.fail(new EntifixLogicError(`No link resolver registered for entity "${entityConstructor.name}"`, undefined, {
|
|
33
|
+
entity: entityConstructor.name
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
return Effect.provide(repository.get(id), configurationStore);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
return Context.make(EntityLinkResolverTag, resolver);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//# sourceMappingURL=entity-link-resolver.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/repository/entity-link-resolver.repository.ts"],"sourcesContent":["import {\n type EntifixError,\n EntifixLogicError,\n type Entity,\n type EntityConstructor,\n type EntityLinkResolver,\n} from '@entifix/core';\nimport { Context, Effect } from 'effect';\n\nimport { ConfigurationRepositoryTag } from './config.repository.js';\nimport { EntityRepositoryTag } from './entity.repository.js';\n\n/**\n * DI tag for the {@link EntityLinkResolver} an environment provides so entity\n * links can materialize their targets. The composition root supplies a resolver\n * backed by the appropriate adapters (REST on the web, Mongo on the backend),\n * and use-cases that follow links yield this tag rather than depending on any\n * concrete repository.\n */\nexport class EntityLinkResolverTag extends Context.Tag('EntityLinkResolverTag')<\n EntityLinkResolverTag,\n EntityLinkResolver\n>() {}\n\n/** Maps a link target constructor to the repository adapter that loads it. */\nexport type EntityLinkRegistration = readonly [\n EntityConstructor<Entity>,\n Context.Context<EntityRepositoryTag>,\n];\n\n/**\n * Assembles an {@link EntityLinkResolverTag} context from repository adapters\n * that already exist. Each registration's `get` is closed over the\n * configuration context, so the resulting resolver effects have no outstanding\n * requirements.\n *\n * It lives here, beside the tag, rather than with React's `useEntityLinkResolver`\n * — which is only a `useMemo` around it — because nothing about it is React.\n * A server component that imported it from the React package would pull that\n * package's whole barrel, hooks included, and Next rejects `useState` reaching\n * a server module. Framework-free code belongs at a framework-free layer.\n */\nexport function createEntityLinkResolver(\n configurationStore: Context.Context<ConfigurationRepositoryTag>,\n registrations: ReadonlyArray<EntityLinkRegistration>,\n): Context.Context<EntityLinkResolverTag> {\n const repositoryByConstructor = new Map(\n registrations.map(\n ([entityConstructor, repositoryContext]) =>\n [\n entityConstructor,\n Context.get(repositoryContext, EntityRepositoryTag),\n ] as const,\n ),\n );\n\n const resolver: EntityLinkResolver = {\n resolve(entityConstructor, id) {\n const repository = repositoryByConstructor.get(\n entityConstructor as EntityConstructor<Entity>,\n );\n if (!repository) {\n return Effect.fail(\n new EntifixLogicError(\n `No link resolver registered for entity \"${entityConstructor.name}\"`,\n undefined,\n { entity: entityConstructor.name },\n ),\n );\n }\n return Effect.provide(\n repository.get(id),\n configurationStore,\n ) as Effect.Effect<never, EntifixError>;\n },\n };\n\n return Context.make(EntityLinkResolverTag, resolver);\n}\n"],"names":["EntifixLogicError","Context","Effect","EntityRepositoryTag","EntityLinkResolverTag","Tag","createEntityLinkResolver","configurationStore","registrations","repositoryByConstructor","Map","map","entityConstructor","repositoryContext","get","resolver","resolve","id","repository","fail","name","undefined","entity","provide","make"],"mappings":"AAAA,SAEEA,iBAAiB,QAIZ,gBAAgB;AACvB,SAASC,OAAO,EAAEC,MAAM,QAAQ,SAAS;AAGzC,SAASC,mBAAmB,QAAQ,yBAAyB;AAE7D;;;;;;CAMC,GACD,OAAO,MAAMC,8BAA8BH,QAAQI,GAAG,CAAC;AAGlD;AAQL;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,yBACdC,kBAA+D,EAC/DC,aAAoD;IAEpD,MAAMC,0BAA0B,IAAIC,IAClCF,cAAcG,GAAG,CACf,CAAC,CAACC,mBAAmBC,kBAAkB,GACrC;YACED;YACAX,QAAQa,GAAG,CAACD,mBAAmBV;SAChC;IAIP,MAAMY,WAA+B;QACnCC,SAAQJ,iBAAiB,EAAEK,EAAE;YAC3B,MAAMC,aAAaT,wBAAwBK,GAAG,CAC5CF;YAEF,IAAI,CAACM,YAAY;gBACf,OAAOhB,OAAOiB,IAAI,CAChB,IAAInB,kBACF,CAAC,wCAAwC,EAAEY,kBAAkBQ,IAAI,CAAC,CAAC,CAAC,EACpEC,WACA;oBAAEC,QAAQV,kBAAkBQ,IAAI;gBAAC;YAGvC;YACA,OAAOlB,OAAOqB,OAAO,CACnBL,WAAWJ,GAAG,CAACG,KACfV;QAEJ;IACF;IAEA,OAAON,QAAQuB,IAAI,CAACpB,uBAAuBW;AAC7C"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Entity, EntityId, EntityLoadRequest } from '@entifix/core';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
declare const EntityIdTag_base: Context.TagClass<EntityIdTag, "EntityIdTag", EntityId>;
|
|
4
|
+
export declare class EntityIdTag extends EntityIdTag_base {
|
|
5
|
+
}
|
|
6
|
+
declare const EntityLoadRequestTag_base: Context.TagClass<EntityLoadRequestTag, "EntityLoadRequestTag", EntityLoadRequest<Entity>>;
|
|
7
|
+
export declare class EntityLoadRequestTag extends EntityLoadRequestTag_base {
|
|
8
|
+
}
|
|
9
|
+
declare const EntityTag_base: Context.TagClass<EntityTag, "EntityTag", Entity>;
|
|
10
|
+
export declare class EntityTag extends EntityTag_base {
|
|
11
|
+
}
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=entity-mixins.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-mixins.repository.d.ts","sourceRoot":"","sources":["../../src/repository/entity-mixins.repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;;AAEjC,qBAAa,WAAY,SAAQ,gBAG9B;CAAG;;AAEN,qBAAa,oBAAqB,SAAQ,yBAGvC;CAAG;;AAEN,qBAAa,SAAU,SAAQ,cAA6C;CAAG"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Context } from 'effect';
|
|
2
|
+
export class EntityIdTag extends Context.Tag('EntityIdTag')() {
|
|
3
|
+
}
|
|
4
|
+
export class EntityLoadRequestTag extends Context.Tag('EntityLoadRequestTag')() {
|
|
5
|
+
}
|
|
6
|
+
export class EntityTag extends Context.Tag('EntityTag')() {
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=entity-mixins.repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/repository/entity-mixins.repository.ts"],"sourcesContent":["import { Entity, EntityId, EntityLoadRequest } from '@entifix/core';\nimport { Context } from 'effect';\n\nexport class EntityIdTag extends Context.Tag('EntityIdTag')<\n EntityIdTag,\n EntityId\n>() {}\n\nexport class EntityLoadRequestTag extends Context.Tag('EntityLoadRequestTag')<\n EntityLoadRequestTag,\n EntityLoadRequest\n>() {}\n\nexport class EntityTag extends Context.Tag('EntityTag')<EntityTag, Entity>() {}\n"],"names":["Context","EntityIdTag","Tag","EntityLoadRequestTag","EntityTag"],"mappings":"AACA,SAASA,OAAO,QAAQ,SAAS;AAEjC,OAAO,MAAMC,oBAAoBD,QAAQE,GAAG,CAAC;AAGxC;AAEL,OAAO,MAAMC,6BAA6BH,QAAQE,GAAG,CAAC;AAGjD;AAEL,OAAO,MAAME,kBAAkBJ,QAAQE,GAAG,CAAC;AAAmC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EntifixError, Entity, EntityId, EntityLoadRequest, EntityPage } from '@entifix/core';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
import { Effect } from 'effect/Effect';
|
|
4
|
+
import { ConfigurationRepositoryTag } from './config.repository.js';
|
|
5
|
+
export interface EntityRepository {
|
|
6
|
+
get<TEntity extends Entity>(id: EntityId): Effect<TEntity, EntifixError, ConfigurationRepositoryTag>;
|
|
7
|
+
load<TEntity extends Entity>(request: EntityLoadRequest<TEntity>): Effect<EntityPage<TEntity>, EntifixError, ConfigurationRepositoryTag>;
|
|
8
|
+
save<TEntity extends Entity>(entity: TEntity): Effect<TEntity, EntifixError, ConfigurationRepositoryTag>;
|
|
9
|
+
delete<TEntity extends Entity>(entityOrId: EntityId | TEntity): Effect<void, EntifixError, ConfigurationRepositoryTag>;
|
|
10
|
+
}
|
|
11
|
+
declare const EntityRepositoryTag_base: Context.TagClass<EntityRepositoryTag, "EntityRepositoryTag", EntityRepository>;
|
|
12
|
+
export declare class EntityRepositoryTag extends EntityRepositoryTag_base {
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=entity.repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.repository.d.ts","sourceRoot":"","sources":["../../src/repository/entity.repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,iBAAiB,EACjB,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B,GAAG,CAAC,OAAO,SAAS,MAAM,EACxB,EAAE,EAAE,QAAQ,GACX,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,0BAA0B,CAAC,CAAC;IAC7D,IAAI,CAAC,OAAO,SAAS,MAAM,EACzB,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAClC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,0BAA0B,CAAC,CAAC;IACzE,IAAI,CAAC,OAAO,SAAS,MAAM,EACzB,MAAM,EAAE,OAAO,GACd,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,0BAA0B,CAAC,CAAC;IAC7D,MAAM,CAAC,OAAO,SAAS,MAAM,EAC3B,UAAU,EAAE,QAAQ,GAAG,OAAO,GAC7B,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,0BAA0B,CAAC,CAAC;CAC3D;;AAED,qBAAa,mBAAoB,SAAQ,wBAGtC;CAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/repository/entity.repository.ts"],"sourcesContent":["import type {\n EntifixError,\n Entity,\n EntityId,\n EntityLoadRequest,\n EntityPage,\n} from '@entifix/core';\nimport { Context } from 'effect';\nimport { Effect } from 'effect/Effect';\n\nimport { ConfigurationRepositoryTag } from './config.repository.js';\n\nexport interface EntityRepository {\n get<TEntity extends Entity>(\n id: EntityId,\n ): Effect<TEntity, EntifixError, ConfigurationRepositoryTag>;\n load<TEntity extends Entity>(\n request: EntityLoadRequest<TEntity>,\n ): Effect<EntityPage<TEntity>, EntifixError, ConfigurationRepositoryTag>;\n save<TEntity extends Entity>(\n entity: TEntity,\n ): Effect<TEntity, EntifixError, ConfigurationRepositoryTag>;\n delete<TEntity extends Entity>(\n entityOrId: EntityId | TEntity,\n ): Effect<void, EntifixError, ConfigurationRepositoryTag>;\n}\n\nexport class EntityRepositoryTag extends Context.Tag('EntityRepositoryTag')<\n EntityRepositoryTag,\n EntityRepository\n>() {}\n"],"names":["Context","EntityRepositoryTag","Tag"],"mappings":"AAOA,SAASA,OAAO,QAAQ,SAAS;AAoBjC,OAAO,MAAMC,4BAA4BD,QAAQE,GAAG,CAAC;AAGhD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EntifixError } from '@entifix/core';
|
|
2
|
+
import { Context } from 'effect';
|
|
3
|
+
import { Effect } from 'effect/Effect';
|
|
4
|
+
/**
|
|
5
|
+
* What a token is for. Namespacing them keeps a token minted for one purpose
|
|
6
|
+
* from being redeemed as another — a password-reset token must not double as
|
|
7
|
+
* an email-verification token.
|
|
8
|
+
*/
|
|
9
|
+
export type TokenPurpose = string;
|
|
10
|
+
/**
|
|
11
|
+
* Short-lived, single-use tokens handed to a user out of band (a link in an
|
|
12
|
+
* email) and redeemed once.
|
|
13
|
+
*
|
|
14
|
+
* The store keeps only a HASH of the token, exactly as it would a password: the
|
|
15
|
+
* plaintext exists in the response to {@link OneTimeTokenStore.issue} and in the
|
|
16
|
+
* message sent to the user, and nowhere else. A store dump then yields nothing
|
|
17
|
+
* an attacker can redeem.
|
|
18
|
+
*/
|
|
19
|
+
export interface OneTimeTokenStore {
|
|
20
|
+
/**
|
|
21
|
+
* Mint a token for `subject`, returning the plaintext exactly once.
|
|
22
|
+
*
|
|
23
|
+
* The caller must send it onward immediately; there is no way to read it back.
|
|
24
|
+
*/
|
|
25
|
+
issue(purpose: TokenPurpose, subject: string, ttlSeconds: number): Effect<string, EntifixError>;
|
|
26
|
+
/**
|
|
27
|
+
* Redeem a token, returning the subject it was minted for.
|
|
28
|
+
*
|
|
29
|
+
* Consuming deletes it, so a link that leaks later — from a browser history, a
|
|
30
|
+
* mail archive, a proxy log — is already spent. Fails when the token is
|
|
31
|
+
* unknown, expired, or already used; the caller must not distinguish those.
|
|
32
|
+
*/
|
|
33
|
+
consume(purpose: TokenPurpose, token: string): Effect<string, EntifixError>;
|
|
34
|
+
}
|
|
35
|
+
declare const OneTimeTokenStoreTag_base: Context.TagClass<OneTimeTokenStoreTag, "OneTimeTokenStoreTag", OneTimeTokenStore>;
|
|
36
|
+
/** DI tag the composition root binds to a concrete {@link OneTimeTokenStore}. */
|
|
37
|
+
export declare class OneTimeTokenStoreTag extends OneTimeTokenStoreTag_base {
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=one-time-token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"one-time-token-store.d.ts","sourceRoot":"","sources":["../../src/sessions/one-time-token-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAEvC;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,KAAK,CACH,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAChC;;;;;;OAMG;IACH,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC7E;;AAED,iFAAiF;AACjF,qBAAa,oBAAqB,SAAQ,yBAGvC;CAAG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/sessions/one-time-token-store.ts"],"sourcesContent":["import type { EntifixError } from '@entifix/core';\nimport { Context } from 'effect';\nimport { Effect } from 'effect/Effect';\n\n/**\n * What a token is for. Namespacing them keeps a token minted for one purpose\n * from being redeemed as another — a password-reset token must not double as\n * an email-verification token.\n */\nexport type TokenPurpose = string;\n\n/**\n * Short-lived, single-use tokens handed to a user out of band (a link in an\n * email) and redeemed once.\n *\n * The store keeps only a HASH of the token, exactly as it would a password: the\n * plaintext exists in the response to {@link OneTimeTokenStore.issue} and in the\n * message sent to the user, and nowhere else. A store dump then yields nothing\n * an attacker can redeem.\n */\nexport interface OneTimeTokenStore {\n /**\n * Mint a token for `subject`, returning the plaintext exactly once.\n *\n * The caller must send it onward immediately; there is no way to read it back.\n */\n issue(\n purpose: TokenPurpose,\n subject: string,\n ttlSeconds: number,\n ): Effect<string, EntifixError>;\n /**\n * Redeem a token, returning the subject it was minted for.\n *\n * Consuming deletes it, so a link that leaks later — from a browser history, a\n * mail archive, a proxy log — is already spent. Fails when the token is\n * unknown, expired, or already used; the caller must not distinguish those.\n */\n consume(purpose: TokenPurpose, token: string): Effect<string, EntifixError>;\n}\n\n/** DI tag the composition root binds to a concrete {@link OneTimeTokenStore}. */\nexport class OneTimeTokenStoreTag extends Context.Tag('OneTimeTokenStoreTag')<\n OneTimeTokenStoreTag,\n OneTimeTokenStore\n>() {}\n"],"names":["Context","OneTimeTokenStoreTag","Tag"],"mappings":"AACA,SAASA,OAAO,QAAQ,SAAS;AAwCjC,+EAA+E,GAC/E,OAAO,MAAMC,6BAA6BD,QAAQE,GAAG,CAAC;AAGjD"}
|