@amodalai/runtime 0.3.70 → 0.3.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/auth/__fixtures__/custom-strategy.d.ts +8 -0
- package/dist/src/auth/__fixtures__/custom-strategy.js +27 -0
- package/dist/src/auth/__fixtures__/custom-strategy.js.map +1 -0
- package/dist/src/auth/compose.d.ts +55 -0
- package/dist/src/auth/compose.js +142 -0
- package/dist/src/auth/compose.js.map +1 -0
- package/dist/src/auth/compose.test.d.ts +6 -0
- package/dist/src/auth/compose.test.js +159 -0
- package/dist/src/auth/compose.test.js.map +1 -0
- package/dist/src/auth/config.d.ts +261 -0
- package/dist/src/auth/config.js +107 -0
- package/dist/src/auth/config.js.map +1 -0
- package/dist/src/auth/config.test.d.ts +6 -0
- package/dist/src/auth/config.test.js +85 -0
- package/dist/src/auth/config.test.js.map +1 -0
- package/dist/src/auth/factory.d.ts +19 -0
- package/dist/src/auth/factory.js +57 -0
- package/dist/src/auth/factory.js.map +1 -0
- package/dist/src/auth/factory.test.d.ts +6 -0
- package/dist/src/auth/factory.test.js +60 -0
- package/dist/src/auth/factory.test.js.map +1 -0
- package/dist/src/auth/index.d.ts +48 -0
- package/dist/src/auth/index.js +19 -0
- package/dist/src/auth/index.js.map +1 -0
- package/dist/src/auth/strategies/amodal.d.ts +36 -0
- package/dist/src/auth/strategies/amodal.js +28 -0
- package/dist/src/auth/strategies/amodal.js.map +1 -0
- package/dist/src/auth/strategies/api-key.d.ts +41 -0
- package/dist/src/auth/strategies/api-key.js +63 -0
- package/dist/src/auth/strategies/api-key.js.map +1 -0
- package/dist/src/auth/strategies/auth-modes.integration.test.d.ts +6 -0
- package/dist/src/auth/strategies/auth-modes.integration.test.js +363 -0
- package/dist/src/auth/strategies/auth-modes.integration.test.js.map +1 -0
- package/dist/src/auth/strategies/cookie.d.ts +41 -0
- package/dist/src/auth/strategies/cookie.js +84 -0
- package/dist/src/auth/strategies/cookie.js.map +1 -0
- package/dist/src/auth/strategies/custom-loader.d.ts +17 -0
- package/dist/src/auth/strategies/custom-loader.js +37 -0
- package/dist/src/auth/strategies/custom-loader.js.map +1 -0
- package/dist/src/auth/strategies/header.d.ts +35 -0
- package/dist/src/auth/strategies/header.js +42 -0
- package/dist/src/auth/strategies/header.js.map +1 -0
- package/dist/src/auth/strategies/jwks.d.ts +38 -0
- package/dist/src/auth/strategies/jwks.js +86 -0
- package/dist/src/auth/strategies/jwks.js.map +1 -0
- package/dist/src/auth/strategies/jwt-secret.d.ts +25 -0
- package/dist/src/auth/strategies/jwt-secret.js +82 -0
- package/dist/src/auth/strategies/jwt-secret.js.map +1 -0
- package/dist/src/auth/strategies/jwt-secret.test.d.ts +6 -0
- package/dist/src/auth/strategies/jwt-secret.test.js +75 -0
- package/dist/src/auth/strategies/jwt-secret.test.js.map +1 -0
- package/dist/src/auth/strategies/none.d.ts +37 -0
- package/dist/src/auth/strategies/none.js +31 -0
- package/dist/src/auth/strategies/none.js.map +1 -0
- package/dist/src/auth/strategies/oidc.d.ts +48 -0
- package/dist/src/auth/strategies/oidc.integration.test.d.ts +6 -0
- package/dist/src/auth/strategies/oidc.integration.test.js +290 -0
- package/dist/src/auth/strategies/oidc.integration.test.js.map +1 -0
- package/dist/src/auth/strategies/oidc.js +284 -0
- package/dist/src/auth/strategies/oidc.js.map +1 -0
- package/dist/src/auth/strategies/oidc.test.d.ts +6 -0
- package/dist/src/auth/strategies/oidc.test.js +111 -0
- package/dist/src/auth/strategies/oidc.test.js.map +1 -0
- package/dist/src/auth/strategies/strategies.test.d.ts +6 -0
- package/dist/src/auth/strategies/strategies.test.js +190 -0
- package/dist/src/auth/strategies/strategies.test.js.map +1 -0
- package/dist/src/auth/types.d.ts +95 -0
- package/dist/src/auth/types.js +7 -0
- package/dist/src/auth/types.js.map +1 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
const EXPECTED_TOKEN = 'secret-value';
|
|
7
|
+
const strategy = {
|
|
8
|
+
name: 'custom-test',
|
|
9
|
+
valid(req) {
|
|
10
|
+
return typeof req.headers['x-custom-token'] === 'string';
|
|
11
|
+
},
|
|
12
|
+
async authenticate(req) {
|
|
13
|
+
const token = req.headers['x-custom-token'];
|
|
14
|
+
if (typeof token !== 'string' || token !== EXPECTED_TOKEN) {
|
|
15
|
+
return { kind: 'rejected', reason: 'Bad custom token' };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
kind: 'authenticated',
|
|
19
|
+
session: {
|
|
20
|
+
user: { id: 'custom-user', name: 'Custom User' },
|
|
21
|
+
method: 'custom-test',
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export default strategy;
|
|
27
|
+
//# sourceMappingURL=custom-strategy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-strategy.js","sourceRoot":"","sources":["../../../../src/auth/__fixtures__/custom-strategy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAUH,MAAM,cAAc,GAAG,cAAc,CAAC;AAEtC,MAAM,QAAQ,GAAiB;IAC7B,IAAI,EAAE,aAAa;IACnB,KAAK,CAAC,GAAY;QAChB,OAAO,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,QAAQ,CAAC;IAC3D,CAAC;IACD,KAAK,CAAC,YAAY,CAAC,GAAY;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC5C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,cAAc,EAAE,CAAC;YAC1D,OAAO,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,kBAAkB,EAAC,CAAC;QACxD,CAAC;QACD,OAAO;YACL,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE;gBACP,IAAI,EAAE,EAAC,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAC;gBAC9C,MAAM,EAAE,aAAa;aACtB;SACF,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Composable auth middleware + router factories.
|
|
8
|
+
*
|
|
9
|
+
* `authenticate(...strategies)` returns an Express middleware that runs
|
|
10
|
+
* the strategies in order, picks the first one whose `valid(req)` is
|
|
11
|
+
* true, awaits its `authenticate(req)`, and attaches the resulting
|
|
12
|
+
* session to `res.locals.session` (or 401s the request). Single-winner.
|
|
13
|
+
*
|
|
14
|
+
* `authRouter(...strategies)` returns a router carrying any strategy-
|
|
15
|
+
* supplied routes (OAuth callbacks, login redirects, etc.). Mount it
|
|
16
|
+
* once at the application root.
|
|
17
|
+
*
|
|
18
|
+
* Per-route mounting solves the Studio / end-user route bifurcation:
|
|
19
|
+
* different routes pass different strategy lists, so a request to
|
|
20
|
+
* `/api/files` can never be authenticated by an end-user-only strategy
|
|
21
|
+
* (it isn't in the list).
|
|
22
|
+
*/
|
|
23
|
+
import { Router } from 'express';
|
|
24
|
+
import type { RequestHandler } from 'express';
|
|
25
|
+
import type { AuthStrategy, AuthSession } from './types.js';
|
|
26
|
+
/** Hook fired once per request after the chain runs. Useful for metrics. */
|
|
27
|
+
export interface AuthLogEvent {
|
|
28
|
+
/** Strategy that resolved the request, or `null` if no strategy matched. */
|
|
29
|
+
strategy: string | null;
|
|
30
|
+
outcome: 'authenticated' | 'rejected' | 'no-match';
|
|
31
|
+
reason?: string;
|
|
32
|
+
durationMs: number;
|
|
33
|
+
}
|
|
34
|
+
export interface AuthenticateOptions {
|
|
35
|
+
logger?: (event: AuthLogEvent) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a middleware that authenticates requests against the given
|
|
39
|
+
* strategy chain. First strategy to match (`valid` && `authenticate →
|
|
40
|
+
* authenticated`) wins. Reject 401s the request. No matching strategy
|
|
41
|
+
* also 401s.
|
|
42
|
+
*/
|
|
43
|
+
export declare function authenticate(...args: Array<AuthStrategy | AuthenticateOptions>): RequestHandler;
|
|
44
|
+
/**
|
|
45
|
+
* Build a router carrying any strategy-supplied routes. Strategies that
|
|
46
|
+
* own a login flow (OAuth callbacks, magic link, etc.) implement the
|
|
47
|
+
* optional `routes()` method; this factory mounts each one onto a single
|
|
48
|
+
* router for app-level wiring.
|
|
49
|
+
*/
|
|
50
|
+
export declare function authRouter(...strategies: readonly AuthStrategy[]): Router;
|
|
51
|
+
/**
|
|
52
|
+
* Read the session attached by `authenticate(...)`. Returns `undefined`
|
|
53
|
+
* if no session is set (e.g. on routes that don't have auth middleware).
|
|
54
|
+
*/
|
|
55
|
+
export declare function getSession(res: import('express').Response): AuthSession | undefined;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Composable auth middleware + router factories.
|
|
8
|
+
*
|
|
9
|
+
* `authenticate(...strategies)` returns an Express middleware that runs
|
|
10
|
+
* the strategies in order, picks the first one whose `valid(req)` is
|
|
11
|
+
* true, awaits its `authenticate(req)`, and attaches the resulting
|
|
12
|
+
* session to `res.locals.session` (or 401s the request). Single-winner.
|
|
13
|
+
*
|
|
14
|
+
* `authRouter(...strategies)` returns a router carrying any strategy-
|
|
15
|
+
* supplied routes (OAuth callbacks, login redirects, etc.). Mount it
|
|
16
|
+
* once at the application root.
|
|
17
|
+
*
|
|
18
|
+
* Per-route mounting solves the Studio / end-user route bifurcation:
|
|
19
|
+
* different routes pass different strategy lists, so a request to
|
|
20
|
+
* `/api/files` can never be authenticated by an end-user-only strategy
|
|
21
|
+
* (it isn't in the list).
|
|
22
|
+
*/
|
|
23
|
+
import { Router } from 'express';
|
|
24
|
+
const SESSION_KEY = 'session';
|
|
25
|
+
/**
|
|
26
|
+
* Build a middleware that authenticates requests against the given
|
|
27
|
+
* strategy chain. First strategy to match (`valid` && `authenticate →
|
|
28
|
+
* authenticated`) wins. Reject 401s the request. No matching strategy
|
|
29
|
+
* also 401s.
|
|
30
|
+
*/
|
|
31
|
+
export function authenticate(...args) {
|
|
32
|
+
const strategies = [];
|
|
33
|
+
let options = {};
|
|
34
|
+
for (const arg of args) {
|
|
35
|
+
if (isStrategy(arg)) {
|
|
36
|
+
strategies.push(arg);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
options = arg;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return (req, res, next) => {
|
|
43
|
+
const startedAt = Date.now();
|
|
44
|
+
void runChain(strategies, req)
|
|
45
|
+
.then((result) => {
|
|
46
|
+
const durationMs = Date.now() - startedAt;
|
|
47
|
+
if (result.kind === 'authenticated') {
|
|
48
|
+
res.locals[SESSION_KEY] = result.session;
|
|
49
|
+
options.logger?.({
|
|
50
|
+
strategy: result.session.method,
|
|
51
|
+
outcome: 'authenticated',
|
|
52
|
+
durationMs,
|
|
53
|
+
});
|
|
54
|
+
next();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (result.kind === 'rejected') {
|
|
58
|
+
options.logger?.({
|
|
59
|
+
strategy: result.strategy,
|
|
60
|
+
outcome: 'rejected',
|
|
61
|
+
reason: result.reason,
|
|
62
|
+
durationMs,
|
|
63
|
+
});
|
|
64
|
+
res.status(401).json({
|
|
65
|
+
error: { code: 'UNAUTHORIZED', message: result.reason },
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
options.logger?.({
|
|
70
|
+
strategy: null,
|
|
71
|
+
outcome: 'no-match',
|
|
72
|
+
durationMs,
|
|
73
|
+
});
|
|
74
|
+
res.status(401).json({
|
|
75
|
+
error: { code: 'UNAUTHORIZED', message: 'Missing authentication' },
|
|
76
|
+
});
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => {
|
|
79
|
+
// `runChain` itself never throws — this catch is defensive against
|
|
80
|
+
// strategy implementations that violate the contract by throwing.
|
|
81
|
+
const durationMs = Date.now() - startedAt;
|
|
82
|
+
const message = err instanceof Error ? err.message : 'Unauthorized';
|
|
83
|
+
options.logger?.({
|
|
84
|
+
strategy: null,
|
|
85
|
+
outcome: 'rejected',
|
|
86
|
+
reason: message,
|
|
87
|
+
durationMs,
|
|
88
|
+
});
|
|
89
|
+
res.status(401).json({
|
|
90
|
+
error: { code: 'UNAUTHORIZED', message },
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async function runChain(strategies, req) {
|
|
96
|
+
for (const strategy of strategies) {
|
|
97
|
+
if (!strategy.valid(req))
|
|
98
|
+
continue;
|
|
99
|
+
const result = await strategy.authenticate(req);
|
|
100
|
+
if (result.kind === 'authenticated') {
|
|
101
|
+
return { kind: 'authenticated', session: result.session };
|
|
102
|
+
}
|
|
103
|
+
if (result.kind === 'rejected') {
|
|
104
|
+
return { kind: 'rejected', strategy: strategy.name, reason: result.reason };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { kind: 'no-match' };
|
|
108
|
+
}
|
|
109
|
+
function isStrategy(value) {
|
|
110
|
+
if (typeof value !== 'object' || value === null)
|
|
111
|
+
return false;
|
|
112
|
+
if (!('name' in value) || !('valid' in value) || !('authenticate' in value)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const candidate = value;
|
|
116
|
+
return (typeof candidate['valid'] === 'function' &&
|
|
117
|
+
typeof candidate['authenticate'] === 'function');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Build a router carrying any strategy-supplied routes. Strategies that
|
|
121
|
+
* own a login flow (OAuth callbacks, magic link, etc.) implement the
|
|
122
|
+
* optional `routes()` method; this factory mounts each one onto a single
|
|
123
|
+
* router for app-level wiring.
|
|
124
|
+
*/
|
|
125
|
+
export function authRouter(...strategies) {
|
|
126
|
+
const router = Router();
|
|
127
|
+
for (const strategy of strategies) {
|
|
128
|
+
if (strategy.routes) {
|
|
129
|
+
router.use(strategy.routes());
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return router;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Read the session attached by `authenticate(...)`. Returns `undefined`
|
|
136
|
+
* if no session is set (e.g. on routes that don't have auth middleware).
|
|
137
|
+
*/
|
|
138
|
+
export function getSession(res) {
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
|
140
|
+
return res.locals[SESSION_KEY];
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=compose.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../src/auth/compose.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Amodal Labs, Inc.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
7
|
+
import express from 'express';
|
|
8
|
+
import supertest from 'supertest';
|
|
9
|
+
import { authenticate, authRouter, getSession } from './compose.js';
|
|
10
|
+
function strategy(name, apply, fn) {
|
|
11
|
+
return { name, valid: apply, authenticate: fn };
|
|
12
|
+
}
|
|
13
|
+
function buildApp(...mw) {
|
|
14
|
+
const app = express();
|
|
15
|
+
for (const m of mw)
|
|
16
|
+
app.use(m);
|
|
17
|
+
app.get('/protected', (_req, res) => {
|
|
18
|
+
res.json({ session: res.locals['session'] ?? null });
|
|
19
|
+
});
|
|
20
|
+
return app;
|
|
21
|
+
}
|
|
22
|
+
describe('authenticate', () => {
|
|
23
|
+
it('returns 401 when no strategy is valid', async () => {
|
|
24
|
+
const app = buildApp(authenticate(strategy('a', () => false, () => Promise.reject(new Error('unreachable'))), strategy('b', () => false, () => Promise.reject(new Error('unreachable')))));
|
|
25
|
+
const res = await supertest(app).get('/protected');
|
|
26
|
+
expect(res.status).toBe(401);
|
|
27
|
+
expect(res.body).toEqual({
|
|
28
|
+
error: { code: 'UNAUTHORIZED', message: 'Missing authentication' },
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
it('first strategy whose valid returns true and authenticates wins', async () => {
|
|
32
|
+
const wins = vi.fn(() => Promise.resolve({
|
|
33
|
+
kind: 'authenticated',
|
|
34
|
+
session: { user: { id: 'u1' }, method: 'first' },
|
|
35
|
+
}));
|
|
36
|
+
const skipped = vi.fn();
|
|
37
|
+
const app = buildApp(authenticate(strategy('first', () => true, wins), strategy('second', () => true, skipped)));
|
|
38
|
+
const res = await supertest(app).get('/protected');
|
|
39
|
+
expect(res.status).toBe(200);
|
|
40
|
+
expect(res.body.session.method).toBe('first');
|
|
41
|
+
expect(wins).toHaveBeenCalledTimes(1);
|
|
42
|
+
expect(skipped).not.toHaveBeenCalled();
|
|
43
|
+
});
|
|
44
|
+
it('skips strategies whose valid returns false (cheap fall-through)', async () => {
|
|
45
|
+
const skipped = vi.fn();
|
|
46
|
+
const fallback = vi.fn(() => Promise.resolve({
|
|
47
|
+
kind: 'authenticated',
|
|
48
|
+
session: { user: { id: 'u1' }, method: 'fallback' },
|
|
49
|
+
}));
|
|
50
|
+
const app = buildApp(authenticate(strategy('skip', () => false, skipped), strategy('fallback', () => true, fallback)));
|
|
51
|
+
const res = await supertest(app).get('/protected');
|
|
52
|
+
expect(res.status).toBe(200);
|
|
53
|
+
expect(res.body.session.method).toBe('fallback');
|
|
54
|
+
expect(skipped).not.toHaveBeenCalled();
|
|
55
|
+
});
|
|
56
|
+
it("rejected result short-circuits with 401 (chain doesn't fall through)", async () => {
|
|
57
|
+
const fallback = vi.fn();
|
|
58
|
+
const app = buildApp(authenticate(strategy('rejector', () => true, () => Promise.resolve({ kind: 'rejected', reason: 'bad token' })), strategy('fallback', () => true, fallback)));
|
|
59
|
+
const res = await supertest(app).get('/protected');
|
|
60
|
+
expect(res.status).toBe(401);
|
|
61
|
+
expect(res.body).toEqual({
|
|
62
|
+
error: { code: 'UNAUTHORIZED', message: 'bad token' },
|
|
63
|
+
});
|
|
64
|
+
expect(fallback).not.toHaveBeenCalled();
|
|
65
|
+
});
|
|
66
|
+
it('a strategy that throws is treated as a rejection (defensive)', async () => {
|
|
67
|
+
const app = buildApp(authenticate(strategy('thrower', () => true, () => Promise.reject(new Error('boom')))));
|
|
68
|
+
const res = await supertest(app).get('/protected');
|
|
69
|
+
expect(res.status).toBe(401);
|
|
70
|
+
expect(res.body.error.message).toBe('boom');
|
|
71
|
+
});
|
|
72
|
+
it('logger receives a structured event per request', async () => {
|
|
73
|
+
const events = [];
|
|
74
|
+
const app = buildApp(authenticate(strategy('s1', () => true, () => Promise.resolve({
|
|
75
|
+
kind: 'authenticated',
|
|
76
|
+
session: { user: { id: 'u1' }, method: 's1' },
|
|
77
|
+
})), { logger: (e) => events.push(e) }));
|
|
78
|
+
await supertest(app).get('/protected');
|
|
79
|
+
expect(events).toHaveLength(1);
|
|
80
|
+
const ev = events[0];
|
|
81
|
+
expect(ev.strategy).toBe('s1');
|
|
82
|
+
expect(ev.outcome).toBe('authenticated');
|
|
83
|
+
expect(typeof ev.durationMs).toBe('number');
|
|
84
|
+
});
|
|
85
|
+
it('logger sees no-match when nothing applies', async () => {
|
|
86
|
+
const events = [];
|
|
87
|
+
const app = buildApp(authenticate(strategy('s1', () => false, () => Promise.reject(new Error('unreachable'))), { logger: (e) => events.push(e) }));
|
|
88
|
+
await supertest(app).get('/protected');
|
|
89
|
+
expect(events).toHaveLength(1);
|
|
90
|
+
const ev = events[0];
|
|
91
|
+
expect(ev.strategy).toBeNull();
|
|
92
|
+
expect(ev.outcome).toBe('no-match');
|
|
93
|
+
});
|
|
94
|
+
it('different routes can use different strategy chains (Studio vs end-user)', async () => {
|
|
95
|
+
const platformJwt = strategy('platform-jwt', (req) => req.headers['authorization'] === 'Bearer platform', () => Promise.resolve({
|
|
96
|
+
kind: 'authenticated',
|
|
97
|
+
session: { user: { id: 'admin' }, method: 'platform-jwt' },
|
|
98
|
+
}));
|
|
99
|
+
const endUser = strategy('end-user', (req) => req.headers['authorization'] === 'Bearer end-user', () => Promise.resolve({
|
|
100
|
+
kind: 'authenticated',
|
|
101
|
+
session: { user: { id: 'enduser' }, method: 'end-user' },
|
|
102
|
+
}));
|
|
103
|
+
const app = express();
|
|
104
|
+
app.use('/api/files', authenticate(platformJwt));
|
|
105
|
+
app.use('/chat', authenticate(platformJwt, endUser));
|
|
106
|
+
app.get('/api/files', (_req, res) => res.json({ m: res.locals['session'].method }));
|
|
107
|
+
app.get('/chat', (_req, res) => res.json({ m: res.locals['session'].method }));
|
|
108
|
+
// Studio route accepts platform JWT
|
|
109
|
+
let res = await supertest(app).get('/api/files').set('Authorization', 'Bearer platform');
|
|
110
|
+
expect(res.status).toBe(200);
|
|
111
|
+
expect(res.body.m).toBe('platform-jwt');
|
|
112
|
+
// Studio route REJECTS end-user token (strategy not in list)
|
|
113
|
+
res = await supertest(app).get('/api/files').set('Authorization', 'Bearer end-user');
|
|
114
|
+
expect(res.status).toBe(401);
|
|
115
|
+
// Chat route accepts end-user token
|
|
116
|
+
res = await supertest(app).get('/chat').set('Authorization', 'Bearer end-user');
|
|
117
|
+
expect(res.status).toBe(200);
|
|
118
|
+
expect(res.body.m).toBe('end-user');
|
|
119
|
+
// Chat route also accepts platform JWT (Studio dev testing)
|
|
120
|
+
res = await supertest(app).get('/chat').set('Authorization', 'Bearer platform');
|
|
121
|
+
expect(res.status).toBe(200);
|
|
122
|
+
expect(res.body.m).toBe('platform-jwt');
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe('authRouter', () => {
|
|
126
|
+
it('mounts strategy-supplied login routes', async () => {
|
|
127
|
+
const oauthMock = {
|
|
128
|
+
name: 'oauth-mock',
|
|
129
|
+
valid: () => false,
|
|
130
|
+
authenticate: () => Promise.resolve({ kind: 'rejected', reason: 'never' }),
|
|
131
|
+
routes: () => {
|
|
132
|
+
const r = express.Router();
|
|
133
|
+
r.get('/auth/oauth-mock/start', (_req, res) => res.json({ redirected: true }));
|
|
134
|
+
return r;
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const app = express();
|
|
138
|
+
app.use(authRouter(oauthMock));
|
|
139
|
+
const res = await supertest(app).get('/auth/oauth-mock/start');
|
|
140
|
+
expect(res.status).toBe(200);
|
|
141
|
+
expect(res.body).toEqual({ redirected: true });
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
describe('getSession', () => {
|
|
145
|
+
it('returns the session attached by authenticate', async () => {
|
|
146
|
+
const app = express();
|
|
147
|
+
app.use(authenticate(strategy('s1', () => true, () => Promise.resolve({
|
|
148
|
+
kind: 'authenticated',
|
|
149
|
+
session: { user: { id: 'u1' }, method: 's1' },
|
|
150
|
+
}))));
|
|
151
|
+
app.get('/x', (_req, res) => {
|
|
152
|
+
const s = getSession(res);
|
|
153
|
+
res.json({ id: s?.user.id });
|
|
154
|
+
});
|
|
155
|
+
const r = await supertest(app).get('/x');
|
|
156
|
+
expect(r.body).toEqual({ id: 'u1' });
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
//# sourceMappingURL=compose.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compose.test.js","sourceRoot":"","sources":["../../../src/auth/compose.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAC;AAChD,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAC,YAAY,EAAE,UAAU,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAGlE,SAAS,QAAQ,CACf,IAAY,EACZ,KAAwC,EACxC,EAAiD;IAEjD,OAAO,EAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAC,CAAC;AAChD,CAAC;AAED,SAAS,QAAQ,CAAC,GAAG,EAA4B;IAC/C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,KAAK,MAAM,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IAC/B,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;QAClC,GAAG,CAAC,IAAI,CAAC,EAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,IAAI,EAAC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAC1E,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAC3E,CACF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACvB,KAAK,EAAE,EAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,wBAAwB,EAAC;SACjE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CACtB,OAAO,CAAC,OAAO,CAAa;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,IAAI,EAAC,EAAE,MAAM,EAAE,OAAO,EAAC;SAC7C,CAAC,CACH,CAAC;QACF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,EACnC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CACxC,CACF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9C,MAAM,CAAC,IAAI,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAC1B,OAAO,CAAC,OAAO,CAAa;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,IAAI,EAAC,EAAE,MAAM,EAAE,UAAU,EAAC;SAChD,CAAC,CACH,CAAC;QACF,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EACtC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAC3C,CACF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CACpC,OAAO,CAAC,OAAO,CAAa,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAC,CAAC,CACrE,EACD,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAC3C,CACF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACvB,KAAK,EAAE,EAAC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,EAAC;SACpD,CAAC,CAAC;QACH,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CACzE,CACF,CAAC;QACF,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAC9B,OAAO,CAAC,OAAO,CAAa;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,IAAI,EAAC,EAAE,MAAM,EAAE,IAAI,EAAC;SAC1C,CAAC,CACH,EACD,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC,CAChC,CACF,CAAC;QACF,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAA4D,CAAC;QAChF,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACzC,MAAM,CAAC,OAAO,EAAE,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,QAAQ,CAClB,YAAY,CACV,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EAC3E,EAAC,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAC,CAChC,CACF,CAAC;QACF,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/B,MAAM,EAAE,GAAG,MAAM,CAAC,CAAC,CAA+C,CAAC;QACnE,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,EACzC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,iBAAiB,EAC3D,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAa;YAChC,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,OAAO,EAAC,EAAE,MAAM,EAAE,cAAc,EAAC;SACvD,CAAC,CACH,CAAC;QACF,MAAM,OAAO,GAAG,QAAQ,CAAC,UAAU,EACjC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,iBAAiB,EAC3D,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAa;YAChC,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,SAAS,EAAC,EAAE,MAAM,EAAE,UAAU,EAAC;SACrD,CAAC,CACH,CAAC;QAEF,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC;QACjD,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QACrD,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAC,CAAC,CAAC,CAAC;QAClF,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAC,CAAC,CAAC,CAAC;QAE7E,oCAAoC;QACpC,IAAI,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QACzF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAExC,6DAA6D;QAC7D,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QACrF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAE7B,oCAAoC;QACpC,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEpC,4DAA4D;QAC5D,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAC;QAChF,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,SAAS,GAAiB;YAC9B,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK;YAClB,YAAY,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAC,CAAC;YACxE,MAAM,EAAE,GAAG,EAAE;gBACX,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;gBAC3B,CAAC,CAAC,GAAG,CAAC,wBAAwB,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAC,UAAU,EAAE,IAAI,EAAC,CAAC,CAAC,CAAC;gBAC7E,OAAO,CAAC,CAAC;YACX,CAAC;SACF,CAAC;QACF,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/B,MAAM,GAAG,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAC/D,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAC,UAAU,EAAE,IAAI,EAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,YAAY,EAAE,GAAG,EAAE;IAC1B,EAAE,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,GAAG,CAAC,GAAG,CACL,YAAY,CACV,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,CAC9B,OAAO,CAAC,OAAO,CAAa;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,EAAC,IAAI,EAAE,EAAC,EAAE,EAAE,IAAI,EAAC,EAAE,MAAM,EAAE,IAAI,EAAC;SAC1C,CAAC,CACH,CACF,CACF,CAAC;QACF,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC1B,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC1B,GAAG,CAAC,IAAI,CAAC,EAAC,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE,EAAC,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,EAAC,EAAE,EAAE,IAAI,EAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|