@webda/content-mapper 0.0.0-stage → 4.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +166 -0
- package/lib/coercions.d.ts +22 -0
- package/lib/coercions.d.ts.map +1 -0
- package/lib/coercions.js +10 -0
- package/lib/coercions.js.map +1 -0
- package/lib/context.d.ts +56 -0
- package/lib/context.d.ts.map +1 -0
- package/lib/context.js +111 -0
- package/lib/context.js.map +1 -0
- package/lib/defaults.d.ts +31 -0
- package/lib/defaults.d.ts.map +1 -0
- package/lib/defaults.js +33 -0
- package/lib/defaults.js.map +1 -0
- package/lib/generators/accessors.d.ts +15 -0
- package/lib/generators/accessors.d.ts.map +1 -0
- package/lib/generators/accessors.js +567 -0
- package/lib/generators/accessors.js.map +1 -0
- package/lib/generators/behaviors.d.ts +21 -0
- package/lib/generators/behaviors.d.ts.map +1 -0
- package/lib/generators/behaviors.js +292 -0
- package/lib/generators/behaviors.js.map +1 -0
- package/lib/generators/imports.d.ts +86 -0
- package/lib/generators/imports.d.ts.map +1 -0
- package/lib/generators/imports.js +205 -0
- package/lib/generators/imports.js.map +1 -0
- package/lib/generators/loadparameters.d.ts +7 -0
- package/lib/generators/loadparameters.d.ts.map +1 -0
- package/lib/generators/loadparameters.js +175 -0
- package/lib/generators/loadparameters.js.map +1 -0
- package/lib/generators/qlvalidator.d.ts +53 -0
- package/lib/generators/qlvalidator.d.ts.map +1 -0
- package/lib/generators/qlvalidator.js +238 -0
- package/lib/generators/qlvalidator.js.map +1 -0
- package/lib/index.d.ts +53 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +36 -0
- package/lib/index.js.map +1 -0
- package/lib/module-discovery.d.ts +211 -0
- package/lib/module-discovery.d.ts.map +1 -0
- package/lib/module-discovery.js +662 -0
- package/lib/module-discovery.js.map +1 -0
- package/lib/module.d.ts +213 -0
- package/lib/module.d.ts.map +1 -0
- package/lib/module.js +1243 -0
- package/lib/module.js.map +1 -0
- package/lib/plan.d.ts +101 -0
- package/lib/plan.d.ts.map +1 -0
- package/lib/plan.js +61 -0
- package/lib/plan.js.map +1 -0
- package/lib/property-order.d.ts +37 -0
- package/lib/property-order.d.ts.map +1 -0
- package/lib/property-order.js +141 -0
- package/lib/property-order.js.map +1 -0
- package/lib/schema/action.d.ts +46 -0
- package/lib/schema/action.d.ts.map +1 -0
- package/lib/schema/action.js +190 -0
- package/lib/schema/action.js.map +1 -0
- package/lib/schema/annotations.d.ts +63 -0
- package/lib/schema/annotations.d.ts.map +1 -0
- package/lib/schema/annotations.js +116 -0
- package/lib/schema/annotations.js.map +1 -0
- package/lib/schema/converter.d.ts +363 -0
- package/lib/schema/converter.d.ts.map +1 -0
- package/lib/schema/converter.js +1377 -0
- package/lib/schema/converter.js.map +1 -0
- package/lib/schema/jsdoc.d.ts +34 -0
- package/lib/schema/jsdoc.d.ts.map +1 -0
- package/lib/schema/jsdoc.js +206 -0
- package/lib/schema/jsdoc.js.map +1 -0
- package/lib/schema/model.d.ts +27 -0
- package/lib/schema/model.d.ts.map +1 -0
- package/lib/schema/model.js +86 -0
- package/lib/schema/model.js.map +1 -0
- package/lib/schema/project.d.ts +34 -0
- package/lib/schema/project.d.ts.map +1 -0
- package/lib/schema/project.js +305 -0
- package/lib/schema/project.js.map +1 -0
- package/lib/schema/service.d.ts +62 -0
- package/lib/schema/service.d.ts.map +1 -0
- package/lib/schema/service.js +130 -0
- package/lib/schema/service.js.map +1 -0
- package/lib/schema/types.d.ts +61 -0
- package/lib/schema/types.d.ts.map +1 -0
- package/lib/schema/types.js +36 -0
- package/lib/schema/types.js.map +1 -0
- package/lib/schema/worker-cli.d.ts +3 -0
- package/lib/schema/worker-cli.d.ts.map +1 -0
- package/lib/schema/worker-cli.js +7 -0
- package/lib/schema/worker-cli.js.map +1 -0
- package/lib/schema/worker.d.ts +81 -0
- package/lib/schema/worker.d.ts.map +1 -0
- package/lib/schema/worker.js +186 -0
- package/lib/schema/worker.js.map +1 -0
- package/lib/server.d.ts +2 -0
- package/lib/server.d.ts.map +1 -0
- package/lib/server.js +133 -0
- package/lib/server.js.map +1 -0
- package/lib/session.d.ts +84 -0
- package/lib/session.d.ts.map +1 -0
- package/lib/session.js +141 -0
- package/lib/session.js.map +1 -0
- package/lib/spans.d.ts +46 -0
- package/lib/spans.d.ts.map +1 -0
- package/lib/spans.js +64 -0
- package/lib/spans.js.map +1 -0
- package/lib/twopass.d.ts +45 -0
- package/lib/twopass.d.ts.map +1 -0
- package/lib/twopass.js +87 -0
- package/lib/twopass.js.map +1 -0
- package/lib/vite.d.ts +20 -0
- package/lib/vite.d.ts.map +1 -0
- package/lib/vite.js +41 -0
- package/lib/vite.js.map +1 -0
- package/package.json +93 -3
- package/README.md +0 -3
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { JSONSchema7 } from "./types.ts";
|
|
2
|
+
/** A single schema request. */
|
|
3
|
+
export interface SchemaRequest {
|
|
4
|
+
/** Caller-chosen key, echoed in the response. */
|
|
5
|
+
id: string;
|
|
6
|
+
/**
|
|
7
|
+
* What to generate. `topLevel` needs no class and answers with a map;
|
|
8
|
+
* `module` needs no class and answers with the whole `webda.module.json`
|
|
9
|
+
* as `{ module, namingViolations, errors }`.
|
|
10
|
+
*/
|
|
11
|
+
kind: "service" | "model" | "topLevel" | "module" | "emit";
|
|
12
|
+
/** Absolute path of the file declaring the class. */
|
|
13
|
+
file: string;
|
|
14
|
+
/** Class to generate for. */
|
|
15
|
+
className: string;
|
|
16
|
+
/** Add the free-form `openapi` property; moddas and beans only. */
|
|
17
|
+
addOpenApi?: boolean;
|
|
18
|
+
/** Base type the parameters derive from; `DeployerResources` for deployers. */
|
|
19
|
+
parametersBase?: string;
|
|
20
|
+
/** Source root, for a `topLevel` or `module` request. */
|
|
21
|
+
rootDir?: string;
|
|
22
|
+
/** Output root, for a `topLevel` or `module` request. */
|
|
23
|
+
outDir?: string;
|
|
24
|
+
/** Namespace prefix, for a `topLevel` or `module` request. */
|
|
25
|
+
namespace?: string;
|
|
26
|
+
/** Top-level `capabilities`, for a `module` request; defaults to package.json `webda.capabilities`. */
|
|
27
|
+
capabilities?: Record<string, string>;
|
|
28
|
+
}
|
|
29
|
+
/** A batch of requests against one project. */
|
|
30
|
+
export interface WorkerRequest {
|
|
31
|
+
/** Project root, or an explicit tsconfig path. */
|
|
32
|
+
project: string;
|
|
33
|
+
/** Requests to answer. */
|
|
34
|
+
requests: SchemaRequest[];
|
|
35
|
+
}
|
|
36
|
+
/** The worker's reply. */
|
|
37
|
+
export interface WorkerResponse {
|
|
38
|
+
/** Successful results, keyed by request id. */
|
|
39
|
+
results: Record<string, JSONSchema7>;
|
|
40
|
+
/** Failures, keyed by request id. */
|
|
41
|
+
errors: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Answer a batch of requests.
|
|
45
|
+
*
|
|
46
|
+
* One session, and therefore one `Program`, is built per batch — the caller
|
|
47
|
+
* batches precisely so that cost is paid once.
|
|
48
|
+
* @param request - the batch
|
|
49
|
+
* @returns results and per-request errors
|
|
50
|
+
*/
|
|
51
|
+
export declare function handle(request: WorkerRequest): WorkerResponse;
|
|
52
|
+
/** What an `emit` request reports. */
|
|
53
|
+
export interface EmitReport {
|
|
54
|
+
/** Pass-2 diagnostics, formatted as `tsc` would print them. */
|
|
55
|
+
diagnostics: string;
|
|
56
|
+
/** Number of diagnostics; nothing is written unless it is zero. */
|
|
57
|
+
diagnosticCount: number;
|
|
58
|
+
/** Files written. */
|
|
59
|
+
written: number;
|
|
60
|
+
/** Edits per generator, for the build log. */
|
|
61
|
+
editCounts: Record<string, number>;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Build a project through the two-pass tsgo pipeline and write its output.
|
|
65
|
+
*
|
|
66
|
+
* Replaces `tsProgram.emit` with the four `@webda/ts-plugin` transformers.
|
|
67
|
+
* Pass 1 plans the generated code over the authored sources; pass 2 type-
|
|
68
|
+
* checks and emits the rewritten text, so what ships is exactly what was
|
|
69
|
+
* checked. If pass 2 reports anything, nothing is written — the old emit
|
|
70
|
+
* wrote files even when `getPreEmitDiagnostics` failed.
|
|
71
|
+
* @param configFile - absolute tsconfig path
|
|
72
|
+
* @param rootDir - source root
|
|
73
|
+
* @returns diagnostics and what was written
|
|
74
|
+
*/
|
|
75
|
+
export declare function emitProject(configFile: string, rootDir: string): EmitReport;
|
|
76
|
+
/**
|
|
77
|
+
* Read one request from stdin and write one response to stdout.
|
|
78
|
+
* @returns a promise resolving when the response is written
|
|
79
|
+
*/
|
|
80
|
+
export declare function main(): Promise<void>;
|
|
81
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/schema/worker.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,+BAA+B;AAC/B,MAAM,WAAW,aAAa;IAC5B,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX;;;;OAIG;IACH,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IAC3D,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uGAAuG;IACvG,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,0BAA0B;AAC1B,MAAM,WAAW,cAAc;IAC7B,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,cAAc,CAuE7D;AAYD,sCAAsC;AACtC,MAAM,WAAW,UAAU;IACzB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,eAAe,EAAE,MAAM,CAAC;IACxB,qBAAqB;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAmB3E;AAuBD;;;GAGG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAK1C"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Out-of-process schema generation on the TypeScript 7.1 checker.
|
|
3
|
+
*
|
|
4
|
+
* `@webda/compiler` drives this as a subprocess rather than importing it.
|
|
5
|
+
* That is not indirection for its own sake: the compiler still runs on
|
|
6
|
+
* TypeScript 6 and this package needs 7.1, a package can declare only one
|
|
7
|
+
* `typescript`, and a process boundary is the only thing that lets the two
|
|
8
|
+
* coexist until the atomic switch.
|
|
9
|
+
*
|
|
10
|
+
* It began as a way to diff against `@webda/schema`, which generated these
|
|
11
|
+
* schemas before and has since been deleted; the protocol shape is
|
|
12
|
+
* unchanged from that comparison, which is why it names things rather than
|
|
13
|
+
* passing them.
|
|
14
|
+
*
|
|
15
|
+
* Protocol: one JSON request on stdin, one JSON response on stdout.
|
|
16
|
+
*
|
|
17
|
+
* ```
|
|
18
|
+
* { "project": "/abs/path", "requests": [
|
|
19
|
+
* { "id": "Webda/Store", "kind": "service", "file": "/abs/f.ts",
|
|
20
|
+
* "className": "Store", "addOpenApi": true }
|
|
21
|
+
* ]}
|
|
22
|
+
* -> { "results": { "Webda/Store": { } }, "errors": { } }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* `{ "id": "m", "kind": "module" }` answers with
|
|
26
|
+
* `{ module, namingViolations, errors }` — see `../module.ts`.
|
|
27
|
+
*/
|
|
28
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
29
|
+
import { dirname, join } from "node:path";
|
|
30
|
+
import { formatDiagnostics } from "typescript/unstable/sync";
|
|
31
|
+
import { runTwoPass } from "../twopass.js";
|
|
32
|
+
import { ModifierFlags } from "typescript/unstable/ast";
|
|
33
|
+
import * as is from "typescript/unstable/ast/is";
|
|
34
|
+
import { openSession } from "../context.js";
|
|
35
|
+
import { generateWebdaModule } from "../module.js";
|
|
36
|
+
import { generateModelSchemas } from "./model.js";
|
|
37
|
+
import { generateTopLevelSchemas, namespaceOf } from "./project.js";
|
|
38
|
+
import { generateServiceSchema } from "./service.js";
|
|
39
|
+
/**
|
|
40
|
+
* Answer a batch of requests.
|
|
41
|
+
*
|
|
42
|
+
* One session, and therefore one `Program`, is built per batch — the caller
|
|
43
|
+
* batches precisely so that cost is paid once.
|
|
44
|
+
* @param request - the batch
|
|
45
|
+
* @returns results and per-request errors
|
|
46
|
+
*/
|
|
47
|
+
export function handle(request) {
|
|
48
|
+
const response = { results: {}, errors: {} };
|
|
49
|
+
const configFile = request.project.endsWith(".json") ? request.project : join(request.project, "tsconfig.json");
|
|
50
|
+
const session = openSession(configFile, request.project);
|
|
51
|
+
try {
|
|
52
|
+
for (const item of request.requests) {
|
|
53
|
+
try {
|
|
54
|
+
if (item.kind === "topLevel") {
|
|
55
|
+
// Answers with the whole map: these entries record no provenance,
|
|
56
|
+
// so the caller cannot name them one at a time.
|
|
57
|
+
response.results[item.id] = generateTopLevelSchemas(session.ctx, {
|
|
58
|
+
appPath: request.project,
|
|
59
|
+
rootDir: item.rootDir ?? join(request.project, "src"),
|
|
60
|
+
outDir: item.outDir ?? join(request.project, "lib"),
|
|
61
|
+
namespace: item.namespace ?? namespaceOf(request.project)
|
|
62
|
+
});
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (item.kind === "emit") {
|
|
66
|
+
response.results[item.id] = emitProject(configFile, item.rootDir ?? join(request.project, "src"));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (item.kind === "module") {
|
|
70
|
+
// The whole module in one answer; naming violations and errors
|
|
71
|
+
// travel with it so the caller decides how to report them.
|
|
72
|
+
response.results[item.id] = generateWebdaModule(session.ctx, {
|
|
73
|
+
appPath: request.project,
|
|
74
|
+
rootDir: item.rootDir ?? join(request.project, "src"),
|
|
75
|
+
outDir: item.outDir ?? join(request.project, "lib"),
|
|
76
|
+
namespace: item.namespace ?? namespaceOf(request.project),
|
|
77
|
+
capabilities: item.capabilities
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const declaration = findClass(session.ctx.program, item.file, item.className);
|
|
82
|
+
if (!declaration) {
|
|
83
|
+
response.errors[item.id] = `class ${item.className} not found in ${item.file}`;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (item.kind === "model") {
|
|
87
|
+
response.results[item.id] = generateModelSchemas(declaration, {
|
|
88
|
+
project: session.ctx.project,
|
|
89
|
+
checker: session.ctx.checker
|
|
90
|
+
});
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const schema = generateServiceSchema(declaration, {
|
|
94
|
+
project: session.ctx.project,
|
|
95
|
+
checker: session.ctx.checker,
|
|
96
|
+
addOpenApi: item.addOpenApi,
|
|
97
|
+
parametersBase: item.parametersBase,
|
|
98
|
+
// The declaration's own name, not the requested one: a default
|
|
99
|
+
// export is requested as `"default"` but titled after the class.
|
|
100
|
+
title: declaration.name?.text ?? item.className
|
|
101
|
+
});
|
|
102
|
+
if (!schema) {
|
|
103
|
+
response.errors[item.id] = `no parameters type found on ${item.className}`;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
response.results[item.id] = schema;
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
// Per-request isolation, so one unconvertible type does not hide the
|
|
110
|
+
// rest of the batch. The converter still refuses to emit for it.
|
|
111
|
+
response.errors[item.id] = error instanceof Error ? error.message : String(error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
finally {
|
|
116
|
+
session.dispose();
|
|
117
|
+
}
|
|
118
|
+
return response;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Build a project through the two-pass tsgo pipeline and write its output.
|
|
122
|
+
*
|
|
123
|
+
* Replaces `tsProgram.emit` with the four `@webda/ts-plugin` transformers.
|
|
124
|
+
* Pass 1 plans the generated code over the authored sources; pass 2 type-
|
|
125
|
+
* checks and emits the rewritten text, so what ships is exactly what was
|
|
126
|
+
* checked. If pass 2 reports anything, nothing is written — the old emit
|
|
127
|
+
* wrote files even when `getPreEmitDiagnostics` failed.
|
|
128
|
+
* @param configFile - absolute tsconfig path
|
|
129
|
+
* @param rootDir - source root
|
|
130
|
+
* @returns diagnostics and what was written
|
|
131
|
+
*/
|
|
132
|
+
export function emitProject(configFile, rootDir) {
|
|
133
|
+
const result = runTwoPass({ configFile, rootDir, emit: true });
|
|
134
|
+
const conflicts = result.conflicts.map(c => `${c.fileName}: overlapping edits from ${c.a.source} and ${c.b.source}`);
|
|
135
|
+
const diagnostics = formatDiagnostics(result.diagnostics, {
|
|
136
|
+
getCanonicalFileName: name => name,
|
|
137
|
+
getCurrentDirectory: () => dirname(configFile),
|
|
138
|
+
getNewLine: () => "\n"
|
|
139
|
+
}) + conflicts.join("\n");
|
|
140
|
+
const diagnosticCount = result.diagnostics.length + conflicts.length;
|
|
141
|
+
let written = 0;
|
|
142
|
+
if (diagnosticCount === 0) {
|
|
143
|
+
for (const [path, text] of result.emitted) {
|
|
144
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
145
|
+
writeFileSync(path, text);
|
|
146
|
+
written++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return { diagnostics, diagnosticCount, written, editCounts: result.editCounts };
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Find a class declaration by file and name.
|
|
153
|
+
* @param program - the program to search
|
|
154
|
+
* @param file - absolute file path
|
|
155
|
+
* @param className - class name
|
|
156
|
+
* @returns the declaration, when found
|
|
157
|
+
*/
|
|
158
|
+
function findClass(program, file, className) {
|
|
159
|
+
const sourceFile = program.getSourceFile(file);
|
|
160
|
+
if (!sourceFile)
|
|
161
|
+
return undefined;
|
|
162
|
+
for (const statement of sourceFile.statements) {
|
|
163
|
+
const node = statement;
|
|
164
|
+
if (!is.isClassDeclaration(node))
|
|
165
|
+
continue;
|
|
166
|
+
if (node.name?.text === className)
|
|
167
|
+
return node;
|
|
168
|
+
// `webda.module.json` records a default export as `:default`, so there is
|
|
169
|
+
// no name to match on — the modifier is the only handle.
|
|
170
|
+
if (className === "default" && (node.modifierFlags & ModifierFlags.Default) !== 0)
|
|
171
|
+
return node;
|
|
172
|
+
}
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Read one request from stdin and write one response to stdout.
|
|
177
|
+
* @returns a promise resolving when the response is written
|
|
178
|
+
*/
|
|
179
|
+
export async function main() {
|
|
180
|
+
const chunks = [];
|
|
181
|
+
for await (const chunk of process.stdin)
|
|
182
|
+
chunks.push(chunk);
|
|
183
|
+
const request = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
184
|
+
process.stdout.write(JSON.stringify(handle(request)));
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/schema/worker.ts"],"names":[],"mappings":"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"}
|
package/lib/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":""}
|
package/lib/server.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The content mapper process.
|
|
3
|
+
*
|
|
4
|
+
* Speaks the TypeScript 7.1 content mapper protocol over stdio (JSON-RPC with
|
|
5
|
+
* LSP-style `Content-Length` framing). TypeScript drives every exchange:
|
|
6
|
+
* `initialize` -> `openProject` -> `transform`* -> `closeProject`. Mappers never
|
|
7
|
+
* send requests.
|
|
8
|
+
*
|
|
9
|
+
* All the interesting work is in {@link WarmSession}; this file is transport,
|
|
10
|
+
* project bookkeeping and timing instrumentation.
|
|
11
|
+
*
|
|
12
|
+
* Diagnostics go to a log file rather than stderr, because stderr is consumed by
|
|
13
|
+
* the compiler as mapper log output.
|
|
14
|
+
*/
|
|
15
|
+
import { appendFileSync } from "node:fs";
|
|
16
|
+
import { WarmSession } from "./session.js";
|
|
17
|
+
const LOG = process.env.WEBDA_MAPPER_LOG;
|
|
18
|
+
/**
|
|
19
|
+
* Append a line to the debug log, if one is configured.
|
|
20
|
+
* @param parts - message parts
|
|
21
|
+
*/
|
|
22
|
+
function log(...parts) {
|
|
23
|
+
if (!LOG)
|
|
24
|
+
return;
|
|
25
|
+
appendFileSync(LOG, parts.map(p => (typeof p === "string" ? p : JSON.stringify(p))).join(" ") + "\n");
|
|
26
|
+
}
|
|
27
|
+
const projects = new Map();
|
|
28
|
+
/**
|
|
29
|
+
* Write a JSON-RPC message with `Content-Length` framing.
|
|
30
|
+
* @param message - the message
|
|
31
|
+
*/
|
|
32
|
+
function send(message) {
|
|
33
|
+
const body = JSON.stringify(message);
|
|
34
|
+
process.stdout.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Handle one request and reply.
|
|
38
|
+
* @param message - the decoded request
|
|
39
|
+
*/
|
|
40
|
+
function handle(message) {
|
|
41
|
+
const { id, method, params } = message;
|
|
42
|
+
try {
|
|
43
|
+
let result;
|
|
44
|
+
switch (method) {
|
|
45
|
+
case "initialize":
|
|
46
|
+
// UTF-16 matches JavaScript string offsets exactly, so no re-encoding.
|
|
47
|
+
result = {
|
|
48
|
+
positionEncoding: params.positionEncodings.includes("utf-16") ? "utf-16" : params.positionEncodings[0],
|
|
49
|
+
diagnosticSource: "webda"
|
|
50
|
+
};
|
|
51
|
+
break;
|
|
52
|
+
case "openProject": {
|
|
53
|
+
const session = new WarmSession({
|
|
54
|
+
configFile: params.configFileName,
|
|
55
|
+
cwd: params.configFileName.replace(/[/\\][^/\\]+$/, ""),
|
|
56
|
+
storageModule: params.options?.storageModule ?? "@webda/models",
|
|
57
|
+
qlModule: params.options?.qlModule,
|
|
58
|
+
accessorsForAll: !!params.options?.accessorsForAll
|
|
59
|
+
});
|
|
60
|
+
projects.set(params.projectHandle, { session, transforms: 0, timings: [] });
|
|
61
|
+
log("openProject", params.configFileName, `startup=${session.startupMs.toFixed(0)}ms`, `files=${session.fileCount}`);
|
|
62
|
+
result = {};
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "transform": {
|
|
66
|
+
const entry = projects.get(params.projectHandle);
|
|
67
|
+
if (!entry)
|
|
68
|
+
throw new Error(`unknown projectHandle ${params.projectHandle}`);
|
|
69
|
+
const outcome = entry.session.transform(params.fileName, params.content);
|
|
70
|
+
entry.transforms++;
|
|
71
|
+
entry.timings.push(outcome.timing);
|
|
72
|
+
const t = outcome.timing;
|
|
73
|
+
log("transform", params.fileName.replace(/^.*\//, ""), `edits=${outcome.editCount}`, `spans=${outcome.mappings.length}`, `total=${t.totalMs.toFixed(1)}ms`, `(snapshot=${t.snapshotMs.toFixed(1)} analyze=${t.analyzeMs.toFixed(1)} splice=${t.spliceMs.toFixed(2)})`, t.changed ? "changed" : "cached");
|
|
74
|
+
// `diagnostics` reports problems against the *authored* file, which is
|
|
75
|
+
// how a generator surfaces something it cannot fix by rewriting.
|
|
76
|
+
result = {
|
|
77
|
+
text: outcome.text,
|
|
78
|
+
extension: ".ts",
|
|
79
|
+
mappings: outcome.mappings,
|
|
80
|
+
...(outcome.diagnostics.length ? { diagnostics: outcome.diagnostics } : {})
|
|
81
|
+
};
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
case "closeProject": {
|
|
85
|
+
const entry = projects.get(params.projectHandle);
|
|
86
|
+
if (entry) {
|
|
87
|
+
entry.session.dispose();
|
|
88
|
+
projects.delete(params.projectHandle);
|
|
89
|
+
log("closeProject", `transforms=${entry.transforms}`);
|
|
90
|
+
}
|
|
91
|
+
result = {};
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: `unknown method ${method}` } });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (id !== undefined)
|
|
99
|
+
send({ jsonrpc: "2.0", id, result });
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
log("ERROR", method, error?.stack ?? String(error));
|
|
103
|
+
if (id !== undefined)
|
|
104
|
+
send({ jsonrpc: "2.0", id, error: { code: -32603, message: String(error?.message ?? error) } });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
let buffer = Buffer.alloc(0);
|
|
108
|
+
process.stdin.on("data", (chunk) => {
|
|
109
|
+
buffer = Buffer.concat([buffer, chunk]);
|
|
110
|
+
for (;;) {
|
|
111
|
+
const headerEnd = buffer.indexOf("\r\n\r\n");
|
|
112
|
+
if (headerEnd < 0)
|
|
113
|
+
return;
|
|
114
|
+
const header = buffer.subarray(0, headerEnd).toString("ascii");
|
|
115
|
+
const match = /Content-Length:\s*(\d+)/i.exec(header);
|
|
116
|
+
if (!match) {
|
|
117
|
+
log("malformed header", header);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const length = Number(match[1]);
|
|
121
|
+
if (buffer.length < headerEnd + 4 + length)
|
|
122
|
+
return;
|
|
123
|
+
const body = buffer.subarray(headerEnd + 4, headerEnd + 4 + length).toString("utf8");
|
|
124
|
+
buffer = buffer.subarray(headerEnd + 4 + length);
|
|
125
|
+
handle(JSON.parse(body));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
process.on("exit", () => {
|
|
129
|
+
for (const entry of projects.values())
|
|
130
|
+
entry.session.dispose();
|
|
131
|
+
});
|
|
132
|
+
log("mapper started", process.argv.join(" "));
|
|
133
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/lib/session.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type GeneratedDiagnostic, type Generator } from "./plan.ts";
|
|
2
|
+
import { type MappedText } from "./spans.ts";
|
|
3
|
+
/** How to open the resident program. */
|
|
4
|
+
export interface WarmSessionOptions {
|
|
5
|
+
/** Absolute tsconfig path, as supplied by `openProject`. */
|
|
6
|
+
configFile: string;
|
|
7
|
+
/** Working directory for the resident API. */
|
|
8
|
+
cwd: string;
|
|
9
|
+
/** Module specifier providing `WEBDA_STORAGE`. */
|
|
10
|
+
storageModule?: string;
|
|
11
|
+
/** Treat every class as eligible, not just models. */
|
|
12
|
+
accessorsForAll?: boolean;
|
|
13
|
+
/** Module specifier providing the WebdaQL `escape` helper. */
|
|
14
|
+
qlModule?: string;
|
|
15
|
+
/** Override the generator set (tests). */
|
|
16
|
+
generators?: Generator[];
|
|
17
|
+
}
|
|
18
|
+
/** Per-request cost breakdown, in milliseconds. */
|
|
19
|
+
export interface TransformTiming {
|
|
20
|
+
/** Refreshing the resident snapshot after a buffer change. */
|
|
21
|
+
snapshotMs: number;
|
|
22
|
+
/** Running the generators, i.e. the checker queries. */
|
|
23
|
+
analyzeMs: number;
|
|
24
|
+
/** Splicing edits and building the span map. */
|
|
25
|
+
spliceMs: number;
|
|
26
|
+
totalMs: number;
|
|
27
|
+
/** False when the buffer was unchanged and the snapshot was reused. */
|
|
28
|
+
changed: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** A transform result plus what it cost. */
|
|
31
|
+
export interface TransformOutcome extends MappedText {
|
|
32
|
+
timing: TransformTiming;
|
|
33
|
+
editCount: number;
|
|
34
|
+
/** Generator diagnostics, for `TransformResult.diagnostics`. */
|
|
35
|
+
diagnostics: GeneratedDiagnostic[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A resident `Program` + `Checker` that answers `transform` requests.
|
|
39
|
+
*/
|
|
40
|
+
export declare class WarmSession {
|
|
41
|
+
private readonly api;
|
|
42
|
+
private readonly options;
|
|
43
|
+
private readonly generators;
|
|
44
|
+
/** Live buffer text per file, served to the resident program. */
|
|
45
|
+
private readonly overlay;
|
|
46
|
+
private snapshot;
|
|
47
|
+
/** Cost of the initial program construction. */
|
|
48
|
+
readonly startupMs: number;
|
|
49
|
+
/**
|
|
50
|
+
* Open the resident program for a project.
|
|
51
|
+
* @param options - how to open it
|
|
52
|
+
*/
|
|
53
|
+
constructor(options: WarmSessionOptions);
|
|
54
|
+
/**
|
|
55
|
+
* Files in the resident program, for benchmarking.
|
|
56
|
+
* @returns the source file count
|
|
57
|
+
*/
|
|
58
|
+
get fileCount(): number;
|
|
59
|
+
/**
|
|
60
|
+
* The single project held by the current snapshot.
|
|
61
|
+
* @returns the project
|
|
62
|
+
*/
|
|
63
|
+
private project;
|
|
64
|
+
/**
|
|
65
|
+
* Build an analysis context restricted to a single file.
|
|
66
|
+
*
|
|
67
|
+
* Restricting `sourceFiles` is what makes re-analysis incremental: the
|
|
68
|
+
* generators only walk the file that changed, while the checker still resolves
|
|
69
|
+
* types across the whole program.
|
|
70
|
+
* @param fileName - the file to analyse
|
|
71
|
+
* @returns a context the generators accept
|
|
72
|
+
*/
|
|
73
|
+
private contextFor;
|
|
74
|
+
/**
|
|
75
|
+
* Transform one file, refreshing the resident program only if its text moved.
|
|
76
|
+
* @param fileName - absolute path of the file being transformed
|
|
77
|
+
* @param content - the editor's current buffer text
|
|
78
|
+
* @returns generated text, span map and timings
|
|
79
|
+
*/
|
|
80
|
+
transform(fileName: string, content: string): TransformOutcome;
|
|
81
|
+
/** Shut down the resident server process. */
|
|
82
|
+
dispose(): void;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAwB,KAAK,mBAAmB,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AAC3F,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9D,wCAAwC;AACxC,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,UAAU,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,UAAU,CAAC,EAAE,SAAS,EAAE,CAAC;CAC1B;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,8DAA8D;IAC9D,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,WAAW,EAAE,mBAAmB,EAAE,CAAC;CACpC;AAID;;GAEG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,iEAAiE;IACjE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IACrD,OAAO,CAAC,QAAQ,CAAM;IACtB,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B;;;OAGG;IACH,YAAY,OAAO,EAAE,kBAAkB,EAYtC;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED;;;OAGG;IACH,OAAO,CAAC,OAAO;IAIf;;;;;;;;OAQG;IACH,OAAO,CAAC,UAAU;IASlB;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,gBAAgB,CA0C7D;IAED,6CAA6C;IAC7C,OAAO,IAAI,IAAI,CAMd;CACF"}
|
package/lib/session.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A warm analysis session for the content mapper.
|
|
3
|
+
*
|
|
4
|
+
* This is the part `transform` cannot do on its own. The protocol hands the
|
|
5
|
+
* mapper one file's text and nothing else — no `Program`, no `Checker` — but
|
|
6
|
+
* Webda's dominant coercions (`ManyToOne<T>` -> `ModelLink`, `OneToMany<T>` ->
|
|
7
|
+
* `ModelRelated`, `@WebdaAutoSetter` set-methods) all need type resolution. So
|
|
8
|
+
* the mapper runs *its own* TypeScript program over the original sources and
|
|
9
|
+
* keeps it warm across requests: pass 1 of the two-pass design, resident.
|
|
10
|
+
*
|
|
11
|
+
* Two details make it work:
|
|
12
|
+
*
|
|
13
|
+
* - **The live buffer is served through `fs.readFile`.** The editor's unsaved
|
|
14
|
+
* text is put in an overlay, so the resident program analyses exactly what the
|
|
15
|
+
* user sees rather than what is on disk.
|
|
16
|
+
* - **`.model.ts` degrades to plain TypeScript.** The mapper opens the project's
|
|
17
|
+
* own tsconfig without `runExternalCode`, so content mappers are inert there
|
|
18
|
+
* and no recursion is possible; the files are simply read as authored.
|
|
19
|
+
*/
|
|
20
|
+
import { API } from "typescript/unstable/sync";
|
|
21
|
+
import { createFileSystemLayer } from "typescript/unstable/fs";
|
|
22
|
+
import { baseNames, textOf, triviaOf } from "./context.js";
|
|
23
|
+
import { defaultGenerators } from "./defaults.js";
|
|
24
|
+
import { mergePlan } from "./plan.js";
|
|
25
|
+
import { buildMappedText } from "./spans.js";
|
|
26
|
+
const ms = (from) => Number(process.hrtime.bigint() - from) / 1e6;
|
|
27
|
+
/**
|
|
28
|
+
* A resident `Program` + `Checker` that answers `transform` requests.
|
|
29
|
+
*/
|
|
30
|
+
export class WarmSession {
|
|
31
|
+
api;
|
|
32
|
+
options;
|
|
33
|
+
generators;
|
|
34
|
+
/** Live buffer text per file, served to the resident program. */
|
|
35
|
+
overlay = new Map();
|
|
36
|
+
snapshot;
|
|
37
|
+
/** Cost of the initial program construction. */
|
|
38
|
+
startupMs;
|
|
39
|
+
/**
|
|
40
|
+
* Open the resident program for a project.
|
|
41
|
+
* @param options - how to open it
|
|
42
|
+
*/
|
|
43
|
+
constructor(options) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.generators = options.generators ?? defaultGenerators(options);
|
|
46
|
+
const t0 = process.hrtime.bigint();
|
|
47
|
+
this.api = new API({ cwd: options.cwd });
|
|
48
|
+
this.snapshot = this.api.createSnapshot({ openProjects: [options.configFile] });
|
|
49
|
+
if (!this.snapshot.getProjects()[0]) {
|
|
50
|
+
this.api.close();
|
|
51
|
+
throw new Error(`No project loaded for ${options.configFile}`);
|
|
52
|
+
}
|
|
53
|
+
this.startupMs = ms(t0);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Files in the resident program, for benchmarking.
|
|
57
|
+
* @returns the source file count
|
|
58
|
+
*/
|
|
59
|
+
get fileCount() {
|
|
60
|
+
return this.project().program.getSourceFileNames().length;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The single project held by the current snapshot.
|
|
64
|
+
* @returns the project
|
|
65
|
+
*/
|
|
66
|
+
project() {
|
|
67
|
+
return this.snapshot.getProjects()[0];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build an analysis context restricted to a single file.
|
|
71
|
+
*
|
|
72
|
+
* Restricting `sourceFiles` is what makes re-analysis incremental: the
|
|
73
|
+
* generators only walk the file that changed, while the checker still resolves
|
|
74
|
+
* types across the whole program.
|
|
75
|
+
* @param fileName - the file to analyse
|
|
76
|
+
* @returns a context the generators accept
|
|
77
|
+
*/
|
|
78
|
+
contextFor(fileName) {
|
|
79
|
+
const project = this.project();
|
|
80
|
+
const program = project.program;
|
|
81
|
+
const checker = project.checker;
|
|
82
|
+
const sf = program.getSourceFile(fileName);
|
|
83
|
+
const sourceFiles = sf ? [sf] : [];
|
|
84
|
+
return { project, program, checker, sourceFiles, textOf, triviaOf, baseNames };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Transform one file, refreshing the resident program only if its text moved.
|
|
88
|
+
* @param fileName - absolute path of the file being transformed
|
|
89
|
+
* @param content - the editor's current buffer text
|
|
90
|
+
* @returns generated text, span map and timings
|
|
91
|
+
*/
|
|
92
|
+
transform(fileName, content) {
|
|
93
|
+
const t0 = process.hrtime.bigint();
|
|
94
|
+
const changed = this.overlay.get(fileName) !== content;
|
|
95
|
+
if (changed) {
|
|
96
|
+
this.overlay.set(fileName, content);
|
|
97
|
+
const previous = this.snapshot;
|
|
98
|
+
// Two things are load-bearing here, and both were found the hard way:
|
|
99
|
+
//
|
|
100
|
+
// - a filesystem *layer* is consulted ahead of the host filesystem, which
|
|
101
|
+
// is how unsaved buffer text reaches the resident program. The
|
|
102
|
+
// spawn-time `APIOptions.fs` callback is not enough, because source
|
|
103
|
+
// files are cached and a later edit to the same path is never re-read.
|
|
104
|
+
// - `ensurePrograms` is required. Programs update lazily, so without it
|
|
105
|
+
// the new snapshot still hands back the previous program and every
|
|
106
|
+
// transform silently analyses stale text.
|
|
107
|
+
this.snapshot = this.snapshot.update({
|
|
108
|
+
fileSystem: createFileSystemLayer([...this.overlay]),
|
|
109
|
+
ensurePrograms: true
|
|
110
|
+
});
|
|
111
|
+
previous.dispose();
|
|
112
|
+
}
|
|
113
|
+
const snapshotMs = ms(t0);
|
|
114
|
+
const t1 = process.hrtime.bigint();
|
|
115
|
+
const ctx = this.contextFor(fileName);
|
|
116
|
+
const produced = ctx.sourceFiles.length ? this.generators.flatMap(g => g.analyze(ctx)) : [];
|
|
117
|
+
const { plan } = mergePlan(produced);
|
|
118
|
+
const edits = plan.get(fileName) ?? [];
|
|
119
|
+
const diagnostics = produced.filter(f => f.fileName === fileName).flatMap(f => f.diagnostics ?? []);
|
|
120
|
+
const analyzeMs = ms(t1);
|
|
121
|
+
const t2 = process.hrtime.bigint();
|
|
122
|
+
const mapped = buildMappedText(content, edits);
|
|
123
|
+
const spliceMs = ms(t2);
|
|
124
|
+
return {
|
|
125
|
+
...mapped,
|
|
126
|
+
diagnostics,
|
|
127
|
+
editCount: edits.length,
|
|
128
|
+
timing: { snapshotMs, analyzeMs, spliceMs, totalMs: ms(t0), changed }
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/** Shut down the resident server process. */
|
|
132
|
+
dispose() {
|
|
133
|
+
try {
|
|
134
|
+
this.snapshot?.dispose();
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
this.api.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=session.js.map
|