@superpanel-sdk/runtime 0.2.1 → 0.2.2
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/builder/codegen/generate.js +15 -9
- package/dist/builder/company-import-graph.d.ts +11 -0
- package/dist/builder/company-import-graph.js +105 -0
- package/dist/builder/compile/compile-sequence.d.ts +4 -1
- package/dist/builder/compile/compile-sequence.js +28 -9
- package/dist/builder/load.js +3 -3
- package/dist/builder/types.d.ts +5 -0
- package/dist/cli/serve/index.js +48 -36
- package/dist/compute-host/source-compute-host.d.ts +4 -0
- package/dist/compute-host/source-compute-host.js +5 -0
- package/dist/fn-context/index.d.ts +1 -1
- package/dist/fn-context/index.js +1 -1
- package/dist/fn-context/rpc/index.d.ts +1 -1
- package/dist/fn-context/rpc/index.js +1 -1
- package/dist/fn-context/rpc/request-id.d.ts +5 -0
- package/dist/fn-context/rpc/request-id.js +11 -0
- package/dist/fn-context/rpc/trpc-client.d.ts +2 -0
- package/dist/fn-context/rpc/trpc-client.js +2 -1
- package/dist/fn-context/rpc/trpc-init.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { withCompanyImportGraph } from '../company-import-graph.js';
|
|
3
4
|
import { listSequenceIds, loadCompanySchema, loadSequenceSlots } from '../load.js';
|
|
4
5
|
import { mergeEffectiveSchema } from '../merge.js';
|
|
5
6
|
import { findTsModule, resolveTsModule } from '../paths.js';
|
|
@@ -12,6 +13,9 @@ function sequenceCaseContextGenPath(repoRoot, sequenceId) {
|
|
|
12
13
|
return path.join(repoRoot, 'sequences', sequenceId, '__gen', 'case-context.type.ts');
|
|
13
14
|
}
|
|
14
15
|
export async function generateSequence(repoRoot, sequenceId, options = {}) {
|
|
16
|
+
return withCompanyImportGraph(repoRoot, () => generateSequenceInGraph(repoRoot, sequenceId, options));
|
|
17
|
+
}
|
|
18
|
+
async function generateSequenceInGraph(repoRoot, sequenceId, options) {
|
|
15
19
|
const schemaPath = resolveTsModule(repoRoot, 'schema', `No company schema found. Expected schema.ts or schema/index.ts under ${repoRoot}`);
|
|
16
20
|
const slotsPath = resolveTsModule(path.join(repoRoot, 'sequences', sequenceId), 'slots', `No sequence slots found. Expected sequences/${sequenceId}/slots.ts or sequences/${sequenceId}/slots/index.ts`);
|
|
17
21
|
const company = await loadCompanySchema(schemaPath);
|
|
@@ -38,15 +42,17 @@ export async function generateSequence(repoRoot, sequenceId, options = {}) {
|
|
|
38
42
|
};
|
|
39
43
|
}
|
|
40
44
|
export async function generateAll(repoRoot, options = {}) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
return withCompanyImportGraph(repoRoot, async () => {
|
|
46
|
+
const ids = options.sequenceIds ?? listSequenceIds(repoRoot);
|
|
47
|
+
if (ids.length === 0) {
|
|
48
|
+
throw new Error(`No sequences with slots found under ${path.join(repoRoot, 'sequences')} (expected slots.ts or slots/index.ts)`);
|
|
49
|
+
}
|
|
50
|
+
const results = [];
|
|
51
|
+
for (const sequenceId of ids) {
|
|
52
|
+
results.push(await generateSequence(repoRoot, sequenceId, options));
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
});
|
|
50
56
|
}
|
|
51
57
|
function assertFileMatches(outPath, next, repoRoot) {
|
|
52
58
|
let current = '';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Stamp a company-repo entry URL, or return it unchanged outside a graph. */
|
|
2
|
+
export declare function companyImportGraphHref(fileHref: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Load company-repo modules as one ESM graph.
|
|
5
|
+
*
|
|
6
|
+
* Nested calls (same async context) reuse the graph so compile + overlay
|
|
7
|
+
* `loadSequenceFunctions` share object identity. Overlapping outer calls wait
|
|
8
|
+
* their turn, then bump generation so watch sees new source.
|
|
9
|
+
*/
|
|
10
|
+
export declare function withCompanyImportGraph<T>(repoRoot: string, fn: () => Promise<T>): Promise<T>;
|
|
11
|
+
//# sourceMappingURL=company-import-graph.d.ts.map
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { registerHooks } from 'node:module';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
const COMPANY_GRAPH_GEN_QUERY = 'spgen';
|
|
7
|
+
const COMPANY_GRAPH_ROOT_QUERY = 'sproot';
|
|
8
|
+
const graph = new AsyncLocalStorage();
|
|
9
|
+
let generation = 0;
|
|
10
|
+
let registered = false;
|
|
11
|
+
let outerTail = Promise.resolve();
|
|
12
|
+
function filePathFromUrl(url) {
|
|
13
|
+
const parsed = new URL(url);
|
|
14
|
+
parsed.search = '';
|
|
15
|
+
parsed.hash = '';
|
|
16
|
+
return fileURLToPath(parsed);
|
|
17
|
+
}
|
|
18
|
+
function realPath(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
return realpathSync(filePath);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return path.resolve(filePath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function underRoot(filePath, root) {
|
|
27
|
+
const resolved = realPath(filePath);
|
|
28
|
+
const base = realPath(root);
|
|
29
|
+
return resolved === base || resolved.startsWith(`${base}${path.sep}`);
|
|
30
|
+
}
|
|
31
|
+
function inNodeModules(filePath, url) {
|
|
32
|
+
return (url.includes('/node_modules/') ||
|
|
33
|
+
url.includes('\\node_modules\\') ||
|
|
34
|
+
filePath.includes(`${path.sep}node_modules${path.sep}`));
|
|
35
|
+
}
|
|
36
|
+
function stampCompanyResolve(specifier, context, nextResolve) {
|
|
37
|
+
const result = nextResolve(specifier, context);
|
|
38
|
+
if (!result.url.startsWith('file:'))
|
|
39
|
+
return result;
|
|
40
|
+
const resolved = new URL(result.url);
|
|
41
|
+
if (resolved.searchParams.has(COMPANY_GRAPH_GEN_QUERY))
|
|
42
|
+
return result;
|
|
43
|
+
const parent = context.parentURL ? new URL(context.parentURL) : null;
|
|
44
|
+
const graphGeneration = parent?.searchParams.get(COMPANY_GRAPH_GEN_QUERY);
|
|
45
|
+
const repoRoot = parent?.searchParams.get(COMPANY_GRAPH_ROOT_QUERY);
|
|
46
|
+
if (!graphGeneration || !repoRoot)
|
|
47
|
+
return result;
|
|
48
|
+
let resolvedFile;
|
|
49
|
+
try {
|
|
50
|
+
resolvedFile = filePathFromUrl(result.url);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
if (inNodeModules(resolvedFile, result.url))
|
|
56
|
+
return result;
|
|
57
|
+
if (!underRoot(resolvedFile, repoRoot))
|
|
58
|
+
return result;
|
|
59
|
+
resolved.searchParams.set(COMPANY_GRAPH_GEN_QUERY, graphGeneration);
|
|
60
|
+
resolved.searchParams.set(COMPANY_GRAPH_ROOT_QUERY, repoRoot);
|
|
61
|
+
return { ...result, url: resolved.href };
|
|
62
|
+
}
|
|
63
|
+
function ensureHookRegistered() {
|
|
64
|
+
if (registered)
|
|
65
|
+
return;
|
|
66
|
+
registered = true;
|
|
67
|
+
registerHooks({ resolve: stampCompanyResolve });
|
|
68
|
+
}
|
|
69
|
+
/** Stamp a company-repo entry URL, or return it unchanged outside a graph. */
|
|
70
|
+
export function companyImportGraphHref(fileHref) {
|
|
71
|
+
const store = graph.getStore();
|
|
72
|
+
if (!store)
|
|
73
|
+
return fileHref;
|
|
74
|
+
const url = new URL(fileHref);
|
|
75
|
+
url.searchParams.set(COMPANY_GRAPH_GEN_QUERY, String(store.generation));
|
|
76
|
+
url.searchParams.set(COMPANY_GRAPH_ROOT_QUERY, store.repoRoot);
|
|
77
|
+
return url.href;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Load company-repo modules as one ESM graph.
|
|
81
|
+
*
|
|
82
|
+
* Nested calls (same async context) reuse the graph so compile + overlay
|
|
83
|
+
* `loadSequenceFunctions` share object identity. Overlapping outer calls wait
|
|
84
|
+
* their turn, then bump generation so watch sees new source.
|
|
85
|
+
*/
|
|
86
|
+
export async function withCompanyImportGraph(repoRoot, fn) {
|
|
87
|
+
ensureHookRegistered();
|
|
88
|
+
if (graph.getStore())
|
|
89
|
+
return fn();
|
|
90
|
+
const root = realpathSync(path.resolve(repoRoot));
|
|
91
|
+
let release;
|
|
92
|
+
const previous = outerTail;
|
|
93
|
+
outerTail = new Promise((resolve) => {
|
|
94
|
+
release = resolve;
|
|
95
|
+
});
|
|
96
|
+
await previous;
|
|
97
|
+
generation += 1;
|
|
98
|
+
try {
|
|
99
|
+
return await graph.run({ generation, repoRoot: root }, fn);
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
release();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=company-import-graph.js.map
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { FuncMap } from '../../core/functions/types.js';
|
|
2
2
|
import type { CompanySchema } from '../../core/slot/schema.js';
|
|
3
|
-
import type { SequenceBuild } from '../types.js';
|
|
3
|
+
import type { RuntimePackageSource, SequenceBuild } from '../types.js';
|
|
4
4
|
/** SequenceBuild / SequenceConfigBuild cache key. Bump when compiled `code` shape changes. */
|
|
5
5
|
export declare function runtimePackageVersion(): string;
|
|
6
|
+
export declare function runtimePackageSource(repoRoot: string): RuntimePackageSource;
|
|
6
7
|
/** Deep-clone company schema with derived computeFn normalized to string names. */
|
|
7
8
|
export declare function snapshotCompanySchema(company: CompanySchema, functions?: FuncMap): CompanySchema;
|
|
8
9
|
export declare function sequenceBuildPath(repoRoot: string, sequenceId: string): string;
|
|
@@ -12,6 +13,8 @@ export type CompileSequenceOptions = {
|
|
|
12
13
|
includeCode?: boolean;
|
|
13
14
|
/** Persist on SequenceBuild.sha. Defaults to `local`. */
|
|
14
15
|
sha?: string;
|
|
16
|
+
/** Dependency source captured before deployment rewrites package.json. */
|
|
17
|
+
runtimeSource?: RuntimePackageSource;
|
|
15
18
|
};
|
|
16
19
|
export type CompileSequenceResult = {
|
|
17
20
|
sequenceId: string;
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
4
4
|
import { resolveCaseContextViewFn } from '../../core/functions/define/case-context.js';
|
|
5
5
|
import { resolveFuncName } from '../../core/functions/func-ref.js';
|
|
6
6
|
import { compileSlotSchema } from '../../core/slot/compile-schema.js';
|
|
7
|
+
import { withCompanyImportGraph } from '../company-import-graph.js';
|
|
7
8
|
import { listSequenceIds, loadCompanySchema, loadSequenceAgendas, loadSequenceCaseContextView, loadSequenceFunctions, loadSequenceSlots, } from '../load.js';
|
|
8
9
|
import { findTsModule, resolveTsModule } from '../paths.js';
|
|
9
10
|
import { compileSequenceAgendas, fatalAgendaIssues } from './compile-agenda.js';
|
|
@@ -27,6 +28,18 @@ export function runtimePackageVersion() {
|
|
|
27
28
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
28
29
|
return pkg.version ?? '0.0.0';
|
|
29
30
|
}
|
|
31
|
+
export function runtimePackageSource(repoRoot) {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
34
|
+
const spec = pkg.dependencies?.['@superpanel-sdk/runtime'] ??
|
|
35
|
+
pkg.devDependencies?.['@superpanel-sdk/runtime'] ??
|
|
36
|
+
pkg.optionalDependencies?.['@superpanel-sdk/runtime'];
|
|
37
|
+
return spec?.startsWith('file:') || spec?.startsWith('workspace:') ? 'local-file' : 'npm';
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return 'npm';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
30
43
|
/** Deep-clone company schema with derived computeFn normalized to string names. */
|
|
31
44
|
export function snapshotCompanySchema(company, functions) {
|
|
32
45
|
const tables = {};
|
|
@@ -51,6 +64,9 @@ export function sequenceBuildPath(repoRoot, sequenceId) {
|
|
|
51
64
|
return path.join(repoRoot, 'sequences', sequenceId, 'build', 'sequence-build.json');
|
|
52
65
|
}
|
|
53
66
|
export async function compileSequence(repoRoot, sequenceId, options = {}) {
|
|
67
|
+
return withCompanyImportGraph(repoRoot, () => compileSequenceInGraph(repoRoot, sequenceId, options));
|
|
68
|
+
}
|
|
69
|
+
async function compileSequenceInGraph(repoRoot, sequenceId, options) {
|
|
54
70
|
const schemaPath = resolveTsModule(repoRoot, 'schema', `No company schema found. Expected schema.ts or schema/index.ts under ${repoRoot}`);
|
|
55
71
|
const sequenceDir = path.join(repoRoot, 'sequences', sequenceId);
|
|
56
72
|
const slotsPath = resolveTsModule(sequenceDir, 'slots', `No sequence slots found. Expected sequences/${sequenceId}/slots.ts or sequences/${sequenceId}/slots/index.ts`);
|
|
@@ -92,6 +108,7 @@ export async function compileSequence(repoRoot, sequenceId, options = {}) {
|
|
|
92
108
|
sequenceId,
|
|
93
109
|
sha: options.sha ?? 'local',
|
|
94
110
|
engineVersion: runtimePackageVersion(),
|
|
111
|
+
runtimeSource: options.runtimeSource ?? runtimePackageSource(repoRoot),
|
|
95
112
|
compiledAt: new Date().toISOString(),
|
|
96
113
|
companySchemaSnapshot: snapshotCompanySchema(company, functions),
|
|
97
114
|
schema: compiledAgendas.schema,
|
|
@@ -108,14 +125,16 @@ export async function compileSequence(repoRoot, sequenceId, options = {}) {
|
|
|
108
125
|
return { sequenceId, outPath, build, wrote: !options.dryRun };
|
|
109
126
|
}
|
|
110
127
|
export async function compileAll(repoRoot, options = {}) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
return withCompanyImportGraph(repoRoot, async () => {
|
|
129
|
+
const ids = options.sequenceIds ?? listSequenceIds(repoRoot);
|
|
130
|
+
if (ids.length === 0) {
|
|
131
|
+
throw new Error(`No sequences with slots found under ${path.join(repoRoot, 'sequences')} (expected slots.ts or slots/index.ts)`);
|
|
132
|
+
}
|
|
133
|
+
const results = [];
|
|
134
|
+
for (const sequenceId of ids) {
|
|
135
|
+
results.push(await compileSequence(repoRoot, sequenceId, options));
|
|
136
|
+
}
|
|
137
|
+
return results;
|
|
138
|
+
});
|
|
120
139
|
}
|
|
121
140
|
//# sourceMappingURL=compile-sequence.js.map
|
package/dist/builder/load.js
CHANGED
|
@@ -3,11 +3,11 @@ import path from 'node:path';
|
|
|
3
3
|
import { pathToFileURL } from 'node:url';
|
|
4
4
|
import { resolveCaseContextViewFn } from '../core/functions/define/case-context.js';
|
|
5
5
|
import { isAgendaTree, isSequenceAgendas } from '../core/stage/flatten.js';
|
|
6
|
+
import { companyImportGraphHref } from './company-import-graph.js';
|
|
6
7
|
import { findTsModule } from './paths.js';
|
|
7
8
|
async function importModule(filePath) {
|
|
8
|
-
const href = pathToFileURL(filePath).href;
|
|
9
|
-
|
|
10
|
-
const mod = await import(`${href}?t=${Date.now()}`);
|
|
9
|
+
const href = companyImportGraphHref(pathToFileURL(filePath).href);
|
|
10
|
+
const mod = await import(href);
|
|
11
11
|
return mod;
|
|
12
12
|
}
|
|
13
13
|
function isFuncMap(value) {
|
package/dist/builder/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { CaseContextViewRegistryEntry, FunctionRegistry } from '../core/fun
|
|
|
2
2
|
import type { CompanySchema, EffectiveSchema } from '../core/slot/schema.js';
|
|
3
3
|
import type { CompiledSlotSchema } from '../core/slot/types.js';
|
|
4
4
|
import type { CompiledAgenda } from '../core/stage/types.js';
|
|
5
|
+
export type RuntimePackageSource = 'local-file' | 'npm';
|
|
5
6
|
/**
|
|
6
7
|
* Compiled sequence artifact (Manual §9 subset; grows over time).
|
|
7
8
|
* Omitted for now: paramsDefaults, schedulers, caseManual.
|
|
@@ -13,6 +14,10 @@ export type SequenceBuild = {
|
|
|
13
14
|
sequenceId: string;
|
|
14
15
|
sha: string;
|
|
15
16
|
engineVersion: string;
|
|
17
|
+
/** Installed @superpanel-sdk/runtime version resolved from the company repo. */
|
|
18
|
+
runtimeVersion?: string;
|
|
19
|
+
/** Dependency source declared by the company repo. Optional for legacy persisted builds. */
|
|
20
|
+
runtimeSource?: RuntimePackageSource;
|
|
16
21
|
compiledAt: string;
|
|
17
22
|
companySchemaSnapshot: CompanySchema;
|
|
18
23
|
/** SlotEngine runtime schema (tables + derivedOrder / reverseDeps). */
|
package/dist/cli/serve/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import chokidar from 'chokidar';
|
|
3
3
|
import { compileAll } from '../../builder/compile/compile-sequence.js';
|
|
4
|
+
import { withCompanyImportGraph } from '../../builder/company-import-graph.js';
|
|
4
5
|
import { findTsModule } from '../../builder/paths.js';
|
|
5
6
|
import { listSequenceIds, loadSequenceCaseContextView, loadSequenceFunctions } from '../../builder/load.js';
|
|
6
7
|
import { createSourceComputeHost } from '../../compute-host/source-compute-host.js';
|
|
@@ -37,30 +38,34 @@ function isSchemaPath(repoRoot, file) {
|
|
|
37
38
|
return rel === 'schema.ts' || rel.startsWith(`schema${path.sep}`) || rel.startsWith('schema/');
|
|
38
39
|
}
|
|
39
40
|
async function loadOne(repoRoot, sequenceId, generation) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
return withCompanyImportGraph(repoRoot, async () => {
|
|
42
|
+
const [compiled] = await compileAll(repoRoot, {
|
|
43
|
+
sequenceIds: [sequenceId],
|
|
44
|
+
dryRun: true,
|
|
45
|
+
includeCode: false,
|
|
46
|
+
});
|
|
47
|
+
if (!compiled) {
|
|
48
|
+
throw new Error(`Failed to compile sequence ${sequenceId}`);
|
|
49
|
+
}
|
|
50
|
+
const sequenceDir = path.join(repoRoot, 'sequences', sequenceId);
|
|
51
|
+
const functionsPath = findTsModule(sequenceDir, 'functions');
|
|
52
|
+
if (!functionsPath) {
|
|
53
|
+
throw new Error(`No functions module for sequence ${sequenceId}`);
|
|
54
|
+
}
|
|
55
|
+
const functions = await loadSequenceFunctions(functionsPath);
|
|
56
|
+
const viewPath = findTsModule(sequenceDir, 'view');
|
|
57
|
+
const view = viewPath ? await loadSequenceCaseContextView(viewPath) : undefined;
|
|
58
|
+
return { build: compiled.build, functions, view, generation };
|
|
44
59
|
});
|
|
45
|
-
if (!compiled) {
|
|
46
|
-
throw new Error(`Failed to compile sequence ${sequenceId}`);
|
|
47
|
-
}
|
|
48
|
-
const sequenceDir = path.join(repoRoot, 'sequences', sequenceId);
|
|
49
|
-
const functionsPath = findTsModule(sequenceDir, 'functions');
|
|
50
|
-
if (!functionsPath) {
|
|
51
|
-
throw new Error(`No functions module for sequence ${sequenceId}`);
|
|
52
|
-
}
|
|
53
|
-
const functions = await loadSequenceFunctions(functionsPath);
|
|
54
|
-
const viewPath = findTsModule(sequenceDir, 'view');
|
|
55
|
-
const view = viewPath ? await loadSequenceCaseContextView(viewPath) : undefined;
|
|
56
|
-
return { build: compiled.build, functions, view, generation };
|
|
57
60
|
}
|
|
58
61
|
async function loadSequences(repoRoot, sequenceIds, generation) {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
return withCompanyImportGraph(repoRoot, async () => {
|
|
63
|
+
const out = new Map();
|
|
64
|
+
for (const sequenceId of sequenceIds) {
|
|
65
|
+
out.set(sequenceId, await loadOne(repoRoot, sequenceId, generation));
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
});
|
|
64
69
|
}
|
|
65
70
|
function send(ws, message) {
|
|
66
71
|
ws.send(encodeOverlayMessage(message));
|
|
@@ -74,6 +79,8 @@ async function handleExecute(loaded, message) {
|
|
|
74
79
|
caseContextView: loaded.view,
|
|
75
80
|
data: message.data,
|
|
76
81
|
runToken: message.runToken,
|
|
82
|
+
runtimeVersion: loaded.build.runtimeVersion ?? loaded.build.engineVersion,
|
|
83
|
+
runtimeSource: loaded.build.runtimeSource ?? 'npm',
|
|
77
84
|
getRequestId: () => message.logRequestId,
|
|
78
85
|
getTags: () => message.logTags,
|
|
79
86
|
});
|
|
@@ -249,32 +256,37 @@ export async function runServe(repoRoot, options = {}) {
|
|
|
249
256
|
logField('websocket', url);
|
|
250
257
|
let timer;
|
|
251
258
|
let pending = { all: false, ids: new Set() };
|
|
259
|
+
let flushing = false;
|
|
252
260
|
const flush = () => {
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
if (flushing)
|
|
262
|
+
return;
|
|
263
|
+
flushing = true;
|
|
255
264
|
void (async () => {
|
|
256
265
|
try {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
while (pending.all || pending.ids.size > 0) {
|
|
267
|
+
const job = pending;
|
|
268
|
+
pending = { all: false, ids: new Set() };
|
|
269
|
+
generation += 1;
|
|
270
|
+
const ids = job.all ? sequenceIds : [...job.ids];
|
|
271
|
+
const next = await loadSequences(repoRoot, ids, generation);
|
|
272
|
+
if (job.all)
|
|
273
|
+
sequences.clear();
|
|
262
274
|
for (const [id, loaded] of next)
|
|
263
275
|
sequences.set(id, loaded);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
sequences.set(sequenceId, await loadOne(repoRoot, sequenceId, generation));
|
|
276
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
277
|
+
pushOverlays(socket, sequences);
|
|
278
|
+
console.log(`reloaded sequences ${ids.join(', ')}`);
|
|
268
279
|
}
|
|
269
280
|
}
|
|
270
|
-
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
271
|
-
pushOverlays(socket, sequences);
|
|
272
|
-
console.log(`reloaded sequences ${ids.join(', ')}`);
|
|
273
|
-
}
|
|
274
281
|
}
|
|
275
282
|
catch (error) {
|
|
276
283
|
console.error('reload failed:', error instanceof Error ? error.message : error);
|
|
277
284
|
}
|
|
285
|
+
finally {
|
|
286
|
+
flushing = false;
|
|
287
|
+
if (pending.all || pending.ids.size > 0)
|
|
288
|
+
flush();
|
|
289
|
+
}
|
|
278
290
|
})();
|
|
279
291
|
};
|
|
280
292
|
chokidar
|
|
@@ -12,6 +12,10 @@ export type SourceComputeHostOptions = {
|
|
|
12
12
|
data?: ActionDataSlice;
|
|
13
13
|
/** When set with data.config.backendUrl, uses Http RpcApiHost. */
|
|
14
14
|
runToken?: string;
|
|
15
|
+
/** Runtime version forwarded on every `ctx.api` RPC. Required when RpcApiHost is used. */
|
|
16
|
+
runtimeVersion?: string;
|
|
17
|
+
/** Runtime dependency source forwarded with runtimeVersion. Required when RpcApiHost is used. */
|
|
18
|
+
runtimeSource?: 'local-file' | 'npm';
|
|
15
19
|
/** Forwarded on every `ctx.api` RPC as `x-request-id`. */
|
|
16
20
|
getRequestId?: () => string | undefined;
|
|
17
21
|
/** Forwarded to script log RPC calls. */
|
|
@@ -62,9 +62,14 @@ function resolveApiHost(options) {
|
|
|
62
62
|
if (options.apiHost)
|
|
63
63
|
return options.apiHost;
|
|
64
64
|
if (options.runToken && options.data?.config.backendUrl) {
|
|
65
|
+
if (!options.runtimeVersion || !options.runtimeSource) {
|
|
66
|
+
throw new Error('runtimeVersion and runtimeSource are required when using RpcApiHost');
|
|
67
|
+
}
|
|
65
68
|
return createRpcApiHost({
|
|
66
69
|
baseUrl: options.data.config.backendUrl,
|
|
67
70
|
getToken: () => options.runToken,
|
|
71
|
+
runtimeVersion: options.runtimeVersion,
|
|
72
|
+
runtimeSource: options.runtimeSource,
|
|
68
73
|
getRequestId: options.getRequestId,
|
|
69
74
|
getTags: options.getTags,
|
|
70
75
|
});
|
|
@@ -3,7 +3,7 @@ export { buildActionContext, type BuildActionContextOptions } from './build-cont
|
|
|
3
3
|
export { resolveSystemFnParams } from './system-args.js';
|
|
4
4
|
export { createFakeApiHost, type FakeApiHostOptions } from './fake/index.js';
|
|
5
5
|
export { defaultPlatformApis, emptyIntegration, type PlatformApiOverrides } from './fake/empty-api.js';
|
|
6
|
-
export { createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, } from './rpc/index.js';
|
|
6
|
+
export { createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, } from './rpc/index.js';
|
|
7
7
|
export { bindLogMeta, noopLog } from './log.js';
|
|
8
8
|
export { makeCtx, makeCtxAsync } from './make-ctx.js';
|
|
9
9
|
export { defaultHttpApi } from '../core/context/http.js';
|
package/dist/fn-context/index.js
CHANGED
|
@@ -2,7 +2,7 @@ export { buildActionContext } from './build-context.js';
|
|
|
2
2
|
export { resolveSystemFnParams } from './system-args.js';
|
|
3
3
|
export { createFakeApiHost } from './fake/index.js';
|
|
4
4
|
export { defaultPlatformApis, emptyIntegration } from './fake/empty-api.js';
|
|
5
|
-
export { createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, } from './rpc/index.js';
|
|
5
|
+
export { createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, } from './rpc/index.js';
|
|
6
6
|
export { bindLogMeta, noopLog } from './log.js';
|
|
7
7
|
export { makeCtx, makeCtxAsync } from './make-ctx.js';
|
|
8
8
|
export { defaultHttpApi } from '../core/context/http.js';
|
|
@@ -6,5 +6,5 @@ export { createListBindingsProcedure } from './integration/list-bindings.js';
|
|
|
6
6
|
export { createPlatformRouter, type PlatformInvokeHandler } from './platform.js';
|
|
7
7
|
export { createLogRouter, type LogEmitHandler } from './log.js';
|
|
8
8
|
export { createRpcApiHost } from './trpc-client.js';
|
|
9
|
-
export { LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, } from './request-id.js';
|
|
9
|
+
export { LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, } from './request-id.js';
|
|
10
10
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -5,5 +5,5 @@ export { createListBindingsProcedure } from './integration/list-bindings.js';
|
|
|
5
5
|
export { createPlatformRouter } from './platform.js';
|
|
6
6
|
export { createLogRouter } from './log.js';
|
|
7
7
|
export { createRpcApiHost } from './trpc-client.js';
|
|
8
|
-
export { LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, } from './request-id.js';
|
|
8
|
+
export { LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, } from './request-id.js';
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
export declare const REQUEST_ID_HEADER = "x-request-id";
|
|
2
2
|
export declare const LOG_TAGS_HEADER = "x-superpanel-log-tags";
|
|
3
|
+
export declare const RUNTIME_VERSION_HEADER = "x-superpanel-runtime-version";
|
|
4
|
+
export declare function formatRuntimeVersion(version: string, source: 'local-file' | 'npm'): string;
|
|
3
5
|
export declare function readRequestIdHeader(headers: {
|
|
4
6
|
get(name: string): string | null;
|
|
5
7
|
} | Record<string, string | string[] | undefined>): string | undefined;
|
|
8
|
+
export declare function readRuntimeVersionHeader(headers: {
|
|
9
|
+
get(name: string): string | null;
|
|
10
|
+
} | Record<string, string | string[] | undefined>): string | undefined;
|
|
6
11
|
export declare function encodeLogTagsHeader(tags: string[]): string;
|
|
7
12
|
export declare function readLogTagsHeader(headers: {
|
|
8
13
|
get(name: string): string | null;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export const REQUEST_ID_HEADER = 'x-request-id';
|
|
2
2
|
export const LOG_TAGS_HEADER = 'x-superpanel-log-tags';
|
|
3
|
+
export const RUNTIME_VERSION_HEADER = 'x-superpanel-runtime-version';
|
|
4
|
+
export function formatRuntimeVersion(version, source) {
|
|
5
|
+
return `${version}-${source}`;
|
|
6
|
+
}
|
|
3
7
|
function firstString(value) {
|
|
4
8
|
const raw = Array.isArray(value) ? value[0] : value;
|
|
5
9
|
return typeof raw === 'string' && raw.length > 0 ? raw : undefined;
|
|
@@ -11,6 +15,13 @@ export function readRequestIdHeader(headers) {
|
|
|
11
15
|
const record = headers;
|
|
12
16
|
return firstString(record[REQUEST_ID_HEADER]);
|
|
13
17
|
}
|
|
18
|
+
export function readRuntimeVersionHeader(headers) {
|
|
19
|
+
if (typeof headers.get === 'function') {
|
|
20
|
+
return firstString(headers.get(RUNTIME_VERSION_HEADER));
|
|
21
|
+
}
|
|
22
|
+
const record = headers;
|
|
23
|
+
return firstString(record[RUNTIME_VERSION_HEADER]);
|
|
24
|
+
}
|
|
14
25
|
export function encodeLogTagsHeader(tags) {
|
|
15
26
|
return JSON.stringify(tags);
|
|
16
27
|
}
|
|
@@ -2,6 +2,8 @@ import type { ApiHost } from '../types.js';
|
|
|
2
2
|
export declare function createRpcApiHost(options: {
|
|
3
3
|
baseUrl: string;
|
|
4
4
|
getToken: () => string | Promise<string>;
|
|
5
|
+
runtimeVersion: string;
|
|
6
|
+
runtimeSource: 'local-file' | 'npm';
|
|
5
7
|
getRequestId?: () => string | undefined;
|
|
6
8
|
getTags?: () => string[] | undefined;
|
|
7
9
|
waitUntil?: (promise: Promise<unknown>) => void;
|
|
@@ -3,7 +3,7 @@ import { platformSurfaces } from '../../core/context/platform-registry.js';
|
|
|
3
3
|
import { defaultHttpApi } from '../../core/context/http.js';
|
|
4
4
|
import { integrationSurfaceKeys, } from '../../core/context/types.js';
|
|
5
5
|
import { bindEnvService } from '../../core/context/env.js';
|
|
6
|
-
import { REQUEST_ID_HEADER } from './request-id.js';
|
|
6
|
+
import { REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, formatRuntimeVersion, } from './request-id.js';
|
|
7
7
|
function rpcLogger(c, getTags, waitUntil) {
|
|
8
8
|
const emit = (level, message, extra) => {
|
|
9
9
|
const pending = c.log.emit.mutate({ level, message, extra, tags: getTags?.() }).catch(() => { });
|
|
@@ -86,6 +86,7 @@ export function createRpcApiHost(options) {
|
|
|
86
86
|
headers: async () => {
|
|
87
87
|
const headers = {
|
|
88
88
|
authorization: `Bearer ${await options.getToken()}`,
|
|
89
|
+
[RUNTIME_VERSION_HEADER]: formatRuntimeVersion(options.runtimeVersion, options.runtimeSource),
|
|
89
90
|
};
|
|
90
91
|
const requestId = options.getRequestId?.();
|
|
91
92
|
if (requestId)
|
package/dist/index.d.ts
CHANGED
|
@@ -19,18 +19,18 @@ export { twilioCreateCallInput, twilioCreateCallOutput, twilioIncomingNumber, tw
|
|
|
19
19
|
export type { ApplyWritesOptions, ApplyWritesResult, CompiledSlotSchema, ExtractedField, FieldHistoryEntry, FieldValue, HistoryAppend, MultiRowProjection, MultiRowRecord, MultiRowTableData, RecomputeResult, RuntimeDerivedFieldDef, RuntimeFieldDef, RuntimeNormalFieldDef, RuntimeSlotsSnapshot, RuntimeTableDef, SingleRowProjection, SingleRowTableData, SlotCurrent, SlotFillingOutput, SlotFillingValidateResult, TableData, UpdatedBy, ValidationIssue, ValidationResult, WriteSource, } from './core/slot/types.js';
|
|
20
20
|
export type { FuncRef } from './core/functions/func-ref.js';
|
|
21
21
|
export { resolveFuncName } from './core/functions/func-ref.js';
|
|
22
|
-
export type { SequenceBuild } from './builder/types.js';
|
|
22
|
+
export type { RuntimePackageSource, SequenceBuild } from './builder/types.js';
|
|
23
23
|
export type { CaseContextViewRegistryEntry, ComputeFn, CustomFn, DefinedFunc, FuncMap, FuncOptions, FuncRegistration, FunctionCall, FunctionCtx, FunctionRegistry, FunctionRegistryEntry, GetFirstMessageFunction, GetFirstMessageParams, SystemFuncMap, WebHookFunction, WebHookFunctionParams, } from './core/functions/types.js';
|
|
24
24
|
export { toFuncRegistrations } from './core/functions/types.js';
|
|
25
25
|
export { toCaseContextViewRegistry, toFunctionRegistry } from './builder/compile/registry.js';
|
|
26
26
|
export { compileFunctionCode } from './builder/compile/function-code.js';
|
|
27
|
-
export { compileAll, compileSequence, runtimePackageVersion, snapshotCompanySchema, type CompileSequenceOptions, type CompileSequenceResult, } from './builder/compile/compile-sequence.js';
|
|
27
|
+
export { compileAll, compileSequence, runtimePackageSource, runtimePackageVersion, snapshotCompanySchema, type CompileSequenceOptions, type CompileSequenceResult, } from './builder/compile/compile-sequence.js';
|
|
28
28
|
export { compileSequenceAgendas, fatalAgendaIssues } from './builder/compile/compile-agenda.js';
|
|
29
29
|
export { loadSequenceCaseContextView, loadSequenceFunctions } from './builder/load.js';
|
|
30
30
|
export { findTsModule } from './builder/paths.js';
|
|
31
31
|
export type { OverlayClientMessage, OverlayExecuteMessage, OverlayExecuteResultMessage, OverlayAuthErrorMessage, OverlayAuthMessage, OverlayAuthOkMessage, OverlayMessage, OverlayPushMessage, OverlayReplacedMessage, OverlayServerMessage, } from './dev-overlay/protocol.js';
|
|
32
32
|
export { encodeOverlayMessage, OVERLAY_REPLACED_CLOSE_CODE, parseOverlayMessage } from './dev-overlay/protocol.js';
|
|
33
|
-
export { buildActionContext, bindLogMeta, createFakeApiHost, defaultPlatformApis, emptyIntegration, createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, makeCtx, makeCtxAsync, noopLog, type ActionContext, type ActionDataSlice, type ApiHost, type FakeApiHostOptions, type RunScope, } from './fn-context/index.js';
|
|
33
|
+
export { buildActionContext, bindLogMeta, createFakeApiHost, defaultPlatformApis, emptyIntegration, createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, makeCtx, makeCtxAsync, noopLog, type ActionContext, type ActionDataSlice, type ApiHost, type FakeApiHostOptions, type RunScope, } from './fn-context/index.js';
|
|
34
34
|
export { createAppRouter, createIntegrationRouter, createListBindingsProcedure, createLogRouter, createPlatformRouter, isRpcCodedError, parseScriptInput, ScriptInputError, type AppRouter, type RpcCodedError, type RpcErrorCode, type IntegrationInvokeHandler, type LogEmitHandler, type PlatformInvokeHandler, type RpcContext, } from './fn-context/rpc/index.js';
|
|
35
35
|
export * from './compute-host/index.js';
|
|
36
36
|
export { defineBoolComputeFunc, defineComputeFunc } from './core/functions/define/compute.js';
|
package/dist/index.js
CHANGED
|
@@ -14,12 +14,12 @@ export { resolveFuncName } from './core/functions/func-ref.js';
|
|
|
14
14
|
export { toFuncRegistrations } from './core/functions/types.js';
|
|
15
15
|
export { toCaseContextViewRegistry, toFunctionRegistry } from './builder/compile/registry.js';
|
|
16
16
|
export { compileFunctionCode } from './builder/compile/function-code.js';
|
|
17
|
-
export { compileAll, compileSequence, runtimePackageVersion, snapshotCompanySchema, } from './builder/compile/compile-sequence.js';
|
|
17
|
+
export { compileAll, compileSequence, runtimePackageSource, runtimePackageVersion, snapshotCompanySchema, } from './builder/compile/compile-sequence.js';
|
|
18
18
|
export { compileSequenceAgendas, fatalAgendaIssues } from './builder/compile/compile-agenda.js';
|
|
19
19
|
export { loadSequenceCaseContextView, loadSequenceFunctions } from './builder/load.js';
|
|
20
20
|
export { findTsModule } from './builder/paths.js';
|
|
21
21
|
export { encodeOverlayMessage, OVERLAY_REPLACED_CLOSE_CODE, parseOverlayMessage } from './dev-overlay/protocol.js';
|
|
22
|
-
export { buildActionContext, bindLogMeta, createFakeApiHost, defaultPlatformApis, emptyIntegration, createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, encodeLogTagsHeader, readLogTagsHeader, readRequestIdHeader, makeCtx, makeCtxAsync, noopLog, } from './fn-context/index.js';
|
|
22
|
+
export { buildActionContext, bindLogMeta, createFakeApiHost, defaultPlatformApis, emptyIntegration, createRpcApiHost, LOG_TAGS_HEADER, REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, encodeLogTagsHeader, formatRuntimeVersion, readLogTagsHeader, readRequestIdHeader, readRuntimeVersionHeader, makeCtx, makeCtxAsync, noopLog, } from './fn-context/index.js';
|
|
23
23
|
export { createAppRouter, createIntegrationRouter, createListBindingsProcedure, createLogRouter, createPlatformRouter, isRpcCodedError, parseScriptInput, ScriptInputError, } from './fn-context/rpc/index.js';
|
|
24
24
|
export * from './compute-host/index.js';
|
|
25
25
|
export { defineBoolComputeFunc, defineComputeFunc } from './core/functions/define/compute.js';
|