@sasjs/api 0.9.0 → 0.11.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/build/browser.js +457 -102
- package/build/cjs/code/index.js +12 -0
- package/build/cjs/code/service.js +57 -0
- package/build/cjs/corpus/code.js +99 -0
- package/build/cjs/corpus/runner.js +4 -1
- package/build/cjs/corpus/session.js +73 -0
- package/build/cjs/host/dispatch.js +65 -3
- package/build/cjs/index.js +5 -1
- package/build/cjs/routes/generated.js +16 -0
- package/build/cjs/routes/overlay.js +15 -20
- package/build/cjs/session/memory-host.js +74 -0
- package/build/cjs/session/service.js +65 -0
- package/build/esm/code/index.d.ts +10 -0
- package/build/esm/code/index.d.ts.map +1 -0
- package/build/esm/code/index.js +8 -0
- package/build/esm/code/service.d.ts +40 -0
- package/build/esm/code/service.d.ts.map +1 -0
- package/build/esm/code/service.js +53 -0
- package/build/esm/corpus/code.d.ts +17 -0
- package/build/esm/corpus/code.d.ts.map +1 -0
- package/build/esm/corpus/code.js +96 -0
- package/build/esm/corpus/drive.d.ts +9 -0
- package/build/esm/corpus/drive.d.ts.map +1 -1
- package/build/esm/corpus/runner.d.ts.map +1 -1
- package/build/esm/corpus/runner.js +4 -1
- package/build/esm/corpus/session.d.ts +16 -0
- package/build/esm/corpus/session.d.ts.map +1 -0
- package/build/esm/corpus/session.js +70 -0
- package/build/esm/host/dispatch.d.ts +9 -0
- package/build/esm/host/dispatch.d.ts.map +1 -1
- package/build/esm/host/dispatch.js +65 -3
- package/build/esm/index.d.ts +2 -0
- package/build/esm/index.d.ts.map +1 -1
- package/build/esm/index.js +2 -0
- package/build/esm/routes/generated.d.ts.map +1 -1
- package/build/esm/routes/generated.js +16 -0
- package/build/esm/routes/overlay.d.ts.map +1 -1
- package/build/esm/routes/overlay.js +15 -20
- package/build/esm/session/memory-host.d.ts +19 -0
- package/build/esm/session/memory-host.d.ts.map +1 -0
- package/build/esm/session/memory-host.js +70 -0
- package/build/esm/session/service.d.ts +62 -0
- package/build/esm/session/service.d.ts.map +1 -0
- package/build/esm/session/service.js +61 -0
- package/package.json +9 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Diagnostic, LintConfig } from '@sasjs/lint';
|
|
2
|
+
import type { Subject } from '../identity/types.js';
|
|
3
|
+
/** What both code routes take: a buffer, and where it came from when it has a file. */
|
|
4
|
+
export interface CodeRequest {
|
|
5
|
+
code: string;
|
|
6
|
+
/** The drive path the code belongs to, so a host can pick that folder's rules. */
|
|
7
|
+
filePath?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CodeServiceOptions {
|
|
10
|
+
/**
|
|
11
|
+
* The lint configuration that applies to a file.
|
|
12
|
+
*
|
|
13
|
+
* A host resolves the nearest `.sasjslint` at or above `filePath`, so each
|
|
14
|
+
* folder can carry its own rules; without one the linter's defaults apply,
|
|
15
|
+
* which is what an untitled buffer lints against.
|
|
16
|
+
*/
|
|
17
|
+
lintConfig?: (filePath?: string) => LintConfig | undefined;
|
|
18
|
+
}
|
|
19
|
+
export interface CodeService {
|
|
20
|
+
/** The diagnostics for a buffer. Nothing is written and nothing is executed. */
|
|
21
|
+
lint(caller: Subject | undefined, body: CodeRequest): Promise<Diagnostic[]>;
|
|
22
|
+
/** The formatted buffer. */
|
|
23
|
+
format(caller: Subject | undefined, body: CodeRequest): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The two editor routes, over `@sasjs/lint`.
|
|
27
|
+
*
|
|
28
|
+
* The linter is a dependency because it is the same everywhere, and it is
|
|
29
|
+
* deliberately NOT reachable from the package root: `@sasjs/lint` resolves its
|
|
30
|
+
* configuration by walking the filesystem, so it cannot be bundled into the
|
|
31
|
+
* service worker the browser entry produces. A host that wants these routes
|
|
32
|
+
* imports `@sasjs/api/code`, which is Node-only, exactly like the linter.
|
|
33
|
+
*
|
|
34
|
+
* The *rules* are the host's: which `.sasjslint` applies to a path is a fact
|
|
35
|
+
* about a drive, so `lintConfig` is asked for it.
|
|
36
|
+
* Neither route touches the drive or runs anything; they answer about the text
|
|
37
|
+
* in the request, which is what lets an editor check unsaved changes.
|
|
38
|
+
*/
|
|
39
|
+
export declare const createCodeService: (options?: CodeServiceOptions) => CodeService;
|
|
40
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/code/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGzD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAEnD,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAA;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,gFAAgF;IAChF,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAA;IAC3E,4BAA4B;IAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACxE;AAmBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAS,kBAAuB,KAC/B,WAqBF,CAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { formatText, lintText } from '@sasjs/lint';
|
|
2
|
+
import { ApiError } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `code`, refused the way the server's schema refuses it.
|
|
5
|
+
*
|
|
6
|
+
* Coercing with `String(...)` defeated the refusal: `{"code": 123}` was linted as
|
|
7
|
+
* the text "123" and `{"code": ""}` answered "required" where the server says it
|
|
8
|
+
* is empty. The three messages are Joi's, because they are the ones a client
|
|
9
|
+
* written against the document already handles.
|
|
10
|
+
*/
|
|
11
|
+
const codeOf = (body) => {
|
|
12
|
+
const value = body.code;
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
throw new ApiError(400, '"code" is required');
|
|
15
|
+
if (typeof value !== 'string')
|
|
16
|
+
throw new ApiError(400, '"code" must be a string');
|
|
17
|
+
if (value === '')
|
|
18
|
+
throw new ApiError(400, '"code" is not allowed to be empty');
|
|
19
|
+
return value;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The two editor routes, over `@sasjs/lint`.
|
|
23
|
+
*
|
|
24
|
+
* The linter is a dependency because it is the same everywhere, and it is
|
|
25
|
+
* deliberately NOT reachable from the package root: `@sasjs/lint` resolves its
|
|
26
|
+
* configuration by walking the filesystem, so it cannot be bundled into the
|
|
27
|
+
* service worker the browser entry produces. A host that wants these routes
|
|
28
|
+
* imports `@sasjs/api/code`, which is Node-only, exactly like the linter.
|
|
29
|
+
*
|
|
30
|
+
* The *rules* are the host's: which `.sasjslint` applies to a path is a fact
|
|
31
|
+
* about a drive, so `lintConfig` is asked for it.
|
|
32
|
+
* Neither route touches the drive or runs anything; they answer about the text
|
|
33
|
+
* in the request, which is what lets an editor check unsaved changes.
|
|
34
|
+
*/
|
|
35
|
+
export const createCodeService = (options = {}) => {
|
|
36
|
+
const requireCaller = (caller) => {
|
|
37
|
+
// both routes are permissioned at the gate, so a caller that reached the
|
|
38
|
+
// service without one is a host that skipped it rather than a public route
|
|
39
|
+
if (!caller)
|
|
40
|
+
throw new ApiError(401, 'Unauthorized');
|
|
41
|
+
};
|
|
42
|
+
const configFor = (filePath) => options.lintConfig ? options.lintConfig(filePath) : undefined;
|
|
43
|
+
return {
|
|
44
|
+
async lint(caller, body) {
|
|
45
|
+
requireCaller(caller);
|
|
46
|
+
return lintText(codeOf(body), configFor(body.filePath));
|
|
47
|
+
},
|
|
48
|
+
async format(caller, body) {
|
|
49
|
+
requireCaller(caller);
|
|
50
|
+
return formatText(codeOf(body), configFor(body.filePath));
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { CorpusCase } from './drive.js';
|
|
2
|
+
/**
|
|
3
|
+
* The editor's corpus: the two routes that answer about a buffer rather than a
|
|
4
|
+
* file.
|
|
5
|
+
*
|
|
6
|
+
* Both expectations are the linter's own output for the same input, measured
|
|
7
|
+
* against `@sasjs/lint` with no configuration - the defaults an untitled buffer
|
|
8
|
+
* lints against. A host that supplies a `.sasjslint` changes the rules for its
|
|
9
|
+
* own drive, which is why the case names no `filePath`.
|
|
10
|
+
*
|
|
11
|
+
* lintText('data _null_;\n x = 1;\nrun;\n')
|
|
12
|
+
* -> [{ message: 'File missing Doxygen header', lineNumber: 1, ... }]
|
|
13
|
+
* formatText(...)
|
|
14
|
+
* -> the header block, then the program with its spacing normalised
|
|
15
|
+
*/
|
|
16
|
+
export declare const CODE_CORPUS: CorpusCase[];
|
|
17
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../src/corpus/code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,WAAW,EAAE,UAAU,EAoFnC,CAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor's corpus: the two routes that answer about a buffer rather than a
|
|
3
|
+
* file.
|
|
4
|
+
*
|
|
5
|
+
* Both expectations are the linter's own output for the same input, measured
|
|
6
|
+
* against `@sasjs/lint` with no configuration - the defaults an untitled buffer
|
|
7
|
+
* lints against. A host that supplies a `.sasjslint` changes the rules for its
|
|
8
|
+
* own drive, which is why the case names no `filePath`.
|
|
9
|
+
*
|
|
10
|
+
* lintText('data _null_;\n x = 1;\nrun;\n')
|
|
11
|
+
* -> [{ message: 'File missing Doxygen header', lineNumber: 1, ... }]
|
|
12
|
+
* formatText(...)
|
|
13
|
+
* -> the header block, then the program with its spacing normalised
|
|
14
|
+
*/
|
|
15
|
+
export const CODE_CORPUS = [
|
|
16
|
+
{
|
|
17
|
+
id: 'code-lint-missing-header',
|
|
18
|
+
route: 'POST /SASjsApi/code/lint',
|
|
19
|
+
description: 'answers the diagnostics for a buffer, without touching a file',
|
|
20
|
+
request: {
|
|
21
|
+
// the routes are permissioned; the runner resolves the fixture's admin
|
|
22
|
+
subject: { userId: 'user-admin', clientId: 'web_app' },
|
|
23
|
+
body: { code: 'data _null_;\n x = 1;\nrun;\n' }
|
|
24
|
+
},
|
|
25
|
+
expect: {
|
|
26
|
+
status: 200,
|
|
27
|
+
body: [
|
|
28
|
+
{
|
|
29
|
+
message: 'File missing Doxygen header',
|
|
30
|
+
lineNumber: 1,
|
|
31
|
+
startColumnNumber: 1,
|
|
32
|
+
endColumnNumber: 1,
|
|
33
|
+
severity: 1
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'code-lint-unauthenticated',
|
|
40
|
+
route: 'POST /SASjsApi/code/lint',
|
|
41
|
+
description: 'refuses a caller with no token',
|
|
42
|
+
request: { body: { code: 'data _null_; run;' } },
|
|
43
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
id: 'code-lint-non-string',
|
|
47
|
+
route: 'POST /SASjsApi/code/lint',
|
|
48
|
+
description: 'refuses a code value that is not a string, rather than coercing it',
|
|
49
|
+
request: {
|
|
50
|
+
subject: { userId: 'user-admin', clientId: 'web_app' },
|
|
51
|
+
body: { code: 123 }
|
|
52
|
+
},
|
|
53
|
+
expect: { status: 400, error: '"code" must be a string' }
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: 'code-lint-empty',
|
|
57
|
+
route: 'POST /SASjsApi/code/lint',
|
|
58
|
+
description: 'refuses an empty buffer, which is not the same as a missing one',
|
|
59
|
+
request: {
|
|
60
|
+
subject: { userId: 'user-admin', clientId: 'web_app' },
|
|
61
|
+
body: { code: '' }
|
|
62
|
+
},
|
|
63
|
+
expect: { status: 400, error: '"code" is not allowed to be empty' }
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'code-format-unauthenticated',
|
|
67
|
+
route: 'POST /SASjsApi/code/format',
|
|
68
|
+
description: 'refuses a caller with no token',
|
|
69
|
+
request: { body: { code: 'data _null_; run;' } },
|
|
70
|
+
expect: { status: 401, error: 'Unauthorized' }
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'code-format-non-string',
|
|
74
|
+
route: 'POST /SASjsApi/code/format',
|
|
75
|
+
description: 'refuses a code value that is not a string',
|
|
76
|
+
request: {
|
|
77
|
+
subject: { userId: 'user-admin', clientId: 'web_app' },
|
|
78
|
+
body: { code: 123 }
|
|
79
|
+
},
|
|
80
|
+
expect: { status: 400, error: '"code" must be a string' }
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 'code-format-adds-the-header',
|
|
84
|
+
route: 'POST /SASjsApi/code/format',
|
|
85
|
+
description: 'answers the formatted buffer, header block included',
|
|
86
|
+
request: {
|
|
87
|
+
// the routes are permissioned; the runner resolves the fixture's admin
|
|
88
|
+
subject: { userId: 'user-admin', clientId: 'web_app' },
|
|
89
|
+
body: { code: 'data _null_;\n x = 1;\nrun;\n' }
|
|
90
|
+
},
|
|
91
|
+
expect: {
|
|
92
|
+
status: 200,
|
|
93
|
+
body: '/**\n @file\n @brief <Your brief here>\n\n <h4> SAS Macros </h4>\n\n**/\ndata _null_;\n x = 1;\nrun;\n'
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
];
|
|
@@ -47,6 +47,15 @@ export interface CorpusCase {
|
|
|
47
47
|
* minted token is not a value a case can state.
|
|
48
48
|
*/
|
|
49
49
|
keys?: string[];
|
|
50
|
+
/**
|
|
51
|
+
* How the body travels, where a string could be either. `json` is a JSON
|
|
52
|
+
* string on the wire - what tsoa emits for `Promise<string>` and what the
|
|
53
|
+
* server's `res.json` sends; `text` is a body the route sends itself, which
|
|
54
|
+
* the document carries in the `description` with no content. A case that
|
|
55
|
+
* names neither is read as `text` when the document declares no content and
|
|
56
|
+
* as `json` when it does.
|
|
57
|
+
*/
|
|
58
|
+
encoding?: 'json' | 'text';
|
|
50
59
|
};
|
|
51
60
|
}
|
|
52
61
|
export declare const DRIVE_CORPUS: CorpusCase[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drive.d.ts","sourceRoot":"","sources":["../../../src/corpus/drive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAA;IACV,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAA;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,UAAU,CAAA;IACnB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;QAC9B,yDAAyD;QACzD,KAAK,CAAC,EAAE,MAAM,CAAA;QACd;;;;WAIG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC;;;WAGG;QACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;
|
|
1
|
+
{"version":3,"file":"drive.d.ts","sourceRoot":"","sources":["../../../src/corpus/drive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAA;IACV,kEAAkE;IAClE,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAA;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,UAAU,CAAA;IACnB,MAAM,EAAE;QACN,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,OAAO,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;QAC9B,yDAAyD;QACzD,KAAK,CAAC,EAAE,MAAM,CAAA;QACd;;;;WAIG;QACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QACjC;;;WAGG;QACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;QACf;;;;;;;WAOG;QACH,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAC3B,CAAA;CACF;AAmBD,eAAO,MAAM,YAAY,EAAE,UAAU,EAqapC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/corpus/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/corpus/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AAWnD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;IAC9B,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,UAAU,UAAU,KAAG,QAmBlD,CAAA;AAkBD;;;;GAIG;AACH,eAAO,MAAM,OAAO,GAClB,UAAU,UAAU,EACpB,WAAU,QAAgC,KACzC,OAAO,CAAC,UAAU,CAepB,CAAA"}
|
|
@@ -3,6 +3,8 @@ import { createDriveService } from '../drive/service.js';
|
|
|
3
3
|
import { MemoryDriveStore } from '../drive/memory-store.js';
|
|
4
4
|
import { createIdentityService } from '../identity/service.js';
|
|
5
5
|
import { createRecordService } from '../identity/records.js';
|
|
6
|
+
import { createSessionService } from '../session/service.js';
|
|
7
|
+
import { MemorySessionHost } from '../session/memory-host.js';
|
|
6
8
|
import { MemoryIdentityStore } from '../identity/memory-store.js';
|
|
7
9
|
import { DEFAULT_IDENTITY } from '../identity/fixture.js';
|
|
8
10
|
import { isApiError } from '../types.js';
|
|
@@ -23,7 +25,8 @@ export const servicesFor = (testCase) => {
|
|
|
23
25
|
? new RefusingDriveStore(testCase.seed || {})
|
|
24
26
|
: new MemoryDriveStore(testCase.seed || {})),
|
|
25
27
|
identity: createIdentityService(identity),
|
|
26
|
-
records: createRecordService(identity)
|
|
28
|
+
records: createRecordService(identity),
|
|
29
|
+
session: createSessionService(identity, new MemorySessionHost())
|
|
27
30
|
};
|
|
28
31
|
};
|
|
29
32
|
/**
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The session routes' conformance corpus.
|
|
3
|
+
*
|
|
4
|
+
* Only the `200` answers are here, and that is a statement about the document
|
|
5
|
+
* rather than about the routes: sasjs/server documents these four with a `200`
|
|
6
|
+
* and nothing else, so `spec/corpus-parity.spec.ts` refuses a case that pins a
|
|
7
|
+
* refusal. Their `401` and the `404` that covers both "no such session" and
|
|
8
|
+
* "not yours" are pinned in `spec/session.spec.ts` instead, which the parity
|
|
9
|
+
* gate does not govern.
|
|
10
|
+
*
|
|
11
|
+
* The routes are in `documentRefusals.baseline.json` upstream; when they carry
|
|
12
|
+
* their `@Response(401)` and `@Response(404)`, those cases move here.
|
|
13
|
+
*/
|
|
14
|
+
import type { CorpusCase } from './drive.js';
|
|
15
|
+
export declare const SESSION_CORPUS: CorpusCase[];
|
|
16
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/corpus/session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAK5C,eAAO,MAAM,cAAc,EAAE,UAAU,EAmEtC,CAAA"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const ADMIN = { userId: 'user-admin', clientId: 'web_app' };
|
|
2
|
+
const RESTRICTED = { userId: 'user-restricted', clientId: 'web_app' };
|
|
3
|
+
export const SESSION_CORPUS = [
|
|
4
|
+
{
|
|
5
|
+
id: 'session-summary-own',
|
|
6
|
+
route: 'GET /SASjsApi/session/{sessionId}',
|
|
7
|
+
description: 'answers the owner the summary of their own session',
|
|
8
|
+
request: { subject: ADMIN, params: { sessionId: 'sess-admin' } },
|
|
9
|
+
expect: {
|
|
10
|
+
status: 200,
|
|
11
|
+
body: {
|
|
12
|
+
sessionId: 'sess-admin',
|
|
13
|
+
state: 'completed',
|
|
14
|
+
pid: 4242,
|
|
15
|
+
creationTimeStamp: '2026-11-07T09:00:00.000Z',
|
|
16
|
+
deathTimeStamp: '2026-11-07T09:00:04.000Z',
|
|
17
|
+
expiresAfterMins: 60
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'session-summary-as-admin',
|
|
23
|
+
route: 'GET /SASjsApi/session/{sessionId}',
|
|
24
|
+
description: "answers an administrator another account's session",
|
|
25
|
+
request: { subject: ADMIN, params: { sessionId: 'sess-restricted' } },
|
|
26
|
+
expect: {
|
|
27
|
+
status: 200,
|
|
28
|
+
body: {
|
|
29
|
+
sessionId: 'sess-restricted',
|
|
30
|
+
state: 'running',
|
|
31
|
+
pid: 4243,
|
|
32
|
+
creationTimeStamp: '2026-11-07T09:05:00.000Z'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'session-log-own',
|
|
38
|
+
route: 'GET /SASjsApi/session/{sessionId}/log',
|
|
39
|
+
description: 'answers the log this session wrote',
|
|
40
|
+
request: { subject: ADMIN, params: { sessionId: 'sess-admin' } },
|
|
41
|
+
expect: {
|
|
42
|
+
status: 200,
|
|
43
|
+
contents: 'NOTE: session started\nNOTE: session completed\n'
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'session-log-nothing-written',
|
|
48
|
+
route: 'GET /SASjsApi/session/{sessionId}/log',
|
|
49
|
+
description: 'answers an empty body for a session that has written nothing',
|
|
50
|
+
request: { subject: ADMIN, params: { sessionId: 'sess-silent' } },
|
|
51
|
+
expect: { status: 200, contents: '' }
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'session-output-own',
|
|
55
|
+
route: 'GET /SASjsApi/session/{sessionId}/output',
|
|
56
|
+
description: 'answers the webout this session produced',
|
|
57
|
+
request: { subject: ADMIN, params: { sessionId: 'sess-admin' } },
|
|
58
|
+
expect: { status: 200, contents: '<html><body>42</body></html>' }
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'session-state-own',
|
|
62
|
+
route: 'GET /SASjsApi/session/{sessionId}/state',
|
|
63
|
+
description: "answers the run time's state for this session",
|
|
64
|
+
request: { subject: RESTRICTED, params: { sessionId: 'sess-restricted' } },
|
|
65
|
+
expect: {
|
|
66
|
+
status: 200,
|
|
67
|
+
body: { state: 'running', pid: 4243, lineCount: 3 }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { DriveService } from '../drive/service.js';
|
|
2
2
|
import type { IdentityService } from '../identity/service.js';
|
|
3
3
|
import type { RecordService } from '../identity/records.js';
|
|
4
|
+
import type { SessionService } from '../session/service.js';
|
|
5
|
+
import type { CodeService } from '../code/service.js';
|
|
4
6
|
import type { Subject } from '../identity/types.js';
|
|
5
7
|
export interface ApiRequest {
|
|
6
8
|
query?: Record<string, string | undefined>;
|
|
@@ -33,6 +35,8 @@ export interface ApiResponse {
|
|
|
33
35
|
contents?: string | Uint8Array;
|
|
34
36
|
}
|
|
35
37
|
export interface Services {
|
|
38
|
+
/** The editor routes, where the host supplies a linter. */
|
|
39
|
+
code?: CodeService;
|
|
36
40
|
drive: DriveService;
|
|
37
41
|
/**
|
|
38
42
|
* Users, clients and tokens. A host without one answers 501 for the identity
|
|
@@ -42,6 +46,11 @@ export interface Services {
|
|
|
42
46
|
identity?: IdentityService;
|
|
43
47
|
/** Users, groups, permission rules and clients. Absent, the record routes answer 501. */
|
|
44
48
|
records?: RecordService;
|
|
49
|
+
/**
|
|
50
|
+
* A triggered session's record, log, output and state. A host without one
|
|
51
|
+
* answers 501 for these four, naming the capability.
|
|
52
|
+
*/
|
|
53
|
+
session?: SessionService;
|
|
45
54
|
}
|
|
46
55
|
export declare const dispatch: (services: Services, routeId: string, request?: ApiRequest) => Promise<ApiResponse>;
|
|
47
56
|
/** Whether a route is one `dispatch` can answer today. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAOnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,yFAAyF;IACzF,OAAO,CAAC,EAAE,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAOnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,2DAA2D;IAC3D,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;IAC1B,yFAAyF;IACzF,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB;;;OAGG;IACH,OAAO,CAAC,EAAE,cAAc,CAAA;CACzB;AA2CD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,CA8brB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAgDlC,CAAA"}
|
|
@@ -21,8 +21,13 @@ const validate = (route, request) => {
|
|
|
21
21
|
const given = param.in === 'query'
|
|
22
22
|
? request.query?.[param.name]
|
|
23
23
|
: request.body?.[param.name];
|
|
24
|
-
|
|
24
|
+
// missing and empty are two refusals, the way the schema has them: a
|
|
25
|
+
// client that sent the field an empty value has a different bug from one
|
|
26
|
+
// that omitted it
|
|
27
|
+
if (given === undefined)
|
|
25
28
|
throw new ApiError(400, `"${param.name}" is required`);
|
|
29
|
+
if (given === '')
|
|
30
|
+
throw new ApiError(400, `"${param.name}" is not allowed to be empty`);
|
|
26
31
|
}
|
|
27
32
|
};
|
|
28
33
|
/** The drive path a write names: the query parameter wins over the form field. */
|
|
@@ -41,7 +46,7 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
41
46
|
const route = routeById(routeId);
|
|
42
47
|
if (!route)
|
|
43
48
|
throw new ApiError(404, `No such operation: ${routeId}`);
|
|
44
|
-
const { drive, identity, records } = services;
|
|
49
|
+
const { drive, identity, records, code } = services;
|
|
45
50
|
// The server authenticates before its payload schema runs, so an anonymous
|
|
46
51
|
// request with an incomplete body is a 401 there rather than a 400. The order
|
|
47
52
|
// is only observable on the route groups that gate on a caller: the sign-in
|
|
@@ -66,6 +71,12 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
66
71
|
const body = request.body || {};
|
|
67
72
|
const params = request.params || {};
|
|
68
73
|
/** The record service, or the 501 a host without one answers. */
|
|
74
|
+
/** The host's sessions, or the 501 that names the missing capability. */
|
|
75
|
+
const sessionRequired = () => {
|
|
76
|
+
if (!services.session)
|
|
77
|
+
throw new ApiError(501, `This host has no session service, so it cannot answer ${routeId}.`);
|
|
78
|
+
return services.session;
|
|
79
|
+
};
|
|
69
80
|
const required = () => {
|
|
70
81
|
if (!records)
|
|
71
82
|
throw new ApiError(501, `This host has no record service, so it cannot answer ${routeId}.`);
|
|
@@ -89,7 +100,7 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
89
100
|
status: 200,
|
|
90
101
|
body: await identity.token({
|
|
91
102
|
clientId: String(body.clientId ?? ''),
|
|
92
|
-
code:
|
|
103
|
+
code: body.code
|
|
93
104
|
})
|
|
94
105
|
};
|
|
95
106
|
case 'POST /SASjsApi/auth/refresh':
|
|
@@ -163,6 +174,31 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
163
174
|
}
|
|
164
175
|
case 'GET /SASjsApi/drive/filetree':
|
|
165
176
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
177
|
+
// ------------------------------------------------------------- sessions
|
|
178
|
+
case 'GET /SASjsApi/session/{sessionId}': {
|
|
179
|
+
return {
|
|
180
|
+
status: 200,
|
|
181
|
+
body: await sessionRequired().summary(await caller(), String(params.sessionId ?? ''))
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
case 'GET /SASjsApi/session/{sessionId}/log': {
|
|
185
|
+
return {
|
|
186
|
+
status: 200,
|
|
187
|
+
contents: await sessionRequired().log(await caller(), String(params.sessionId ?? ''))
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
case 'GET /SASjsApi/session/{sessionId}/output': {
|
|
191
|
+
return {
|
|
192
|
+
status: 200,
|
|
193
|
+
contents: await sessionRequired().output(await caller(), String(params.sessionId ?? ''))
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case 'GET /SASjsApi/session/{sessionId}/state': {
|
|
197
|
+
return {
|
|
198
|
+
status: 200,
|
|
199
|
+
body: await sessionRequired().state(await caller(), String(params.sessionId ?? ''))
|
|
200
|
+
};
|
|
201
|
+
}
|
|
166
202
|
// ------------------------------------------------------------- records
|
|
167
203
|
//
|
|
168
204
|
// The administration routes: users, groups, permission rules, clients and the
|
|
@@ -216,6 +252,26 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
216
252
|
status: 200,
|
|
217
253
|
body: await required().deleteUser(await caller(), { username: String(params.username ?? '') }, { password: body.password })
|
|
218
254
|
};
|
|
255
|
+
case 'POST /SASjsApi/code/lint':
|
|
256
|
+
if (!code)
|
|
257
|
+
throw new ApiError(501, `This host has no code service, so it cannot answer ${routeId}.`);
|
|
258
|
+
return {
|
|
259
|
+
status: 200,
|
|
260
|
+
body: await code.lint(await caller(), {
|
|
261
|
+
code: body.code,
|
|
262
|
+
filePath: body.filePath
|
|
263
|
+
})
|
|
264
|
+
};
|
|
265
|
+
case 'POST /SASjsApi/code/format':
|
|
266
|
+
if (!code)
|
|
267
|
+
throw new ApiError(501, `This host has no code service, so it cannot answer ${routeId}.`);
|
|
268
|
+
return {
|
|
269
|
+
status: 200,
|
|
270
|
+
body: await code.format(await caller(), {
|
|
271
|
+
code: body.code,
|
|
272
|
+
filePath: body.filePath
|
|
273
|
+
})
|
|
274
|
+
};
|
|
219
275
|
case 'GET /SASjsApi/info/authorizedRoutes':
|
|
220
276
|
return {
|
|
221
277
|
status: 200,
|
|
@@ -328,6 +384,12 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
328
384
|
};
|
|
329
385
|
/** Whether a route is one `dispatch` can answer today. */
|
|
330
386
|
export const HANDLED_ROUTES = [
|
|
387
|
+
'GET /SASjsApi/session/{sessionId}',
|
|
388
|
+
'GET /SASjsApi/session/{sessionId}/log',
|
|
389
|
+
'GET /SASjsApi/session/{sessionId}/output',
|
|
390
|
+
'GET /SASjsApi/session/{sessionId}/state',
|
|
391
|
+
'POST /SASjsApi/code/lint',
|
|
392
|
+
'POST /SASjsApi/code/format',
|
|
331
393
|
'GET /SASjsApi/info/authorizedRoutes',
|
|
332
394
|
'GET /SASjsApi/info/myAuthorizedRoutes',
|
|
333
395
|
'GET /SASjsApi/drive/file',
|
package/build/esm/index.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export * from './identity/routeAccess.js';
|
|
|
29
29
|
export type { ApiRequest, ApiResponse, Services } from './host/dispatch.js';
|
|
30
30
|
export { createIdentityService } from './identity/service.js';
|
|
31
31
|
export { createRecordService, sanitiseDisplayName } from './identity/records.js';
|
|
32
|
+
export { CODE_CORPUS } from './corpus/code.js';
|
|
32
33
|
export type { AuthorizedRoutesResponse, GroupDetailsResponse, GroupResponse, PermissionResponse, RecordServiceOptions, RecordService, UserDetailsResponse, UserResponse } from './identity/records.js';
|
|
33
34
|
export type { IdentityService, LoginResult, SessionResult } from './identity/service.js';
|
|
34
35
|
export { MemoryIdentityStore } from './identity/memory-store.js';
|
|
@@ -37,6 +38,7 @@ export type { IdentityFixture, IdentityFixtureClient, IdentityFixtureCode, Ident
|
|
|
37
38
|
export type { IdentityClient, IdentityClientRecord, IdentityGroupRecord, IdentityPermissionRecord, IdentityStore, IdentityUser, IdentityUserRecord, NewGroup, NewPermission, NewUser, Subject, TokenPair, UserChange } from './identity/types.js';
|
|
38
39
|
export { DRIVE_CORPUS } from './corpus/drive.js';
|
|
39
40
|
export { IDENTITY_CORPUS } from './corpus/identity.js';
|
|
41
|
+
export { SESSION_CORPUS } from './corpus/session.js';
|
|
40
42
|
export { RECORD_CORPUS, ADMIN_SIGNED_IN, PROVIDER_MANAGED } from './corpus/records.js';
|
|
41
43
|
export type { CorpusCase } from './corpus/drive.js';
|
|
42
44
|
export { runCase, servicesFor } from './corpus/runner.js';
|
package/build/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,cAAc,YAAY,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,OAAO,EACP,QAAQ,EACR,MAAM,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,UAAU,EACX,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAChE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG7D,cAAc,2BAA2B,CAAA;AACzC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChF,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACb,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,aAAa,EACd,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,OAAO,EACP,OAAO,EACP,SAAS,EACT,UAAU,EACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EACL,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,cAAc,YAAY,CAAA;AAC1B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,OAAO,EACP,QAAQ,EACR,MAAM,EACP,MAAM,kBAAkB,CAAA;AACzB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AACvD,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EAClB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,UAAU,EACX,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,SAAS,EACT,SAAS,EACV,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAChE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAG7D,cAAc,2BAA2B,CAAA;AACzC,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,YAAY,EACV,wBAAwB,EACxB,oBAAoB,EACpB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,mBAAmB,EACnB,YAAY,EACb,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EACf,WAAW,EACX,aAAa,EACd,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,aAAa,EACd,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,oBAAoB,EACpB,yBAAyB,EACzB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAA;AAC9B,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,QAAQ,EACR,aAAa,EACb,OAAO,EACP,OAAO,EACP,SAAS,EACT,UAAU,EACX,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EACL,aAAa,EACb,eAAe,EACf,gBAAgB,EACjB,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA"}
|
package/build/esm/index.js
CHANGED
|
@@ -25,9 +25,11 @@ export { dispatch, HANDLED_ROUTES } from './host/dispatch.js';
|
|
|
25
25
|
export * from './identity/routeAccess.js';
|
|
26
26
|
export { createIdentityService } from './identity/service.js';
|
|
27
27
|
export { createRecordService, sanitiseDisplayName } from './identity/records.js';
|
|
28
|
+
export { CODE_CORPUS } from './corpus/code.js';
|
|
28
29
|
export { MemoryIdentityStore } from './identity/memory-store.js';
|
|
29
30
|
export { DEFAULT_IDENTITY, IDENTITY_FORMAT, parseIdentity } from './identity/fixture.js';
|
|
30
31
|
export { DRIVE_CORPUS } from './corpus/drive.js';
|
|
31
32
|
export { IDENTITY_CORPUS } from './corpus/identity.js';
|
|
33
|
+
export { SESSION_CORPUS } from './corpus/session.js';
|
|
32
34
|
export { RECORD_CORPUS, ADMIN_SIGNED_IN, PROVIDER_MANAGED } from './corpus/records.js';
|
|
33
35
|
export { runCase, servicesFor } from './corpus/runner.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../../src/routes/generated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,eAAO,MAAM,gBAAgB,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../../src/routes/generated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAw4D5C,CAAA"}
|
|
@@ -344,6 +344,14 @@ export const GENERATED_ROUTES = [
|
|
|
344
344
|
{
|
|
345
345
|
status: 200,
|
|
346
346
|
description: 'Ok'
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
status: 400,
|
|
350
|
+
description: '"code" is required / "code" must be a string / "code" is not allowed to be empty'
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
status: 401,
|
|
354
|
+
description: 'Unauthorized'
|
|
347
355
|
}
|
|
348
356
|
]
|
|
349
357
|
},
|
|
@@ -372,6 +380,14 @@ export const GENERATED_ROUTES = [
|
|
|
372
380
|
{
|
|
373
381
|
status: 200,
|
|
374
382
|
description: 'Ok'
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
status: 400,
|
|
386
|
+
description: '"code" is required / "code" must be a string / "code" is not allowed to be empty'
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
status: 401,
|
|
390
|
+
description: 'Unauthorized'
|
|
375
391
|
}
|
|
376
392
|
]
|
|
377
393
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGzD,yDAAyD;AACzD,UAAU,aAAa;IACrB,YAAY,CAAC,EAAE,UAAU,EAAE,CAAA;IAC3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GACtC,CAAC;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAElE,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAczD,CAAA;
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGzD,yDAAyD;AACzD,UAAU,aAAa;IACrB,YAAY,CAAC,EAAE,UAAU,EAAE,CAAA;IAC3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GACtC,CAAC;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAElE,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAczD,CAAA;AAwCD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAgNhD,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,UAAU,EAA8C,CAAA"}
|