@softacus-software/elwis-mcp-core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/README.md +120 -0
  2. package/dist/cjs/app-scope.js +81 -0
  3. package/dist/cjs/app-scope.js.map +1 -0
  4. package/dist/cjs/conformance/host.conformance.js +98 -0
  5. package/dist/cjs/conformance/host.conformance.js.map +1 -0
  6. package/dist/cjs/conformance/read-port.conformance.js +131 -0
  7. package/dist/cjs/conformance/read-port.conformance.js.map +1 -0
  8. package/dist/cjs/errors.js +74 -0
  9. package/dist/cjs/errors.js.map +1 -0
  10. package/dist/cjs/host/elwis-jwt-verifier.js +40 -0
  11. package/dist/cjs/host/elwis-jwt-verifier.js.map +1 -0
  12. package/dist/cjs/host/file-link-store.js +60 -0
  13. package/dist/cjs/host/file-link-store.js.map +1 -0
  14. package/dist/cjs/host/identity.js +3 -0
  15. package/dist/cjs/host/identity.js.map +1 -0
  16. package/dist/cjs/host/index.js +24 -0
  17. package/dist/cjs/host/index.js.map +1 -0
  18. package/dist/cjs/host/link-ticket.js +59 -0
  19. package/dist/cjs/host/link-ticket.js.map +1 -0
  20. package/dist/cjs/host/oidc-token.js +96 -0
  21. package/dist/cjs/host/oidc-token.js.map +1 -0
  22. package/dist/cjs/host/oidc-verifier.js +67 -0
  23. package/dist/cjs/host/oidc-verifier.js.map +1 -0
  24. package/dist/cjs/host/serve-http.js +282 -0
  25. package/dist/cjs/host/serve-http.js.map +1 -0
  26. package/dist/cjs/host/session-broker.js +84 -0
  27. package/dist/cjs/host/session-broker.js.map +1 -0
  28. package/dist/cjs/index.js +43 -0
  29. package/dist/cjs/index.js.map +1 -0
  30. package/dist/cjs/package.json +3 -0
  31. package/dist/cjs/plan/expand.js +297 -0
  32. package/dist/cjs/plan/expand.js.map +1 -0
  33. package/dist/cjs/plan/resolve-existing.js +58 -0
  34. package/dist/cjs/plan/resolve-existing.js.map +1 -0
  35. package/dist/cjs/plan/split.js +92 -0
  36. package/dist/cjs/plan/split.js.map +1 -0
  37. package/dist/cjs/plan/target-state.js +120 -0
  38. package/dist/cjs/plan/target-state.js.map +1 -0
  39. package/dist/cjs/ports/elwis-api.port.js +15 -0
  40. package/dist/cjs/ports/elwis-api.port.js.map +1 -0
  41. package/dist/cjs/resolve.js +89 -0
  42. package/dist/cjs/resolve.js.map +1 -0
  43. package/dist/esm/app-scope.d.ts +26 -0
  44. package/dist/esm/app-scope.d.ts.map +1 -0
  45. package/dist/esm/app-scope.js +76 -0
  46. package/dist/esm/app-scope.js.map +1 -0
  47. package/dist/esm/conformance/host.conformance.d.ts +3 -0
  48. package/dist/esm/conformance/host.conformance.d.ts.map +1 -0
  49. package/dist/esm/conformance/host.conformance.js +95 -0
  50. package/dist/esm/conformance/host.conformance.js.map +1 -0
  51. package/dist/esm/conformance/read-port.conformance.d.ts +24 -0
  52. package/dist/esm/conformance/read-port.conformance.d.ts.map +1 -0
  53. package/dist/esm/conformance/read-port.conformance.js +127 -0
  54. package/dist/esm/conformance/read-port.conformance.js.map +1 -0
  55. package/dist/esm/errors.d.ts +40 -0
  56. package/dist/esm/errors.d.ts.map +1 -0
  57. package/dist/esm/errors.js +63 -0
  58. package/dist/esm/errors.js.map +1 -0
  59. package/dist/esm/host/elwis-jwt-verifier.d.ts +9 -0
  60. package/dist/esm/host/elwis-jwt-verifier.d.ts.map +1 -0
  61. package/dist/esm/host/elwis-jwt-verifier.js +37 -0
  62. package/dist/esm/host/elwis-jwt-verifier.js.map +1 -0
  63. package/dist/esm/host/file-link-store.d.ts +13 -0
  64. package/dist/esm/host/file-link-store.d.ts.map +1 -0
  65. package/dist/esm/host/file-link-store.js +56 -0
  66. package/dist/esm/host/file-link-store.js.map +1 -0
  67. package/dist/esm/host/identity.d.ts +13 -0
  68. package/dist/esm/host/identity.d.ts.map +1 -0
  69. package/dist/esm/host/identity.js +2 -0
  70. package/dist/esm/host/identity.js.map +1 -0
  71. package/dist/esm/host/index.d.ts +15 -0
  72. package/dist/esm/host/index.d.ts.map +1 -0
  73. package/dist/esm/host/index.js +9 -0
  74. package/dist/esm/host/index.js.map +1 -0
  75. package/dist/esm/host/link-ticket.d.ts +17 -0
  76. package/dist/esm/host/link-ticket.d.ts.map +1 -0
  77. package/dist/esm/host/link-ticket.js +54 -0
  78. package/dist/esm/host/link-ticket.js.map +1 -0
  79. package/dist/esm/host/oidc-token.d.ts +20 -0
  80. package/dist/esm/host/oidc-token.d.ts.map +1 -0
  81. package/dist/esm/host/oidc-token.js +93 -0
  82. package/dist/esm/host/oidc-token.js.map +1 -0
  83. package/dist/esm/host/oidc-verifier.d.ts +14 -0
  84. package/dist/esm/host/oidc-verifier.d.ts.map +1 -0
  85. package/dist/esm/host/oidc-verifier.js +63 -0
  86. package/dist/esm/host/oidc-verifier.js.map +1 -0
  87. package/dist/esm/host/serve-http.d.ts +42 -0
  88. package/dist/esm/host/serve-http.d.ts.map +1 -0
  89. package/dist/esm/host/serve-http.js +278 -0
  90. package/dist/esm/host/serve-http.js.map +1 -0
  91. package/dist/esm/host/session-broker.d.ts +45 -0
  92. package/dist/esm/host/session-broker.d.ts.map +1 -0
  93. package/dist/esm/host/session-broker.js +77 -0
  94. package/dist/esm/host/session-broker.js.map +1 -0
  95. package/dist/esm/index.d.ts +18 -0
  96. package/dist/esm/index.d.ts.map +1 -0
  97. package/dist/esm/index.js +10 -0
  98. package/dist/esm/index.js.map +1 -0
  99. package/dist/esm/package.json +3 -0
  100. package/dist/esm/plan/expand.d.ts +37 -0
  101. package/dist/esm/plan/expand.d.ts.map +1 -0
  102. package/dist/esm/plan/expand.js +294 -0
  103. package/dist/esm/plan/expand.js.map +1 -0
  104. package/dist/esm/plan/resolve-existing.d.ts +16 -0
  105. package/dist/esm/plan/resolve-existing.d.ts.map +1 -0
  106. package/dist/esm/plan/resolve-existing.js +55 -0
  107. package/dist/esm/plan/resolve-existing.js.map +1 -0
  108. package/dist/esm/plan/split.d.ts +14 -0
  109. package/dist/esm/plan/split.d.ts.map +1 -0
  110. package/dist/esm/plan/split.js +88 -0
  111. package/dist/esm/plan/split.js.map +1 -0
  112. package/dist/esm/plan/target-state.d.ts +27 -0
  113. package/dist/esm/plan/target-state.d.ts.map +1 -0
  114. package/dist/esm/plan/target-state.js +114 -0
  115. package/dist/esm/plan/target-state.js.map +1 -0
  116. package/dist/esm/ports/elwis-api.port.d.ts +56 -0
  117. package/dist/esm/ports/elwis-api.port.d.ts.map +1 -0
  118. package/dist/esm/ports/elwis-api.port.js +12 -0
  119. package/dist/esm/ports/elwis-api.port.js.map +1 -0
  120. package/dist/esm/resolve.d.ts +23 -0
  121. package/dist/esm/resolve.d.ts.map +1 -0
  122. package/dist/esm/resolve.js +79 -0
  123. package/dist/esm/resolve.js.map +1 -0
  124. package/package.json +55 -0
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @softacus-software/elwis-mcp-core
2
+
3
+ Shared ELWIS domain logic — entity resolution, application scoping, and the access contract —
4
+ consumed by the ELWIS backend assistant and by the ELWIS MCP servers, so all three run the
5
+ same code instead of three divergent copies.
6
+
7
+ This package contains **no transport**. It never opens a socket, reads an environment
8
+ variable, or knows what MCP is. Everything it does goes through a port that the consumer
9
+ implements.
10
+
11
+ ## The seam
12
+
13
+ Domain logic takes its data access as a parameter:
14
+
15
+ ```ts
16
+ ResolveEntity(api, kind, applicationId, nameOrAlias)
17
+ ```
18
+
19
+ `api` satisfies `ElwisReadPort`. The MCP servers implement it over HTTP; the backend
20
+ implements it in-process against tenant services. Same logic, different adapter.
21
+
22
+ ```
23
+ ElwisReadPort whoAmI · systemAttributes · list · getOne · listRecords
24
+ ElwisWritePort create · update · softDelete · createArtifact · updateArtifact ·
25
+ softDeleteArtifact · createLinks
26
+ ElwisApiPort both
27
+ ```
28
+
29
+ Only the read port is exercised by the current conformance suite. Write coverage lands with
30
+ the write slice.
31
+
32
+ ## Install and build
33
+
34
+ ```bash
35
+ npm install
36
+ npm run verify # build + tests + dual-module consumption check
37
+ ```
38
+
39
+ `npm run build` emits `dist/esm` and `dist/cjs`, each stamped with its own `type` field.
40
+
41
+ **TypeScript is pinned to `~5.9.3` on purpose.** The ELWIS backend is on 5.9 and the Data MCP
42
+ on 6.0; declarations are emitted by the lower compiler so both consumers can read them.
43
+
44
+ **`main` and `types` are set alongside `exports` on purpose.** The backend compiles with
45
+ `module: commonjs` and no `moduleResolution`, which defaults to node10 — and node10 ignores
46
+ `exports` entirely. Removing the legacy fields silently breaks that consumer.
47
+
48
+ ## The conformance suite
49
+
50
+ The point of this package is that two adapters behave identically. `RunReadPortConformance`
51
+ is how you prove it — the same cases run against the HTTP adapter and the in-process adapter.
52
+
53
+ ```ts
54
+ import { RunReadPortConformance } from '@softacus-software/elwis-mcp-core';
55
+
56
+ const results = await RunReadPortConformance(myAdapter, {
57
+ applicationId, applicationName, kind: 'data-type', knownEntityName, knownEntityId,
58
+ });
59
+ const failed = results.filter(r => !r.passed);
60
+ ```
61
+
62
+ It is framework-agnostic: no test runner is imported, cases return results rather than
63
+ asserting, so consumers can drive it from jest, node:test, or a script.
64
+
65
+ Every case carries a `why`. The load-bearing one is `excludes-soft-deleted`: the HTTP adapter
66
+ gets soft-delete filtering for free because it injects `deleted: null` into every filter, while
67
+ an adapter reading collections directly does not. Without that case, an in-process adapter
68
+ would quietly surface deleted entities into resolution and into user-facing pickers.
69
+
70
+ The suite is itself tested: `test/conformance.test.ts` runs it against a deliberately broken
71
+ adapter and asserts that it fails.
72
+
73
+ ## Resolution behaviour
74
+
75
+ `ResolveEntity` and `ResolveOneId` are ported verbatim from `elwis-data-mcp`, including the
76
+ `$or`-to-name fallback for backends that reject `$or` with a 400. That fidelity is deliberate:
77
+ the extraction has to be provably behaviour-neutral before anything consumes it.
78
+
79
+ `ResolveOneStrict` is new and nothing calls it yet. It exists because the current assistant
80
+ answers a missed lookup with a raw validator message:
81
+
82
+ ```
83
+ That answer won't work — Invalid string: must match pattern /^[a-f\d]{24}$/i
84
+ ```
85
+
86
+ `ResolveOneStrict` instead throws `NotFoundError` carrying the entities that *do* exist, so a
87
+ caller can say "no data type called X here — did you mean one of these, or shall I create it?"
88
+ `AmbiguityError` covers the multiple-match case.
89
+
90
+ ## HTTP host (`./host`)
91
+
92
+ The `./host` subpath (`ServeMcpHttp`, `ElwisJwtVerifier`) is the one exception to "no
93
+ transport" above: it is an actual `node:http` listener. `elwis-docs-mcp` and `elwis-data-mcp`
94
+ use it to serve MCP over HTTP with a bearer token instead of stdio.
95
+
96
+ It binds `127.0.0.1` unless the caller passes a different `host`; accepts only `POST /mcp` (a
97
+ `GET` is answered `405 Allow: POST` — neither server sends server-initiated messages, so the
98
+ standing `text/event-stream` a `GET` would otherwise open carries nothing and exists only to
99
+ be held open); and caps both concurrent token verifications (`maxConcurrentVerifications`) and
100
+ concurrent live sessions (`maxConcurrentSessions`), so a caller with no valid credential at all
101
+ cannot force unbounded verification work, and a caller who does verify cannot hold an unbounded
102
+ number of built servers open. Neither cap, nor the bind address, is read from an environment
103
+ variable by this package itself — each server that embeds `ServeMcpHttp` wires its own env
104
+ vars for these and documents them in its own README's operator section.
105
+
106
+ This is a library entry point, not a runnable server on its own: nothing in this package
107
+ listens by itself outside of a test.
108
+
109
+ ## Errors
110
+
111
+ `ElwisApiError` carries `status`, so shared logic can branch on an HTTP status without
112
+ importing a transport. `IsElwisApiError` also accepts the Data MCP's existing `ElwisHttpError`
113
+ by shape, so the standalone server does not have to change its error class to adopt this
114
+ package.
115
+
116
+ ## Status
117
+
118
+ Phase 1 of the capability-layer plan: extracted, tested, buildable, with no consumer yet.
119
+ Phase 2 switches `elwis-data-mcp` over and deletes its copies — until that lands, the logic
120
+ exists twice, which is the one thing this package is meant to prevent.
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppScope = void 0;
4
+ exports.FilterApplicationId = FilterApplicationId;
5
+ const errors_js_1 = require("./errors.js");
6
+ const resolve_js_1 = require("./resolve.js");
7
+ function FilterApplicationId(filter) {
8
+ const value = filter.applicationId;
9
+ if (typeof value === 'string')
10
+ return value;
11
+ if (value && typeof value === 'object' && typeof value.$oid === 'string') {
12
+ return value.$oid;
13
+ }
14
+ return undefined;
15
+ }
16
+ class AppScope {
17
+ api;
18
+ entries;
19
+ expectedTenant;
20
+ ids = null;
21
+ tenantVerified = false;
22
+ constructor(api, entries, expectedTenant) {
23
+ this.api = api;
24
+ this.entries = entries;
25
+ this.expectedTenant = expectedTenant;
26
+ }
27
+ get restricted() {
28
+ return this.entries.length > 0;
29
+ }
30
+ identity() {
31
+ return this.api.whoAmI().catch(() => null);
32
+ }
33
+ describe() {
34
+ return { restricted: this.restricted, entries: [...this.entries] };
35
+ }
36
+ async assertTenant() {
37
+ if (!this.expectedTenant || this.tenantVerified)
38
+ return;
39
+ const identity = await this.identity();
40
+ if (identity?.tenantId !== this.expectedTenant) {
41
+ throw new errors_js_1.TenantMismatchError(this.expectedTenant, identity?.tenantId ?? null);
42
+ }
43
+ this.tenantVerified = true;
44
+ }
45
+ async allowedIds() {
46
+ if (!this.restricted)
47
+ return null;
48
+ if (this.ids)
49
+ return this.ids;
50
+ const ids = new Set();
51
+ for (const entry of this.entries) {
52
+ if ((0, resolve_js_1.IsObjectId)(entry)) {
53
+ ids.add(entry);
54
+ continue;
55
+ }
56
+ const rows = await this.api.list('application', { name: entry });
57
+ for (const row of rows)
58
+ ids.add(row.id);
59
+ }
60
+ this.ids = ids;
61
+ return ids;
62
+ }
63
+ async assertAllowed(applicationId) {
64
+ await this.assertTenant();
65
+ const ids = await this.allowedIds();
66
+ if (ids && !ids.has(applicationId))
67
+ throw new errors_js_1.AppScopeError(applicationId);
68
+ }
69
+ async assertListAllowed(kind, filter) {
70
+ await this.assertTenant();
71
+ if (!this.restricted || kind === 'application')
72
+ return;
73
+ const applicationId = FilterApplicationId(filter);
74
+ if (!applicationId) {
75
+ throw new errors_js_1.AppScopeError('unspecified, when an application allowlist is set, list calls require an applicationId filter');
76
+ }
77
+ await this.assertAllowed(applicationId);
78
+ }
79
+ }
80
+ exports.AppScope = AppScope;
81
+ //# sourceMappingURL=app-scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-scope.js","sourceRoot":"","sources":["../../src/app-scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RunHostConformance = RunHostConformance;
4
+ const serve_http_js_1 = require("../host/serve-http.js");
5
+ function assert(condition, message) {
6
+ if (!condition)
7
+ throw new Error(message);
8
+ }
9
+ const FETCH_TIMEOUT_MS = 5_000;
10
+ function ParseJsonRpcFrame(contentType, body) {
11
+ if (contentType?.includes('text/event-stream')) {
12
+ const dataLine = body.split('\n').find((line) => line.startsWith('data:'));
13
+ assert(dataLine, `expected an SSE data frame in the response, got: ${body}`);
14
+ return JSON.parse(dataLine.slice('data:'.length).trim());
15
+ }
16
+ return JSON.parse(body);
17
+ }
18
+ function AdvertisedMetadataPath(header) {
19
+ const match = /resource_metadata="([^"]+)"/.exec(header ?? '');
20
+ const url = match?.[1];
21
+ assert(url, 'the challenge did not advertise a resource_metadata url');
22
+ return new URL(url).pathname;
23
+ }
24
+ const HOST_CONFORMANCE = [
25
+ {
26
+ id: 'unauthenticated-call-challenged',
27
+ title: 'a call with no bearer token is challenged, not silently served',
28
+ why: 'A server that skips the challenge on a missing token would serve tool calls to anyone who can reach the port, defeating authentication entirely.',
29
+ async run(base) {
30
+ const res = await fetch(`${base}/mcp`, { method: 'POST', body: '{}', signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
31
+ assert(res.status === 401, `expected 401 for an unauthenticated call, got ${res.status}`);
32
+ AdvertisedMetadataPath(res.headers.get('www-authenticate'));
33
+ },
34
+ },
35
+ {
36
+ id: 'metadata-discoverable-at-advertised-url',
37
+ title: 'the url the challenge advertises actually serves resource metadata',
38
+ why: 'A client discovers where to authenticate by following resource_metadata from the challenge. If that route is missing or disconnected from the advertised url, discovery dead-ends and no client can ever authenticate.',
39
+ async run(base) {
40
+ const challenged = await fetch(`${base}/mcp`, { method: 'POST', body: '{}', signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
41
+ const metadataPath = AdvertisedMetadataPath(challenged.headers.get('www-authenticate'));
42
+ const metadata = await fetch(`${base}${metadataPath}`, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
43
+ assert(metadata.status === 200, `expected 200 fetching the advertised metadata url, got ${metadata.status}`);
44
+ const body = await metadata.json();
45
+ assert(typeof body.resource === 'string', 'metadata document is missing a resource field');
46
+ assert(Array.isArray(body.authorization_servers), 'metadata document is missing authorization_servers');
47
+ },
48
+ },
49
+ {
50
+ id: 'authenticated-call-served',
51
+ title: 'a call bearing a token the verifier accepts is served',
52
+ why: 'This is the point of hosting a server at all: the transport must actually be wired to the factory-built server, not merely accept the connection and then never respond. A host that calls handleRequest without first connecting the server still answers 200 with SSE headers and then hangs, so the body must be read and parsed as JSON-RPC, not just the status code checked.',
53
+ async run(base) {
54
+ const listed = await fetch(`${base}/mcp`, {
55
+ method: 'POST',
56
+ headers: {
57
+ authorization: 'Bearer conformance-token',
58
+ 'content-type': 'application/json',
59
+ accept: 'application/json, text/event-stream',
60
+ },
61
+ body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }),
62
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
63
+ });
64
+ assert(listed.status === 200, `expected 200 for an authenticated call, got ${listed.status}`);
65
+ const body = await listed.text();
66
+ const frame = ParseJsonRpcFrame(listed.headers.get('content-type'), body);
67
+ assert(frame.jsonrpc === '2.0', `expected a jsonrpc 2.0 response, got: ${body}`);
68
+ assert(frame.id === 1, `expected the response to answer request id 1, got: ${body}`);
69
+ assert('result' in frame || 'error' in frame, `expected a result or error frame, got: ${body}`);
70
+ },
71
+ },
72
+ ];
73
+ async function RunHostConformance(factory, verifier) {
74
+ const handle = await (0, serve_http_js_1.ServeMcpHttp)({
75
+ factory,
76
+ verifier,
77
+ resource: 'http://127.0.0.1/mcp',
78
+ authorizationServers: ['https://id.example.com'],
79
+ port: 0,
80
+ });
81
+ try {
82
+ const base = `http://127.0.0.1:${handle.port}`;
83
+ const failures = [];
84
+ for (const hostCase of HOST_CONFORMANCE) {
85
+ try {
86
+ await hostCase.run(base);
87
+ }
88
+ catch (error) {
89
+ failures.push(`${hostCase.id}: ${error instanceof Error ? error.message : String(error)}`);
90
+ }
91
+ }
92
+ assert(failures.length === 0, `host conformance failed:\n${failures.join('\n')}`);
93
+ }
94
+ finally {
95
+ await handle.close();
96
+ }
97
+ }
98
+ //# sourceMappingURL=host.conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.conformance.js","sourceRoot":"","sources":["../../../src/conformance/host.conformance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.READ_PORT_CONFORMANCE = void 0;
4
+ exports.RunReadPortConformance = RunReadPortConformance;
5
+ const errors_js_1 = require("../errors.js");
6
+ const resolve_js_1 = require("../resolve.js");
7
+ function assert(condition, message) {
8
+ if (!condition)
9
+ throw new Error(message);
10
+ }
11
+ exports.READ_PORT_CONFORMANCE = [
12
+ {
13
+ id: 'excludes-soft-deleted',
14
+ title: 'list never returns soft-deleted rows',
15
+ why: 'The HTTP adapter injects deleted:null for free. An adapter reading collections directly does not, and would leak deleted entities into resolution and into pickers.',
16
+ async run(api, fx) {
17
+ const rows = await api.list(fx.kind, { applicationId: { $oid: fx.applicationId } }, 200);
18
+ const leaked = rows.filter((row) => row.deleted !== null && row.deleted !== undefined);
19
+ assert(leaked.length === 0, `list returned ${leaked.length} soft-deleted ${fx.kind} rows`);
20
+ },
21
+ },
22
+ {
23
+ id: 'scopes-by-application',
24
+ title: 'list honours an applicationId filter',
25
+ why: 'Application scoping is the only thing preventing cross-application reads.',
26
+ async run(api, fx) {
27
+ const rows = await api.list(fx.kind, { applicationId: { $oid: fx.applicationId } }, 200);
28
+ const foreign = rows.filter((row) => row.applicationId !== undefined && String(row.applicationId) !== fx.applicationId);
29
+ assert(foreign.length === 0, `list returned ${foreign.length} rows from another application`);
30
+ },
31
+ },
32
+ {
33
+ id: 'honours-limit',
34
+ title: 'list honours the limit argument',
35
+ why: 'Silent over-return breaks pagination honesty and can blow the model context.',
36
+ async run(api, fx) {
37
+ const rows = await api.list(fx.kind, { applicationId: { $oid: fx.applicationId } }, 1);
38
+ assert(rows.length <= 1, `limit 1 returned ${rows.length} rows`);
39
+ },
40
+ },
41
+ {
42
+ id: 'or-filter-or-400',
43
+ title: 'list either supports $or on name/alias or rejects it with status 400',
44
+ why: 'ResolveEntity falls back to a name-only lookup on 400. An adapter that throws an untyped error, or silently returns nothing, breaks alias resolution without any signal.',
45
+ async run(api, fx) {
46
+ try {
47
+ const rows = await api.list(fx.kind, {
48
+ applicationId: { $oid: fx.applicationId },
49
+ $or: [{ name: fx.knownEntityName }, { alias: fx.knownEntityName }],
50
+ });
51
+ assert(Array.isArray(rows), '$or lookup did not return an array');
52
+ }
53
+ catch (error) {
54
+ assert((0, errors_js_1.ErrorStatus)(error) === 400, `$or lookup failed with a non-400, untyped error: ${String(error)}`);
55
+ }
56
+ },
57
+ },
58
+ {
59
+ id: 'get-one-addresses-entity',
60
+ title: 'getOne returns the addressed entity',
61
+ why: 'Read-back verification depends on addressing a single entity by id.',
62
+ async run(api, fx) {
63
+ const row = await api.getOne(fx.kind, fx.knownEntityId);
64
+ const id = row.id;
65
+ assert(String(id) === fx.knownEntityId, `getOne returned id ${String(id)}, expected ${fx.knownEntityId}`);
66
+ },
67
+ },
68
+ {
69
+ id: 'identity-has-tenant',
70
+ title: 'whoAmI reports a tenant and a role',
71
+ why: 'Every access decision keys off tenant and role. An adapter that leaves tenantId null degrades callers to an anonymous bucket.',
72
+ async run(api) {
73
+ const identity = await api.whoAmI();
74
+ assert(typeof identity.role === 'string', 'identity.role missing');
75
+ assert(identity.tenantId !== undefined, 'identity.tenantId missing');
76
+ },
77
+ },
78
+ {
79
+ id: 'resolve-by-name',
80
+ title: 'ResolveEntity finds a known entity by name',
81
+ why: 'Name resolution is the contract that lets a person type a name instead of an id.',
82
+ async run(api, fx) {
83
+ const { matches } = await (0, resolve_js_1.ResolveEntity)(api, fx.kind, fx.applicationId, fx.knownEntityName);
84
+ assert(matches.some((match) => match.id === fx.knownEntityId), `did not resolve "${fx.knownEntityName}" to ${fx.knownEntityId}`);
85
+ },
86
+ },
87
+ {
88
+ id: 'resolve-application-by-name',
89
+ title: 'ResolveApplicationId maps an application name to its id',
90
+ why: 'Every application-scoped call starts here.',
91
+ async run(api, fx) {
92
+ const id = await (0, resolve_js_1.ResolveApplicationId)(api, fx.applicationName);
93
+ assert(id === fx.applicationId, `resolved ${id}, expected ${fx.applicationId}`);
94
+ },
95
+ },
96
+ {
97
+ id: 'not-found-carries-candidates',
98
+ title: 'ResolveOneStrict reports a miss as NotFoundError with candidates',
99
+ why: 'A miss must offer what does exist. Surfacing a validator message instead is the defect that produced wrong-entity mutations.',
100
+ async run(api, fx) {
101
+ const absent = fx.absentEntityName ?? '__definitely_absent_entity__';
102
+ try {
103
+ await (0, resolve_js_1.ResolveOneStrict)(api, fx.kind, fx.applicationId, absent);
104
+ throw new Error('ResolveOneStrict resolved a name that should not exist');
105
+ }
106
+ catch (error) {
107
+ assert(error instanceof errors_js_1.NotFoundError, `expected NotFoundError, got ${String(error)}`);
108
+ assert(Array.isArray(error.candidates), 'NotFoundError carried no candidates array');
109
+ }
110
+ },
111
+ },
112
+ ];
113
+ async function RunReadPortConformance(api, fixtures) {
114
+ const results = [];
115
+ for (const testCase of exports.READ_PORT_CONFORMANCE) {
116
+ try {
117
+ await testCase.run(api, fixtures);
118
+ results.push({ id: testCase.id, title: testCase.title, passed: true });
119
+ }
120
+ catch (error) {
121
+ results.push({
122
+ id: testCase.id,
123
+ title: testCase.title,
124
+ passed: false,
125
+ error: error instanceof Error ? error.message : String(error),
126
+ });
127
+ }
128
+ }
129
+ return results;
130
+ }
131
+ //# sourceMappingURL=read-port.conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-port.conformance.js","sourceRoot":"","sources":["../../../src/conformance/read-port.conformance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoTenantError = exports.TenantMismatchError = exports.AppScopeError = exports.NotFoundError = exports.AmbiguityError = exports.ElwisApiError = void 0;
4
+ exports.IsElwisApiError = IsElwisApiError;
5
+ exports.ErrorStatus = ErrorStatus;
6
+ class ElwisApiError extends Error {
7
+ status;
8
+ path;
9
+ body;
10
+ constructor(status, path, body) {
11
+ super(`ELWIS API ${status} for ${path}`);
12
+ this.name = 'ElwisApiError';
13
+ this.status = status;
14
+ this.path = path;
15
+ this.body = body;
16
+ }
17
+ }
18
+ exports.ElwisApiError = ElwisApiError;
19
+ function IsElwisApiError(error) {
20
+ if (error instanceof ElwisApiError)
21
+ return true;
22
+ return (typeof error === 'object' &&
23
+ error !== null &&
24
+ typeof error.status === 'number' &&
25
+ typeof error.name === 'string' &&
26
+ /^Elwis(Api|Http)Error$/.test(error.name));
27
+ }
28
+ function ErrorStatus(error) {
29
+ return IsElwisApiError(error) ? error.status : undefined;
30
+ }
31
+ class AmbiguityError extends Error {
32
+ candidates;
33
+ constructor(message, candidates) {
34
+ super(message);
35
+ this.name = 'AmbiguityError';
36
+ this.candidates = candidates;
37
+ }
38
+ }
39
+ exports.AmbiguityError = AmbiguityError;
40
+ class NotFoundError extends Error {
41
+ kind;
42
+ nameOrAlias;
43
+ candidates;
44
+ constructor(kind, nameOrAlias, candidates = []) {
45
+ super(`No active ${kind} named "${nameOrAlias}" in this application`);
46
+ this.name = 'NotFoundError';
47
+ this.kind = kind;
48
+ this.nameOrAlias = nameOrAlias;
49
+ this.candidates = candidates;
50
+ }
51
+ }
52
+ exports.NotFoundError = NotFoundError;
53
+ class AppScopeError extends Error {
54
+ constructor(ref) {
55
+ super(`Application "${ref}" is outside the configured application allowlist`);
56
+ this.name = 'AppScopeError';
57
+ }
58
+ }
59
+ exports.AppScopeError = AppScopeError;
60
+ class TenantMismatchError extends Error {
61
+ constructor(expected, actual) {
62
+ super(`No data was read or written. This connection is locked to tenant ${expected}, but the access token belongs to a different tenant (${actual ?? 'unknown'}).`);
63
+ this.name = 'TenantMismatchError';
64
+ }
65
+ }
66
+ exports.TenantMismatchError = TenantMismatchError;
67
+ class NoTenantError extends Error {
68
+ constructor() {
69
+ super('No tenant is set, so there is no way to know which ELWIS environment to work in.');
70
+ this.name = 'NoTenantError';
71
+ }
72
+ }
73
+ exports.NoTenantError = NoTenantError;
74
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":";;;AAcA,0CASC;AAED,kCAEC;AA3BD,MAAa,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IACf,IAAI,CAAS;IACb,IAAI,CAAU;IAEvB,YAAY,MAAc,EAAE,IAAY,EAAE,IAAa;QACrD,KAAK,CAAC,aAAa,MAAM,QAAQ,IAAI,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAZD,sCAYC;AAED,SAAgB,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,YAAY,aAAa;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAQ,KAA8B,CAAC,MAAM,KAAK,QAAQ;QAC1D,OAAQ,KAA4B,CAAC,IAAI,KAAK,QAAQ;QACtD,wBAAwB,CAAC,IAAI,CAAE,KAA0B,CAAC,IAAI,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,SAAgB,WAAW,CAAC,KAAc;IACxC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,MAAa,cAAe,SAAQ,KAAK;IAC9B,UAAU,CAAsC;IAEzD,YAAY,OAAe,EAAE,UAA+C;QAC1E,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AARD,wCAQC;AAED,MAAa,aAAc,SAAQ,KAAK;IAC7B,IAAI,CAAS;IACb,WAAW,CAAS;IACpB,UAAU,CAAsC;IAEzD,YAAY,IAAY,EAAE,WAAmB,EAAE,aAAkD,EAAE;QACjG,KAAK,CAAC,aAAa,IAAI,WAAW,WAAW,uBAAuB,CAAC,CAAC;QACtE,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;CACF;AAZD,sCAYC;AAED,MAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,GAAW;QACrB,KAAK,CAAC,gBAAgB,GAAG,mDAAmD,CAAC,CAAC;QAC9E,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AALD,sCAKC;AAED,MAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,QAAgB,EAAE,MAAqB;QACjD,KAAK,CACH,oEAAoE,QAAQ,yDAAyD,MAAM,IAAI,SAAS,IAAI,CAC7J,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AAPD,kDAOC;AAED,MAAa,aAAc,SAAQ,KAAK;IACtC;QACE,KAAK,CAAC,kFAAkF,CAAC,CAAC;QAC1F,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AALD,sCAKC"}
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ElwisJwtVerifier = ElwisJwtVerifier;
4
+ const TENANT_ID_PATTERN = /^[a-f0-9]{24}$/i;
5
+ function AssertLooksLikeAToken(token) {
6
+ const parts = token.split('.');
7
+ const payload = parts[1];
8
+ if (parts.length !== 3 || payload === undefined) {
9
+ throw new Error('the bearer value is not a valid token');
10
+ }
11
+ try {
12
+ JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
13
+ }
14
+ catch {
15
+ throw new Error('the bearer value is not a valid token');
16
+ }
17
+ }
18
+ function ElwisJwtVerifier(options) {
19
+ return {
20
+ async verify(token) {
21
+ AssertLooksLikeAToken(token);
22
+ let resolved;
23
+ try {
24
+ resolved = await options.resolveIdentity(token);
25
+ }
26
+ catch (e) {
27
+ throw new Error(`could not resolve the bearer token against ELWIS: ${e instanceof Error ? e.message : String(e)}`);
28
+ }
29
+ if (resolved.tenantId === null || !TENANT_ID_PATTERN.test(resolved.tenantId)) {
30
+ throw new Error(`the resolved identity has no valid tenant id (got: ${resolved.tenantId ?? 'null'})`);
31
+ }
32
+ return {
33
+ token,
34
+ baseUrl: options.baseUrl,
35
+ tenantId: resolved.tenantId,
36
+ };
37
+ },
38
+ };
39
+ }
40
+ //# sourceMappingURL=elwis-jwt-verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elwis-jwt-verifier.js","sourceRoot":"","sources":["../../../src/host/elwis-jwt-verifier.ts"],"names":[],"mappings":";;AAmBA,4CA0BC;AA3CD,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAE5C,SAAS,qBAAqB,CAAC,KAAa;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,IAAI,CAAC;QACH,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAID,SAAgB,gBAAgB,CAAC,OAGhC;IACC,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAa;YACxB,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAE7B,IAAI,QAAqC,CAAC;YAC1C,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAClD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACrH,CAAC;YAED,IAAI,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7E,MAAM,IAAI,KAAK,CAAC,sDAAsD,QAAQ,CAAC,QAAQ,IAAI,MAAM,GAAG,CAAC,CAAC;YACxG,CAAC;YAED,OAAO;gBACL,KAAK;gBACL,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;aAC5B,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileLinkStore = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const FILE_MODE = 0o600;
7
+ const DIRECTORY_MODE = 0o700;
8
+ class FileLinkStore {
9
+ path;
10
+ entries;
11
+ writing = Promise.resolve();
12
+ constructor(path) {
13
+ this.path = path;
14
+ this.entries = this.Load();
15
+ }
16
+ Load() {
17
+ let raw;
18
+ try {
19
+ raw = (0, node_fs_1.readFileSync)(this.path, 'utf8');
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ try {
25
+ const parsed = JSON.parse(raw);
26
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
27
+ return {};
28
+ return parsed;
29
+ }
30
+ catch {
31
+ return {};
32
+ }
33
+ }
34
+ Flush() {
35
+ const snapshot = JSON.stringify(this.entries);
36
+ this.writing = this.writing.then(() => {
37
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(this.path), { recursive: true, mode: DIRECTORY_MODE });
38
+ const temporary = `${this.path}.${process.pid}.tmp`;
39
+ (0, node_fs_1.writeFileSync)(temporary, snapshot, { mode: FILE_MODE });
40
+ (0, node_fs_1.renameSync)(temporary, this.path);
41
+ }, () => { });
42
+ return this.writing;
43
+ }
44
+ async get(key) {
45
+ return this.entries[key] ?? null;
46
+ }
47
+ async put(key, session) {
48
+ this.entries = { ...this.entries, [key]: session };
49
+ await this.Flush();
50
+ }
51
+ async delete(key) {
52
+ const { [key]: removed, ...rest } = this.entries;
53
+ if (removed === undefined)
54
+ return;
55
+ this.entries = rest;
56
+ await this.Flush();
57
+ }
58
+ }
59
+ exports.FileLinkStore = FileLinkStore;
60
+ //# sourceMappingURL=file-link-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-link-store.js","sourceRoot":"","sources":["../../../src/host/file-link-store.ts"],"names":[],"mappings":";;;AAAA,qCAA6E;AAC7E,yCAAoC;AAGpC,MAAM,SAAS,GAAG,KAAK,CAAC;AACxB,MAAM,cAAc,GAAG,KAAK,CAAC;AAE7B,MAAa,aAAa;IACP,IAAI,CAAS;IACtB,OAAO,CAAgC;IACvC,OAAO,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEnD,YAAY,IAAY;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,CAAC;IAEO,IAAI;QACV,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,IAAA,sBAAY,EAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;YAC1C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,EAAE,CAAC;YACtF,OAAO,MAAuC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,KAAK;QACX,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE;YACpC,IAAA,mBAAS,EAAC,IAAA,mBAAO,EAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;YACpD,IAAA,uBAAa,EAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YACxD,IAAA,oBAAU,EAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QACb,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,OAAsB;QAC3C,IAAI,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,CAAC;QACnD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,MAAM,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC;QACjD,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QAClC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AApDD,sCAoDC"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=identity.js.map