@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.
- package/README.md +120 -0
- package/dist/cjs/app-scope.js +81 -0
- package/dist/cjs/app-scope.js.map +1 -0
- package/dist/cjs/conformance/host.conformance.js +98 -0
- package/dist/cjs/conformance/host.conformance.js.map +1 -0
- package/dist/cjs/conformance/read-port.conformance.js +131 -0
- package/dist/cjs/conformance/read-port.conformance.js.map +1 -0
- package/dist/cjs/errors.js +74 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/host/elwis-jwt-verifier.js +40 -0
- package/dist/cjs/host/elwis-jwt-verifier.js.map +1 -0
- package/dist/cjs/host/file-link-store.js +60 -0
- package/dist/cjs/host/file-link-store.js.map +1 -0
- package/dist/cjs/host/identity.js +3 -0
- package/dist/cjs/host/identity.js.map +1 -0
- package/dist/cjs/host/index.js +24 -0
- package/dist/cjs/host/index.js.map +1 -0
- package/dist/cjs/host/link-ticket.js +59 -0
- package/dist/cjs/host/link-ticket.js.map +1 -0
- package/dist/cjs/host/oidc-token.js +96 -0
- package/dist/cjs/host/oidc-token.js.map +1 -0
- package/dist/cjs/host/oidc-verifier.js +67 -0
- package/dist/cjs/host/oidc-verifier.js.map +1 -0
- package/dist/cjs/host/serve-http.js +282 -0
- package/dist/cjs/host/serve-http.js.map +1 -0
- package/dist/cjs/host/session-broker.js +84 -0
- package/dist/cjs/host/session-broker.js.map +1 -0
- package/dist/cjs/index.js +43 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/plan/expand.js +297 -0
- package/dist/cjs/plan/expand.js.map +1 -0
- package/dist/cjs/plan/resolve-existing.js +58 -0
- package/dist/cjs/plan/resolve-existing.js.map +1 -0
- package/dist/cjs/plan/split.js +92 -0
- package/dist/cjs/plan/split.js.map +1 -0
- package/dist/cjs/plan/target-state.js +120 -0
- package/dist/cjs/plan/target-state.js.map +1 -0
- package/dist/cjs/ports/elwis-api.port.js +15 -0
- package/dist/cjs/ports/elwis-api.port.js.map +1 -0
- package/dist/cjs/resolve.js +89 -0
- package/dist/cjs/resolve.js.map +1 -0
- package/dist/esm/app-scope.d.ts +26 -0
- package/dist/esm/app-scope.d.ts.map +1 -0
- package/dist/esm/app-scope.js +76 -0
- package/dist/esm/app-scope.js.map +1 -0
- package/dist/esm/conformance/host.conformance.d.ts +3 -0
- package/dist/esm/conformance/host.conformance.d.ts.map +1 -0
- package/dist/esm/conformance/host.conformance.js +95 -0
- package/dist/esm/conformance/host.conformance.js.map +1 -0
- package/dist/esm/conformance/read-port.conformance.d.ts +24 -0
- package/dist/esm/conformance/read-port.conformance.d.ts.map +1 -0
- package/dist/esm/conformance/read-port.conformance.js +127 -0
- package/dist/esm/conformance/read-port.conformance.js.map +1 -0
- package/dist/esm/errors.d.ts +40 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +63 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/host/elwis-jwt-verifier.d.ts +9 -0
- package/dist/esm/host/elwis-jwt-verifier.d.ts.map +1 -0
- package/dist/esm/host/elwis-jwt-verifier.js +37 -0
- package/dist/esm/host/elwis-jwt-verifier.js.map +1 -0
- package/dist/esm/host/file-link-store.d.ts +13 -0
- package/dist/esm/host/file-link-store.d.ts.map +1 -0
- package/dist/esm/host/file-link-store.js +56 -0
- package/dist/esm/host/file-link-store.js.map +1 -0
- package/dist/esm/host/identity.d.ts +13 -0
- package/dist/esm/host/identity.d.ts.map +1 -0
- package/dist/esm/host/identity.js +2 -0
- package/dist/esm/host/identity.js.map +1 -0
- package/dist/esm/host/index.d.ts +15 -0
- package/dist/esm/host/index.d.ts.map +1 -0
- package/dist/esm/host/index.js +9 -0
- package/dist/esm/host/index.js.map +1 -0
- package/dist/esm/host/link-ticket.d.ts +17 -0
- package/dist/esm/host/link-ticket.d.ts.map +1 -0
- package/dist/esm/host/link-ticket.js +54 -0
- package/dist/esm/host/link-ticket.js.map +1 -0
- package/dist/esm/host/oidc-token.d.ts +20 -0
- package/dist/esm/host/oidc-token.d.ts.map +1 -0
- package/dist/esm/host/oidc-token.js +93 -0
- package/dist/esm/host/oidc-token.js.map +1 -0
- package/dist/esm/host/oidc-verifier.d.ts +14 -0
- package/dist/esm/host/oidc-verifier.d.ts.map +1 -0
- package/dist/esm/host/oidc-verifier.js +63 -0
- package/dist/esm/host/oidc-verifier.js.map +1 -0
- package/dist/esm/host/serve-http.d.ts +42 -0
- package/dist/esm/host/serve-http.d.ts.map +1 -0
- package/dist/esm/host/serve-http.js +278 -0
- package/dist/esm/host/serve-http.js.map +1 -0
- package/dist/esm/host/session-broker.d.ts +45 -0
- package/dist/esm/host/session-broker.d.ts.map +1 -0
- package/dist/esm/host/session-broker.js +77 -0
- package/dist/esm/host/session-broker.js.map +1 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/plan/expand.d.ts +37 -0
- package/dist/esm/plan/expand.d.ts.map +1 -0
- package/dist/esm/plan/expand.js +294 -0
- package/dist/esm/plan/expand.js.map +1 -0
- package/dist/esm/plan/resolve-existing.d.ts +16 -0
- package/dist/esm/plan/resolve-existing.d.ts.map +1 -0
- package/dist/esm/plan/resolve-existing.js +55 -0
- package/dist/esm/plan/resolve-existing.js.map +1 -0
- package/dist/esm/plan/split.d.ts +14 -0
- package/dist/esm/plan/split.d.ts.map +1 -0
- package/dist/esm/plan/split.js +88 -0
- package/dist/esm/plan/split.js.map +1 -0
- package/dist/esm/plan/target-state.d.ts +27 -0
- package/dist/esm/plan/target-state.d.ts.map +1 -0
- package/dist/esm/plan/target-state.js +114 -0
- package/dist/esm/plan/target-state.js.map +1 -0
- package/dist/esm/ports/elwis-api.port.d.ts +56 -0
- package/dist/esm/ports/elwis-api.port.d.ts.map +1 -0
- package/dist/esm/ports/elwis-api.port.js +12 -0
- package/dist/esm/ports/elwis-api.port.js.map +1 -0
- package/dist/esm/resolve.d.ts +23 -0
- package/dist/esm/resolve.d.ts.map +1 -0
- package/dist/esm/resolve.js +79 -0
- package/dist/esm/resolve.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type IncomingMessage } from 'node:http';
|
|
2
|
+
import type { McpServerLike, ResolvedIdentity, TokenVerifier } from './identity.js';
|
|
3
|
+
import { type LinkTicketClaims } from './link-ticket.js';
|
|
4
|
+
export interface McpEndpoint {
|
|
5
|
+
path: string;
|
|
6
|
+
resource: string;
|
|
7
|
+
}
|
|
8
|
+
export type LinkOutcome = {
|
|
9
|
+
status: 'linked';
|
|
10
|
+
} | {
|
|
11
|
+
status: 'needs-sign-in';
|
|
12
|
+
signInUrl: string;
|
|
13
|
+
};
|
|
14
|
+
export interface LinkRequest {
|
|
15
|
+
headers: IncomingMessage['headers'];
|
|
16
|
+
}
|
|
17
|
+
export interface LinkOptions {
|
|
18
|
+
path?: string;
|
|
19
|
+
secret: string;
|
|
20
|
+
complete: (claims: LinkTicketClaims, request: LinkRequest) => Promise<LinkOutcome>;
|
|
21
|
+
}
|
|
22
|
+
export interface ServeOptions {
|
|
23
|
+
factory: (identity: ResolvedIdentity) => McpServerLike;
|
|
24
|
+
verifier: TokenVerifier;
|
|
25
|
+
resource?: string;
|
|
26
|
+
endpoints?: McpEndpoint[];
|
|
27
|
+
authorizationServers: string[];
|
|
28
|
+
link?: LinkOptions;
|
|
29
|
+
port?: number;
|
|
30
|
+
host?: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
maxConcurrentVerifications?: number;
|
|
33
|
+
maxConcurrentSessions?: number;
|
|
34
|
+
}
|
|
35
|
+
export declare const DEFAULT_MAX_CONCURRENT_VERIFICATIONS = 50;
|
|
36
|
+
export declare const DEFAULT_MAX_CONCURRENT_SESSIONS = 200;
|
|
37
|
+
export interface ServeHandle {
|
|
38
|
+
port: number;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
export declare function ServeMcpHttp(options: ServeOptions): Promise<ServeHandle>;
|
|
42
|
+
//# sourceMappingURL=serve-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-http.d.ts","sourceRoot":"","sources":["../../../src/host/serve-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,eAAe,EAAuB,MAAM,WAAW,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpF,OAAO,EAAkB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GACnB;IAAE,MAAM,EAAE,QAAQ,CAAA;CAAE,GACpB;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,aAAa,CAAC;IACvD,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC;IAC1B,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAqGD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CA6K9E"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
3
|
+
import { ReadLinkTicket } from './link-ticket.js';
|
|
4
|
+
export const DEFAULT_MAX_CONCURRENT_VERIFICATIONS = 50;
|
|
5
|
+
export const DEFAULT_MAX_CONCURRENT_SESSIONS = 200;
|
|
6
|
+
const METADATA_PATH = '/.well-known/oauth-protected-resource';
|
|
7
|
+
function BearerToken(req) {
|
|
8
|
+
const header = req.headers.authorization;
|
|
9
|
+
if (!header)
|
|
10
|
+
return null;
|
|
11
|
+
const match = /^Bearer\s+(.+)$/i.exec(header.trim());
|
|
12
|
+
return match?.[1] ?? null;
|
|
13
|
+
}
|
|
14
|
+
function MetadataUrl(resource) {
|
|
15
|
+
const url = new URL(resource);
|
|
16
|
+
const isHttp = url.protocol === 'http:' || url.protocol === 'https:';
|
|
17
|
+
if (!isHttp || url.username || url.password || url.search || url.hash) {
|
|
18
|
+
throw new Error('resource must be an absolute http(s) URL without userinfo, query or fragment');
|
|
19
|
+
}
|
|
20
|
+
const suffix = url.pathname === '/' ? '' : url.pathname;
|
|
21
|
+
return `${url.origin}${METADATA_PATH}${suffix}`;
|
|
22
|
+
}
|
|
23
|
+
function ValidatedCap(name, value, defaultValue) {
|
|
24
|
+
if (value === undefined)
|
|
25
|
+
return defaultValue;
|
|
26
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
27
|
+
throw new Error(`${name} must be a finite integer of at least 1, got: ${value}`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function HeaderSafeValue(value) {
|
|
32
|
+
return value.replace(/[^\x20-\x7e]/g, '').replace(/"/g, "'");
|
|
33
|
+
}
|
|
34
|
+
const MAX_LINK_BODY_BYTES = 8192;
|
|
35
|
+
function Html(value) {
|
|
36
|
+
return value.replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c]));
|
|
37
|
+
}
|
|
38
|
+
function LinkPage(res, status, heading, detail, form = '') {
|
|
39
|
+
res.writeHead(status, {
|
|
40
|
+
'content-type': 'text/html; charset=utf-8',
|
|
41
|
+
'cache-control': 'no-store',
|
|
42
|
+
'x-frame-options': 'DENY',
|
|
43
|
+
'content-security-policy': "frame-ancestors 'none'",
|
|
44
|
+
});
|
|
45
|
+
res.end(`<!doctype html><meta charset=utf-8><title>${Html(heading)}</title>`
|
|
46
|
+
+ `<body style="font:16px/1.5 system-ui,sans-serif;max-width:34rem;margin:15vh auto;padding:0 1.5rem">`
|
|
47
|
+
+ `<h1 style="font-size:1.4rem;margin:0 0 .5rem">${Html(heading)}</h1>`
|
|
48
|
+
+ `<p style="color:#555;margin:0">${Html(detail)}</p>${form}`);
|
|
49
|
+
}
|
|
50
|
+
function ConfirmLinkPage(res, path, ticket, claims) {
|
|
51
|
+
const who = claims.email ?? claims.sub;
|
|
52
|
+
const form = `<form method="post" action="${Html(path)}" style="margin-top:1.5rem">`
|
|
53
|
+
+ `<input type="hidden" name="ticket" value="${Html(ticket)}">`
|
|
54
|
+
+ `<button type="submit" style="font:inherit;padding:.6rem 1.2rem;border-radius:8px;border:0;background:#1f4fd1;color:#fff;cursor:pointer">Link my ELWIS account</button>`
|
|
55
|
+
+ `</form>`;
|
|
56
|
+
LinkPage(res, 200, 'Link your ELWIS account', `The assistant signed in as ${who} asks to work in ELWIS with your current ELWIS session. Continue only if ${who} is you.`, form);
|
|
57
|
+
}
|
|
58
|
+
async function ReadFormTicket(req) {
|
|
59
|
+
let size = 0;
|
|
60
|
+
const chunks = [];
|
|
61
|
+
for await (const chunk of req) {
|
|
62
|
+
size += chunk.length;
|
|
63
|
+
if (size > MAX_LINK_BODY_BYTES)
|
|
64
|
+
throw new Error('the link request is too large');
|
|
65
|
+
chunks.push(chunk);
|
|
66
|
+
}
|
|
67
|
+
return new URLSearchParams(Buffer.concat(chunks).toString('utf8')).get('ticket');
|
|
68
|
+
}
|
|
69
|
+
function LinkUrlOf(error) {
|
|
70
|
+
const candidate = error?.linkUrl;
|
|
71
|
+
return typeof candidate === 'string' && candidate.length > 0 ? candidate : null;
|
|
72
|
+
}
|
|
73
|
+
function Challenge(res, metadataUrl, description, linkUrl) {
|
|
74
|
+
const safeDescription = HeaderSafeValue(description);
|
|
75
|
+
const safeLink = linkUrl ? HeaderSafeValue(linkUrl) : null;
|
|
76
|
+
res.writeHead(401, {
|
|
77
|
+
'www-authenticate': `Bearer resource_metadata="${metadataUrl}", error="invalid_token", error_description="${safeDescription}"`
|
|
78
|
+
+ (safeLink ? `, link_url="${safeLink}"` : ''),
|
|
79
|
+
'content-type': 'application/json',
|
|
80
|
+
});
|
|
81
|
+
res.end(JSON.stringify({ error: 'invalid_token', error_description: safeDescription, ...(safeLink ? { link_url: safeLink } : {}) }));
|
|
82
|
+
}
|
|
83
|
+
function ResolveEndpoints(options) {
|
|
84
|
+
if (options.endpoints?.length) {
|
|
85
|
+
if (options.resource !== undefined) {
|
|
86
|
+
throw new Error('pass either resource or endpoints, not both');
|
|
87
|
+
}
|
|
88
|
+
return options.endpoints;
|
|
89
|
+
}
|
|
90
|
+
if (options.resource === undefined) {
|
|
91
|
+
throw new Error('one of resource or endpoints is required');
|
|
92
|
+
}
|
|
93
|
+
return [{ path: options.path ?? '/mcp', resource: options.resource }];
|
|
94
|
+
}
|
|
95
|
+
export async function ServeMcpHttp(options) {
|
|
96
|
+
const host = options.host ?? '127.0.0.1';
|
|
97
|
+
const endpoints = ResolveEndpoints(options).map(e => ({ ...e, metadataUrl: MetadataUrl(e.resource) }));
|
|
98
|
+
const byPath = new Map(endpoints.map(e => [e.path, e]));
|
|
99
|
+
const byMetadataPath = new Map(endpoints.map(e => [new URL(e.metadataUrl).pathname, e]));
|
|
100
|
+
const firstEndpoint = endpoints[0];
|
|
101
|
+
const maxConcurrentVerifications = ValidatedCap('maxConcurrentVerifications', options.maxConcurrentVerifications, DEFAULT_MAX_CONCURRENT_VERIFICATIONS);
|
|
102
|
+
const maxConcurrentSessions = ValidatedCap('maxConcurrentSessions', options.maxConcurrentSessions, DEFAULT_MAX_CONCURRENT_SESSIONS);
|
|
103
|
+
let inFlightVerifications = 0;
|
|
104
|
+
let liveSessions = 0;
|
|
105
|
+
const usedTickets = new Map();
|
|
106
|
+
const server = createServer(async (req, res) => {
|
|
107
|
+
try {
|
|
108
|
+
const requestPath = req.url?.split('?')[0] ?? '';
|
|
109
|
+
const metadataFor = requestPath === METADATA_PATH ? firstEndpoint : byMetadataPath.get(requestPath);
|
|
110
|
+
if (req.method === 'GET' && metadataFor) {
|
|
111
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
112
|
+
res.end(JSON.stringify({
|
|
113
|
+
resource: metadataFor.resource,
|
|
114
|
+
authorization_servers: options.authorizationServers,
|
|
115
|
+
bearer_methods_supported: ['header'],
|
|
116
|
+
}));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const linkPath = options.link?.path ?? '/link';
|
|
120
|
+
if (options.link && requestPath === linkPath) {
|
|
121
|
+
if (req.method !== 'GET' && req.method !== 'POST') {
|
|
122
|
+
res.writeHead(405, { allow: 'GET, POST' });
|
|
123
|
+
res.end();
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
let ticket;
|
|
127
|
+
let claims;
|
|
128
|
+
try {
|
|
129
|
+
ticket = req.method === 'POST' ? await ReadFormTicket(req) : new URL(req.url ?? '/', 'http://host').searchParams.get('ticket');
|
|
130
|
+
if (!ticket)
|
|
131
|
+
throw new Error('a link ticket is required');
|
|
132
|
+
claims = ReadLinkTicket(ticket, { secret: options.link.secret });
|
|
133
|
+
if (usedTickets.has(ticket))
|
|
134
|
+
throw new Error('this link was already used; ask the assistant for a new one');
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
LinkPage(res, 400, 'This link cannot be used', e instanceof Error ? e.message : 'the link ticket was rejected');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (req.method === 'GET') {
|
|
141
|
+
ConfirmLinkPage(res, linkPath, ticket, claims);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
let outcome;
|
|
145
|
+
try {
|
|
146
|
+
outcome = await options.link.complete(claims, { headers: req.headers });
|
|
147
|
+
}
|
|
148
|
+
catch (e) {
|
|
149
|
+
LinkPage(res, 502, 'ELWIS could not complete the sign-in', e instanceof Error ? e.message : 'unknown failure');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (outcome.status === 'needs-sign-in') {
|
|
153
|
+
let target;
|
|
154
|
+
try {
|
|
155
|
+
target = new URL(outcome.signInUrl);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
LinkPage(res, 500, 'The sign-in address is not usable', 'the configured sign-in url is not an absolute url');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (target.protocol !== 'http:' && target.protocol !== 'https:') {
|
|
162
|
+
LinkPage(res, 500, 'The sign-in address is not usable', 'the configured sign-in url is not http or https');
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
res.writeHead(302, { location: target.toString(), 'cache-control': 'no-store' });
|
|
166
|
+
res.end();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
170
|
+
for (const [used, exp] of usedTickets)
|
|
171
|
+
if (exp <= nowSeconds)
|
|
172
|
+
usedTickets.delete(used);
|
|
173
|
+
usedTickets.set(ticket, claims.exp);
|
|
174
|
+
LinkPage(res, 200, 'Account linked', 'You can close this tab and go back to your assistant.');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const endpoint = byPath.get(requestPath);
|
|
178
|
+
if (!endpoint) {
|
|
179
|
+
res.writeHead(404, { 'content-type': 'application/json' });
|
|
180
|
+
res.end(JSON.stringify({ error: 'not_found' }));
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (req.method === 'GET') {
|
|
184
|
+
res.writeHead(405, { 'content-type': 'application/json', allow: 'POST' });
|
|
185
|
+
res.end(JSON.stringify({ error: 'method_not_allowed', error_description: 'GET is not supported; this server does not send server-initiated messages' }));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const token = BearerToken(req);
|
|
189
|
+
if (!token) {
|
|
190
|
+
Challenge(res, endpoint.metadataUrl, 'a bearer token is required');
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (inFlightVerifications >= maxConcurrentVerifications) {
|
|
194
|
+
res.writeHead(503, { 'content-type': 'application/json', 'retry-after': '1' });
|
|
195
|
+
res.end(JSON.stringify({ error: 'too_many_requests', error_description: 'too many verifications in flight; try again shortly' }));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
let identity;
|
|
199
|
+
inFlightVerifications++;
|
|
200
|
+
try {
|
|
201
|
+
identity = await options.verifier.verify(token, endpoint.resource);
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
const linkUrl = LinkUrlOf(e);
|
|
205
|
+
Challenge(res, endpoint.metadataUrl, linkUrl ? 'this account is not linked to ELWIS yet' : 'the token was rejected', linkUrl);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
inFlightVerifications--;
|
|
210
|
+
}
|
|
211
|
+
if (liveSessions >= maxConcurrentSessions) {
|
|
212
|
+
res.writeHead(503, { 'content-type': 'application/json', 'retry-after': '1' });
|
|
213
|
+
res.end(JSON.stringify({ error: 'too_many_requests', error_description: 'too many sessions in flight; try again shortly' }));
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
liveSessions++;
|
|
217
|
+
let sessionReleased = false;
|
|
218
|
+
const releaseSession = () => {
|
|
219
|
+
if (sessionReleased)
|
|
220
|
+
return;
|
|
221
|
+
sessionReleased = true;
|
|
222
|
+
liveSessions--;
|
|
223
|
+
};
|
|
224
|
+
try {
|
|
225
|
+
const mcp = options.factory(identity);
|
|
226
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
227
|
+
res.on('close', () => {
|
|
228
|
+
transport.close().catch(() => { });
|
|
229
|
+
mcp.close().catch(() => { });
|
|
230
|
+
releaseSession();
|
|
231
|
+
});
|
|
232
|
+
await mcp.connect(transport);
|
|
233
|
+
await transport.handleRequest(req, res);
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
releaseSession();
|
|
237
|
+
throw e;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
if (!res.headersSent) {
|
|
242
|
+
res.writeHead(500, { 'content-type': 'application/json' });
|
|
243
|
+
res.end(JSON.stringify({ error: 'internal_error' }));
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
res.end();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
await new Promise((resolve, reject) => {
|
|
251
|
+
const onListenError = (err) => reject(err);
|
|
252
|
+
server.once('error', onListenError);
|
|
253
|
+
server.listen(options.port ?? 0, host, () => {
|
|
254
|
+
server.removeListener('error', onListenError);
|
|
255
|
+
resolve();
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
const address = server.address();
|
|
259
|
+
const port = typeof address === 'object' && address ? address.port : 0;
|
|
260
|
+
return {
|
|
261
|
+
port,
|
|
262
|
+
close: () => new Promise((resolve, reject) => {
|
|
263
|
+
server.closeAllConnections();
|
|
264
|
+
server.close(err => {
|
|
265
|
+
if (!err) {
|
|
266
|
+
resolve();
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (err.code === 'ERR_SERVER_NOT_RUNNING') {
|
|
270
|
+
resolve();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
reject(err);
|
|
274
|
+
});
|
|
275
|
+
}),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=serve-http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve-http.js","sourceRoot":"","sources":["../../../src/host/serve-http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ResolvedIdentity } from './identity.js';
|
|
2
|
+
import type { OidcClaims } from './oidc-token.js';
|
|
3
|
+
export type SubjectClaims = Pick<OidcClaims, 'iss' | 'sub' | 'email'>;
|
|
4
|
+
export interface LinkedSession {
|
|
5
|
+
elwisToken: string;
|
|
6
|
+
tenantId: string;
|
|
7
|
+
elwisUserId: string;
|
|
8
|
+
linkedAt: number;
|
|
9
|
+
}
|
|
10
|
+
export interface LinkStore {
|
|
11
|
+
get(key: string): Promise<LinkedSession | null>;
|
|
12
|
+
put(key: string, session: LinkedSession): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
export declare class MemoryLinkStore implements LinkStore {
|
|
16
|
+
private readonly entries;
|
|
17
|
+
get(key: string): Promise<LinkedSession | null>;
|
|
18
|
+
put(key: string, session: LinkedSession): Promise<void>;
|
|
19
|
+
delete(key: string): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export declare class AccountNotLinkedError extends Error {
|
|
22
|
+
readonly linkUrl: string;
|
|
23
|
+
constructor(linkUrl: string, reason: string);
|
|
24
|
+
}
|
|
25
|
+
export declare function LinkKey(claims: SubjectClaims): string;
|
|
26
|
+
export interface CheckedSession {
|
|
27
|
+
tenantId: string;
|
|
28
|
+
userId: string;
|
|
29
|
+
email?: string;
|
|
30
|
+
rotatedToken?: string;
|
|
31
|
+
}
|
|
32
|
+
export type CheckSession = (token: string) => Promise<CheckedSession>;
|
|
33
|
+
export interface SessionBrokerOptions {
|
|
34
|
+
store: LinkStore;
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
expectedTenant: string;
|
|
37
|
+
linkUrlFor: (claims: SubjectClaims) => string;
|
|
38
|
+
checkSession: CheckSession;
|
|
39
|
+
}
|
|
40
|
+
export interface SessionBroker {
|
|
41
|
+
resolve(claims: SubjectClaims): Promise<ResolvedIdentity>;
|
|
42
|
+
link(claims: SubjectClaims, elwisToken: string): Promise<LinkedSession>;
|
|
43
|
+
}
|
|
44
|
+
export declare function ElwisSessionBroker(options: SessionBrokerOptions): SessionBroker;
|
|
45
|
+
//# sourceMappingURL=session-broker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-broker.d.ts","sourceRoot":"","sources":["../../../src/host/session-broker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAChD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,qBAAa,eAAgB,YAAW,SAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoC;IAEtD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAI/C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzC;AAED,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAErD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;AAEtE,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,CAAC;IAC9C,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;CACzE;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAyD/E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export class MemoryLinkStore {
|
|
2
|
+
entries = new Map();
|
|
3
|
+
async get(key) {
|
|
4
|
+
return this.entries.get(key) ?? null;
|
|
5
|
+
}
|
|
6
|
+
async put(key, session) {
|
|
7
|
+
this.entries.set(key, session);
|
|
8
|
+
}
|
|
9
|
+
async delete(key) {
|
|
10
|
+
this.entries.delete(key);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class AccountNotLinkedError extends Error {
|
|
14
|
+
linkUrl;
|
|
15
|
+
constructor(linkUrl, reason) {
|
|
16
|
+
super(`this account is not linked to ELWIS (${reason}); link it at ${linkUrl}`);
|
|
17
|
+
this.name = 'AccountNotLinkedError';
|
|
18
|
+
this.linkUrl = linkUrl;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function LinkKey(claims) {
|
|
22
|
+
return `${encodeURIComponent(claims.iss)}:${encodeURIComponent(claims.sub)}`;
|
|
23
|
+
}
|
|
24
|
+
export function ElwisSessionBroker(options) {
|
|
25
|
+
async function AssertTenant(token) {
|
|
26
|
+
const checked = await options.checkSession(token);
|
|
27
|
+
if (checked.tenantId !== options.expectedTenant) {
|
|
28
|
+
throw new Error(`the ELWIS session belongs to tenant ${checked.tenantId}, not ${options.expectedTenant}`);
|
|
29
|
+
}
|
|
30
|
+
return checked;
|
|
31
|
+
}
|
|
32
|
+
function AssertSamePerson(signedInAs, elwisEmail) {
|
|
33
|
+
if (!signedInAs)
|
|
34
|
+
throw new Error('the assistant sign-in carries no email, so it cannot be matched to an ELWIS account');
|
|
35
|
+
if (signedInAs.toLowerCase() !== elwisEmail?.toLowerCase()) {
|
|
36
|
+
throw new Error(`the assistant is signed in as ${signedInAs}, but this browser is signed in to ELWIS as ${elwisEmail ?? 'an account without an email'}; sign in to ELWIS as ${signedInAs} and open the link again`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
async resolve(claims) {
|
|
41
|
+
const key = LinkKey(claims);
|
|
42
|
+
const linked = await options.store.get(key);
|
|
43
|
+
if (!linked) {
|
|
44
|
+
throw new AccountNotLinkedError(options.linkUrlFor(claims), 'no linked session');
|
|
45
|
+
}
|
|
46
|
+
let checked;
|
|
47
|
+
try {
|
|
48
|
+
checked = await options.checkSession(linked.elwisToken);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
await options.store.delete(key);
|
|
52
|
+
throw new AccountNotLinkedError(options.linkUrlFor(claims), 'the linked session is no longer valid');
|
|
53
|
+
}
|
|
54
|
+
if (checked.tenantId !== options.expectedTenant) {
|
|
55
|
+
throw new Error(`the linked ELWIS session belongs to tenant ${checked.tenantId}, not ${options.expectedTenant}`);
|
|
56
|
+
}
|
|
57
|
+
const token = checked.rotatedToken ?? linked.elwisToken;
|
|
58
|
+
if (checked.rotatedToken !== undefined && checked.rotatedToken !== linked.elwisToken) {
|
|
59
|
+
await options.store.put(key, { ...linked, elwisToken: checked.rotatedToken });
|
|
60
|
+
}
|
|
61
|
+
return { token, baseUrl: options.baseUrl, tenantId: linked.tenantId };
|
|
62
|
+
},
|
|
63
|
+
async link(claims, elwisToken) {
|
|
64
|
+
const checked = await AssertTenant(elwisToken);
|
|
65
|
+
AssertSamePerson(claims.email, checked.email);
|
|
66
|
+
const session = {
|
|
67
|
+
elwisToken: checked.rotatedToken ?? elwisToken,
|
|
68
|
+
tenantId: checked.tenantId,
|
|
69
|
+
elwisUserId: checked.userId,
|
|
70
|
+
linkedAt: Date.now(),
|
|
71
|
+
};
|
|
72
|
+
await options.store.put(LinkKey(claims), session);
|
|
73
|
+
return session;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=session-broker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-broker.js","sourceRoot":"","sources":["../../../src/host/session-broker.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,eAAe;IACT,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE5D,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,OAAsB;QAC3C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IACrC,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,wCAAwC,MAAM,iBAAiB,OAAO,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,MAAM,UAAU,OAAO,CAAC,MAAqB;IAC3C,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;AAC/E,CAAC;AAwBD,MAAM,UAAU,kBAAkB,CAAC,OAA6B;IAC9D,KAAK,UAAU,YAAY,CAAC,KAAa;QACvC,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,uCAAuC,OAAO,CAAC,QAAQ,SAAS,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;QAC5G,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,SAAS,gBAAgB,CAAC,UAA8B,EAAE,UAA8B;QACtF,IAAI,CAAC,UAAU;YAAE,MAAM,IAAI,KAAK,CAAC,qFAAqF,CAAC,CAAC;QACxH,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,UAAU,EAAE,WAAW,EAAE,EAAE,CAAC;YAC3D,MAAM,IAAI,KAAK,CAAC,iCAAiC,UAAU,+CAA+C,UAAU,IAAI,6BAA6B,yBAAyB,UAAU,0BAA0B,CAAC,CAAC;QACtN,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,OAAO,CAAC,MAAqB;YACjC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC,CAAC;YACnF,CAAC;YAED,IAAI,OAAuB,CAAC;YAC5B,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC1D,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAChC,MAAM,IAAI,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,uCAAuC,CAAC,CAAC;YACvG,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,cAAc,EAAE,CAAC;gBAChD,MAAM,IAAI,KAAK,CAAC,8CAA8C,OAAO,CAAC,QAAQ,SAAS,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;YACnH,CAAC;YAED,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,IAAI,MAAM,CAAC,UAAU,CAAC;YACxD,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,IAAI,OAAO,CAAC,YAAY,KAAK,MAAM,CAAC,UAAU,EAAE,CAAC;gBACrF,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;YAChF,CAAC;YAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxE,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,MAAqB,EAAE,UAAkB;YAClD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,UAAU,CAAC,CAAC;YAC/C,gBAAgB,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;YAC9C,MAAM,OAAO,GAAkB;gBAC7B,UAAU,EAAE,OAAO,CAAC,YAAY,IAAI,UAAU;gBAC9C,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,WAAW,EAAE,OAAO,CAAC,MAAM;gBAC3B,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE;aACrB,CAAC;YACF,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type { ElwisApiPort, ElwisReadPort, ElwisWritePort, ElwisEntity, EntityKind, RecordCollection, RecordPage, Identity, IdentityRole, SystemAttributeIds, } from './ports/elwis-api.port.js';
|
|
2
|
+
export { ResolveIdentityRole } from './ports/elwis-api.port.js';
|
|
3
|
+
export { ElwisApiError, IsElwisApiError, ErrorStatus, AmbiguityError, NotFoundError, AppScopeError, TenantMismatchError, NoTenantError, } from './errors.js';
|
|
4
|
+
export type { EntityMatch } from './resolve.js';
|
|
5
|
+
export { TENANT_LEVEL_KINDS, IsObjectId, ResolveEntity, AssertInApplication, ResolveApplicationId, RequireApplicationId, ResolveOneId, ResolveOneStrict, } from './resolve.js';
|
|
6
|
+
export type { ScopeIdentity } from './app-scope.js';
|
|
7
|
+
export { AppScope, FilterApplicationId } from './app-scope.js';
|
|
8
|
+
export type { ConformanceCase, ConformanceFixtures, ConformanceResult } from './conformance/read-port.conformance.js';
|
|
9
|
+
export { READ_PORT_CONFORMANCE, RunReadPortConformance } from './conformance/read-port.conformance.js';
|
|
10
|
+
export type { PlannableKind, TargetKind, TargetEntity, TargetRelation, TargetState, } from './plan/target-state.js';
|
|
11
|
+
export { PLANNABLE_KINDS, BASE_DATA_TYPES, ResolveBaseDataType, IsDescriptiveName, NormalizeTargetState, } from './plan/target-state.js';
|
|
12
|
+
export type { PlanBinding, PlanStep, PlanAssumption, PlanGap, ExpansionResult, } from './plan/expand.js';
|
|
13
|
+
export { ExpandTargetState } from './plan/expand.js';
|
|
14
|
+
export type { ResolutionAmbiguity, ResolutionResult } from './plan/resolve-existing.js';
|
|
15
|
+
export { ResolveExistingEntities } from './plan/resolve-existing.js';
|
|
16
|
+
export type { PlanChunk, SplitResult } from './plan/split.js';
|
|
17
|
+
export { SplitPlan, MAX_STEPS_PER_PLAN } from './plan/split.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,YAAY,EACZ,aAAa,EACb,cAAc,EACd,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EACL,aAAa,EACb,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAEtB,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE/D,YAAY,EAAE,eAAe,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AACtH,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAEvG,YAAY,EACV,aAAa,EACb,UAAU,EACV,YAAY,EACZ,cAAc,EACd,WAAW,GACZ,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,OAAO,EACP,eAAe,GAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAErD,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAErE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { ResolveIdentityRole } from './ports/elwis-api.port.js';
|
|
2
|
+
export { ElwisApiError, IsElwisApiError, ErrorStatus, AmbiguityError, NotFoundError, AppScopeError, TenantMismatchError, NoTenantError, } from './errors.js';
|
|
3
|
+
export { TENANT_LEVEL_KINDS, IsObjectId, ResolveEntity, AssertInApplication, ResolveApplicationId, RequireApplicationId, ResolveOneId, ResolveOneStrict, } from './resolve.js';
|
|
4
|
+
export { AppScope, FilterApplicationId } from './app-scope.js';
|
|
5
|
+
export { READ_PORT_CONFORMANCE, RunReadPortConformance } from './conformance/read-port.conformance.js';
|
|
6
|
+
export { PLANNABLE_KINDS, BASE_DATA_TYPES, ResolveBaseDataType, IsDescriptiveName, NormalizeTargetState, } from './plan/target-state.js';
|
|
7
|
+
export { ExpandTargetState } from './plan/expand.js';
|
|
8
|
+
export { ResolveExistingEntities } from './plan/resolve-existing.js';
|
|
9
|
+
export { SplitPlan, MAX_STEPS_PER_PLAN } from './plan/split.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,OAAO,EACL,aAAa,EACb,eAAe,EACf,WAAW,EACX,cAAc,EACd,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,aAAa,GACd,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,YAAY,EACZ,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AASvG,OAAO,EACL,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAShC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAGrE,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PlannableKind, TargetState } from './target-state.js';
|
|
2
|
+
export interface PlanBinding {
|
|
3
|
+
field: string;
|
|
4
|
+
fromStep: number;
|
|
5
|
+
fromField: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PlanStep {
|
|
8
|
+
index: number;
|
|
9
|
+
operation: 'create' | 'attach';
|
|
10
|
+
kind: PlannableKind;
|
|
11
|
+
name: string | null;
|
|
12
|
+
toolName: string;
|
|
13
|
+
args: Record<string, unknown>;
|
|
14
|
+
dependsOn: number[];
|
|
15
|
+
bindings: PlanBinding[];
|
|
16
|
+
description: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PlanAssumption {
|
|
19
|
+
kind: string;
|
|
20
|
+
name: string | null;
|
|
21
|
+
field: string;
|
|
22
|
+
value: unknown;
|
|
23
|
+
blocking: boolean;
|
|
24
|
+
reason: string;
|
|
25
|
+
}
|
|
26
|
+
export interface PlanGap {
|
|
27
|
+
kind: string;
|
|
28
|
+
name: string | null;
|
|
29
|
+
reason: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ExpansionResult {
|
|
32
|
+
steps: PlanStep[];
|
|
33
|
+
assumptions: PlanAssumption[];
|
|
34
|
+
gaps: PlanGap[];
|
|
35
|
+
}
|
|
36
|
+
export declare function ExpandTargetState(input: TargetState): ExpansionResult;
|
|
37
|
+
//# sourceMappingURL=expand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../../src/plan/expand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAgB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGlF,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AA4BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,eAAe,CAmRrE"}
|