@superpanel-sdk/runtime 0.0.1-internal.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/bin/superpanel-sdk-runtime.mjs +22 -0
- package/dist/builder/codegen/emit.d.ts +17 -0
- package/dist/builder/codegen/emit.js +143 -0
- package/dist/builder/codegen/generate.d.ts +21 -0
- package/dist/builder/codegen/generate.js +90 -0
- package/dist/builder/compile/compile-agenda.d.ts +15 -0
- package/dist/builder/compile/compile-agenda.js +59 -0
- package/dist/builder/compile/compile-sequence.d.ts +24 -0
- package/dist/builder/compile/compile-sequence.js +121 -0
- package/dist/builder/compile/function-code.d.ts +6 -0
- package/dist/builder/compile/function-code.js +30 -0
- package/dist/builder/compile/registry.d.ts +13 -0
- package/dist/builder/compile/registry.js +42 -0
- package/dist/builder/load.d.ts +22 -0
- package/dist/builder/load.js +169 -0
- package/dist/builder/merge.d.ts +4 -0
- package/dist/builder/merge.js +19 -0
- package/dist/builder/paths.d.ts +10 -0
- package/dist/builder/paths.js +22 -0
- package/dist/builder/types.d.ts +31 -0
- package/dist/builder/types.js +2 -0
- package/dist/cli/compile/index.d.ts +4 -0
- package/dist/cli/compile/index.js +11 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +64 -0
- package/dist/cli/serve/index.d.ts +9 -0
- package/dist/cli/serve/index.js +340 -0
- package/dist/cli/type-gen/index.d.ts +5 -0
- package/dist/cli/type-gen/index.js +90 -0
- package/dist/compute-host/compute-host.d.ts +38 -0
- package/dist/compute-host/compute-host.js +45 -0
- package/dist/compute-host/index.d.ts +5 -0
- package/dist/compute-host/index.js +3 -0
- package/dist/compute-host/source-compute-host.d.ts +29 -0
- package/dist/compute-host/source-compute-host.js +72 -0
- package/dist/core/context/account-fields.d.ts +12 -0
- package/dist/core/context/account-fields.js +2 -0
- package/dist/core/context/contact.d.ts +60 -0
- package/dist/core/context/contact.js +34 -0
- package/dist/core/context/db.d.ts +69 -0
- package/dist/core/context/db.js +44 -0
- package/dist/core/context/docusign.d.ts +134 -0
- package/dist/core/context/docusign.js +108 -0
- package/dist/core/context/env.d.ts +9 -0
- package/dist/core/context/env.js +19 -0
- package/dist/core/context/filevine.d.ts +323 -0
- package/dist/core/context/filevine.js +254 -0
- package/dist/core/context/http.d.ts +9 -0
- package/dist/core/context/http.js +4 -0
- package/dist/core/context/hubspot.d.ts +250 -0
- package/dist/core/context/hubspot.js +191 -0
- package/dist/core/context/platform-registry.d.ts +67 -0
- package/dist/core/context/platform-registry.js +11 -0
- package/dist/core/context/platform.d.ts +33 -0
- package/dist/core/context/platform.js +2 -0
- package/dist/core/context/twilio.d.ts +50 -0
- package/dist/core/context/twilio.js +42 -0
- package/dist/core/context/types.d.ts +87 -0
- package/dist/core/context/types.js +9 -0
- package/dist/core/functions/define/case-context.d.ts +14 -0
- package/dist/core/functions/define/case-context.js +15 -0
- package/dist/core/functions/define/compute.d.ts +7 -0
- package/dist/core/functions/define/compute.js +17 -0
- package/dist/core/functions/define/custom.d.ts +4 -0
- package/dist/core/functions/define/custom.js +8 -0
- package/dist/core/functions/define/system.d.ts +7 -0
- package/dist/core/functions/define/system.js +12 -0
- package/dist/core/functions/func-ref.d.ts +16 -0
- package/dist/core/functions/func-ref.js +20 -0
- package/dist/core/functions/types.d.ts +78 -0
- package/dist/core/functions/types.js +5 -0
- package/dist/core/slot/compile-schema.d.ts +24 -0
- package/dist/core/slot/compile-schema.js +336 -0
- package/dist/core/slot/deps.d.ts +15 -0
- package/dist/core/slot/deps.js +88 -0
- package/dist/core/slot/derived.d.ts +32 -0
- package/dist/core/slot/derived.js +115 -0
- package/dist/core/slot/field.d.ts +36 -0
- package/dist/core/slot/field.js +97 -0
- package/dist/core/slot/id-ref.d.ts +4 -0
- package/dist/core/slot/id-ref.js +15 -0
- package/dist/core/slot/index.d.ts +15 -0
- package/dist/core/slot/index.js +14 -0
- package/dist/core/slot/path.d.ts +18 -0
- package/dist/core/slot/path.js +22 -0
- package/dist/core/slot/row.d.ts +22 -0
- package/dist/core/slot/row.js +54 -0
- package/dist/core/slot/schema.d.ts +102 -0
- package/dist/core/slot/schema.js +17 -0
- package/dist/core/slot/sf-schema.d.ts +23 -0
- package/dist/core/slot/sf-schema.js +125 -0
- package/dist/core/slot/sf-validate.d.ts +14 -0
- package/dist/core/slot/sf-validate.js +132 -0
- package/dist/core/slot/snapshot.d.ts +18 -0
- package/dist/core/slot/snapshot.js +138 -0
- package/dist/core/slot/types.d.ts +144 -0
- package/dist/core/slot/types.js +2 -0
- package/dist/core/slot/value.d.ts +9 -0
- package/dist/core/slot/value.js +97 -0
- package/dist/core/slot/write.d.ts +41 -0
- package/dist/core/slot/write.js +319 -0
- package/dist/core/stage/builders.d.ts +36 -0
- package/dist/core/stage/builders.js +28 -0
- package/dist/core/stage/condition.d.ts +17 -0
- package/dist/core/stage/condition.js +198 -0
- package/dist/core/stage/drive.d.ts +43 -0
- package/dist/core/stage/drive.js +77 -0
- package/dist/core/stage/finish-when.d.ts +20 -0
- package/dist/core/stage/finish-when.js +82 -0
- package/dist/core/stage/flatten.d.ts +27 -0
- package/dist/core/stage/flatten.js +464 -0
- package/dist/core/stage/index.d.ts +11 -0
- package/dist/core/stage/index.js +10 -0
- package/dist/core/stage/simulate.d.ts +24 -0
- package/dist/core/stage/simulate.js +33 -0
- package/dist/core/stage/transfer-fields.d.ts +14 -0
- package/dist/core/stage/transfer-fields.js +88 -0
- package/dist/core/stage/types.d.ts +177 -0
- package/dist/core/stage/types.js +2 -0
- package/dist/core/stage/validate.d.ts +11 -0
- package/dist/core/stage/validate.js +226 -0
- package/dist/core/stage/walk.d.ts +34 -0
- package/dist/core/stage/walk.js +96 -0
- package/dist/dev-overlay/protocol.d.ts +53 -0
- package/dist/dev-overlay/protocol.js +21 -0
- package/dist/fn-context/build-context.d.ts +11 -0
- package/dist/fn-context/build-context.js +21 -0
- package/dist/fn-context/fake/empty-api.d.ts +9 -0
- package/dist/fn-context/fake/empty-api.js +50 -0
- package/dist/fn-context/fake/index.d.ts +12 -0
- package/dist/fn-context/fake/index.js +22 -0
- package/dist/fn-context/index.d.ts +9 -0
- package/dist/fn-context/index.js +8 -0
- package/dist/fn-context/log.d.ts +8 -0
- package/dist/fn-context/log.js +29 -0
- package/dist/fn-context/make-ctx.d.ts +9 -0
- package/dist/fn-context/make-ctx.js +51 -0
- package/dist/fn-context/rpc/errors.d.ts +25 -0
- package/dist/fn-context/rpc/errors.js +23 -0
- package/dist/fn-context/rpc/index.d.ts +10 -0
- package/dist/fn-context/rpc/index.js +9 -0
- package/dist/fn-context/rpc/integration/index.d.ts +51 -0
- package/dist/fn-context/rpc/integration/index.js +19 -0
- package/dist/fn-context/rpc/integration/list-bindings.d.ts +21 -0
- package/dist/fn-context/rpc/integration/list-bindings.js +18 -0
- package/dist/fn-context/rpc/log.d.ts +36 -0
- package/dist/fn-context/rpc/log.js +16 -0
- package/dist/fn-context/rpc/platform.d.ts +28 -0
- package/dist/fn-context/rpc/platform.js +15 -0
- package/dist/fn-context/rpc/request-id.d.ts +5 -0
- package/dist/fn-context/rpc/request-id.js +13 -0
- package/dist/fn-context/rpc/trpc-client.d.ts +7 -0
- package/dist/fn-context/rpc/trpc-client.js +109 -0
- package/dist/fn-context/rpc/trpc-init.d.ts +17 -0
- package/dist/fn-context/rpc/trpc-init.js +17 -0
- package/dist/fn-context/rpc/trpc-router-factory.d.ts +84 -0
- package/dist/fn-context/rpc/trpc-router-factory.js +9 -0
- package/dist/fn-context/types.d.ts +17 -0
- package/dist/fn-context/types.js +2 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +34 -0
- package/dist/voice-service/actions.d.ts +20 -0
- package/dist/voice-service/actions.js +2 -0
- package/dist/voice-service/index.d.ts +3 -0
- package/dist/voice-service/index.js +3 -0
- package/dist/voice-service/types.d.ts +1010 -0
- package/dist/voice-service/types.js +16 -0
- package/package.json +55 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { compileAll } from '../../builder/compile/compile-sequence.js';
|
|
4
|
+
import { findTsModule } from '../../builder/paths.js';
|
|
5
|
+
import { listSequenceIds, loadSequenceCaseContextView, loadSequenceFunctions } from '../../builder/load.js';
|
|
6
|
+
import { createSourceComputeHost } from '../../compute-host/source-compute-host.js';
|
|
7
|
+
import { encodeOverlayMessage, OVERLAY_REPLACED_CLOSE_CODE, parseOverlayMessage, } from '../../dev-overlay/protocol.js';
|
|
8
|
+
const DEFAULT_BACKEND_URL = 'http://127.0.0.1:8100';
|
|
9
|
+
function env(name) {
|
|
10
|
+
const value = process.env[name];
|
|
11
|
+
return value && value.length > 0 ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
export function overlayWebSocketUrl(raw) {
|
|
14
|
+
const url = new URL(raw);
|
|
15
|
+
if (url.protocol === 'http:')
|
|
16
|
+
url.protocol = 'ws:';
|
|
17
|
+
else if (url.protocol === 'https:')
|
|
18
|
+
url.protocol = 'wss:';
|
|
19
|
+
else if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
20
|
+
throw new Error(`unsupported backend url: ${raw}`);
|
|
21
|
+
}
|
|
22
|
+
const path = url.pathname.replace(/\/$/, '');
|
|
23
|
+
if (path === '')
|
|
24
|
+
url.pathname = '/overlay';
|
|
25
|
+
url.search = '';
|
|
26
|
+
url.hash = '';
|
|
27
|
+
return url.toString();
|
|
28
|
+
}
|
|
29
|
+
/** `sequences/<id>/…` → sequence id */
|
|
30
|
+
function sequenceIdFromPath(repoRoot, file) {
|
|
31
|
+
const rel = path.relative(repoRoot, file);
|
|
32
|
+
const match = /^sequences[/\\]([^/\\]+)(?:[/\\]|$)/.exec(rel);
|
|
33
|
+
return match?.[1] ?? null;
|
|
34
|
+
}
|
|
35
|
+
function isSchemaPath(repoRoot, file) {
|
|
36
|
+
const rel = path.relative(repoRoot, file);
|
|
37
|
+
return rel === 'schema.ts' || rel.startsWith(`schema${path.sep}`) || rel.startsWith('schema/');
|
|
38
|
+
}
|
|
39
|
+
async function loadOne(repoRoot, sequenceId, generation) {
|
|
40
|
+
const [compiled] = await compileAll(repoRoot, {
|
|
41
|
+
sequenceIds: [sequenceId],
|
|
42
|
+
dryRun: true,
|
|
43
|
+
includeCode: false,
|
|
44
|
+
});
|
|
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
|
+
}
|
|
58
|
+
async function loadSequences(repoRoot, sequenceIds, generation) {
|
|
59
|
+
const out = new Map();
|
|
60
|
+
for (const sequenceId of sequenceIds) {
|
|
61
|
+
out.set(sequenceId, await loadOne(repoRoot, sequenceId, generation));
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function send(ws, message) {
|
|
66
|
+
ws.send(encodeOverlayMessage(message));
|
|
67
|
+
}
|
|
68
|
+
function logField(label, value) {
|
|
69
|
+
console.log(` ${label.padEnd(10)} ${value}`);
|
|
70
|
+
}
|
|
71
|
+
async function handleExecute(loaded, message) {
|
|
72
|
+
const host = createSourceComputeHost({
|
|
73
|
+
functions: loaded.functions,
|
|
74
|
+
caseContextView: loaded.view,
|
|
75
|
+
data: message.data,
|
|
76
|
+
runToken: message.runToken,
|
|
77
|
+
getRequestId: () => message.logRequestId,
|
|
78
|
+
});
|
|
79
|
+
try {
|
|
80
|
+
return await host.execute(message.name, message.args);
|
|
81
|
+
}
|
|
82
|
+
finally {
|
|
83
|
+
host.dispose();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function pushOverlays(ws, sequences) {
|
|
87
|
+
for (const [sequenceId, loaded] of sequences) {
|
|
88
|
+
send(ws, {
|
|
89
|
+
type: 'overlay',
|
|
90
|
+
sequenceId,
|
|
91
|
+
generation: loaded.generation,
|
|
92
|
+
build: loaded.build,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function waitOpen(ws) {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
99
|
+
resolve();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
ws.addEventListener('open', () => resolve(), { once: true });
|
|
103
|
+
ws.addEventListener('error', () => reject(new Error('websocket error')), { once: true });
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
class OverlayReplacedError extends Error {
|
|
107
|
+
constructor() {
|
|
108
|
+
super('replaced by another serve');
|
|
109
|
+
this.name = 'OverlayReplacedError';
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function waitAuthOk(ws) {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
const onMessage = (event) => {
|
|
115
|
+
try {
|
|
116
|
+
const message = parseOverlayMessage(String(event.data));
|
|
117
|
+
if (message.type === 'auth.ok') {
|
|
118
|
+
cleanup();
|
|
119
|
+
resolve();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (message.type === 'replaced') {
|
|
123
|
+
cleanup();
|
|
124
|
+
reject(new OverlayReplacedError());
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (message.type === 'auth.error') {
|
|
128
|
+
cleanup();
|
|
129
|
+
reject(new Error(message.error));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
cleanup();
|
|
134
|
+
reject(error);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const onClose = (event) => {
|
|
138
|
+
cleanup();
|
|
139
|
+
reject(event.code === OVERLAY_REPLACED_CLOSE_CODE
|
|
140
|
+
? new OverlayReplacedError()
|
|
141
|
+
: new Error('websocket closed before auth.ok'));
|
|
142
|
+
};
|
|
143
|
+
const cleanup = () => {
|
|
144
|
+
ws.removeEventListener('message', onMessage);
|
|
145
|
+
ws.removeEventListener('close', onClose);
|
|
146
|
+
};
|
|
147
|
+
ws.addEventListener('message', onMessage);
|
|
148
|
+
ws.addEventListener('close', onClose);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function attachExecuteHandler(ws, sequences) {
|
|
152
|
+
ws.addEventListener('message', (event) => {
|
|
153
|
+
void (async () => {
|
|
154
|
+
let message;
|
|
155
|
+
try {
|
|
156
|
+
message = parseOverlayMessage(String(event.data));
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error('bad server message:', error instanceof Error ? error.message : error);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (message.type !== 'execute')
|
|
163
|
+
return;
|
|
164
|
+
const call = `${message.sequenceId} ${message.name}`;
|
|
165
|
+
const loaded = sequences.get(message.sequenceId);
|
|
166
|
+
if (!loaded) {
|
|
167
|
+
const error = `unknown sequence ${message.sequenceId}`;
|
|
168
|
+
console.error(`execute ${call} failed: ${error}`);
|
|
169
|
+
send(ws, {
|
|
170
|
+
type: 'execute.result',
|
|
171
|
+
requestId: message.requestId,
|
|
172
|
+
ok: false,
|
|
173
|
+
error,
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
const started = Date.now();
|
|
178
|
+
console.log(`execute ${call}`);
|
|
179
|
+
try {
|
|
180
|
+
const result = await handleExecute(loaded, message);
|
|
181
|
+
console.log(`execute ${call} ok ${Date.now() - started}ms`);
|
|
182
|
+
send(ws, { type: 'execute.result', requestId: message.requestId, ok: true, result });
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
const text = error instanceof Error ? error.message : String(error);
|
|
186
|
+
console.error(`execute ${call} failed: ${text}`);
|
|
187
|
+
send(ws, {
|
|
188
|
+
type: 'execute.result',
|
|
189
|
+
requestId: message.requestId,
|
|
190
|
+
ok: false,
|
|
191
|
+
error: text,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
})();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function waitSessionEnd(ws) {
|
|
198
|
+
return new Promise((resolve) => {
|
|
199
|
+
if (ws.readyState === WebSocket.CLOSED) {
|
|
200
|
+
resolve('closed');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const onMessage = (event) => {
|
|
204
|
+
try {
|
|
205
|
+
const message = parseOverlayMessage(String(event.data));
|
|
206
|
+
if (message.type === 'replaced') {
|
|
207
|
+
cleanup();
|
|
208
|
+
resolve('replaced');
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
const onClose = (event) => {
|
|
216
|
+
cleanup();
|
|
217
|
+
resolve(event.code === OVERLAY_REPLACED_CLOSE_CODE ? 'replaced' : 'closed');
|
|
218
|
+
};
|
|
219
|
+
const cleanup = () => {
|
|
220
|
+
ws.removeEventListener('message', onMessage);
|
|
221
|
+
ws.removeEventListener('close', onClose);
|
|
222
|
+
};
|
|
223
|
+
ws.addEventListener('message', onMessage);
|
|
224
|
+
ws.addEventListener('close', onClose);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
export async function runServe(repoRoot, options = {}) {
|
|
228
|
+
const companyId = options.companyId ?? env('SUPERPANEL_SDK_COMPANY_ID');
|
|
229
|
+
const apiKey = options.apiKey ?? env('SUPERPANEL_SDK_API_KEY');
|
|
230
|
+
if (!companyId)
|
|
231
|
+
throw new Error('SUPERPANEL_SDK_COMPANY_ID or --company is required');
|
|
232
|
+
if (!apiKey)
|
|
233
|
+
throw new Error('SUPERPANEL_SDK_API_KEY or --api-key is required');
|
|
234
|
+
const backendUrl = options.url ?? env('SUPERPANEL_SDK_BACKEND_URL') ?? DEFAULT_BACKEND_URL;
|
|
235
|
+
const url = overlayWebSocketUrl(backendUrl);
|
|
236
|
+
const sequenceIds = options.sequenceIds ?? listSequenceIds(repoRoot);
|
|
237
|
+
if (sequenceIds.length === 0) {
|
|
238
|
+
throw new Error(`No sequences with slots found under ${path.join(repoRoot, 'sequences')}`);
|
|
239
|
+
}
|
|
240
|
+
let generation = 1;
|
|
241
|
+
const sequences = await loadSequences(repoRoot, sequenceIds, generation);
|
|
242
|
+
let socket = null;
|
|
243
|
+
const names = [...sequences.keys()];
|
|
244
|
+
console.log('Local overlay');
|
|
245
|
+
logField('company', companyId);
|
|
246
|
+
logField('sequences', names.join(', '));
|
|
247
|
+
logField('backend', backendUrl);
|
|
248
|
+
logField('websocket', url);
|
|
249
|
+
let timer;
|
|
250
|
+
let pending = { all: false, ids: new Set() };
|
|
251
|
+
const flush = () => {
|
|
252
|
+
const job = pending;
|
|
253
|
+
pending = { all: false, ids: new Set() };
|
|
254
|
+
void (async () => {
|
|
255
|
+
try {
|
|
256
|
+
generation += 1;
|
|
257
|
+
const ids = job.all ? sequenceIds : [...job.ids];
|
|
258
|
+
if (job.all) {
|
|
259
|
+
const next = await loadSequences(repoRoot, sequenceIds, generation);
|
|
260
|
+
sequences.clear();
|
|
261
|
+
for (const [id, loaded] of next)
|
|
262
|
+
sequences.set(id, loaded);
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
for (const sequenceId of ids) {
|
|
266
|
+
sequences.set(sequenceId, await loadOne(repoRoot, sequenceId, generation));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
270
|
+
pushOverlays(socket, sequences);
|
|
271
|
+
console.log(`reloaded sequences ${ids.join(', ')}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
catch (error) {
|
|
275
|
+
console.error('reload failed:', error instanceof Error ? error.message : error);
|
|
276
|
+
}
|
|
277
|
+
})();
|
|
278
|
+
};
|
|
279
|
+
chokidar
|
|
280
|
+
.watch([path.join(repoRoot, 'schema'), path.join(repoRoot, 'schema.ts'), path.join(repoRoot, 'sequences')], {
|
|
281
|
+
ignoreInitial: true,
|
|
282
|
+
ignored: (p) => p.includes(`${path.sep}__gen${path.sep}`) || p.includes(`${path.sep}build${path.sep}`),
|
|
283
|
+
})
|
|
284
|
+
.on('all', (event, file) => {
|
|
285
|
+
if (!file)
|
|
286
|
+
return;
|
|
287
|
+
if (file.includes(`${path.sep}__gen${path.sep}`))
|
|
288
|
+
return;
|
|
289
|
+
if (file.includes(`${path.sep}build${path.sep}`))
|
|
290
|
+
return;
|
|
291
|
+
const rel = path.relative(repoRoot, file);
|
|
292
|
+
const schemaHit = isSchemaPath(repoRoot, file);
|
|
293
|
+
const sequenceId = sequenceIdFromPath(repoRoot, file);
|
|
294
|
+
if (!schemaHit && !sequenceId)
|
|
295
|
+
return;
|
|
296
|
+
console.log(`changed ${rel}`);
|
|
297
|
+
if (schemaHit)
|
|
298
|
+
pending.all = true;
|
|
299
|
+
else if (sequenceId)
|
|
300
|
+
pending.ids.add(sequenceId);
|
|
301
|
+
clearTimeout(timer);
|
|
302
|
+
timer = setTimeout(flush, 100);
|
|
303
|
+
});
|
|
304
|
+
let delayMs = 500;
|
|
305
|
+
for (;;) {
|
|
306
|
+
const ws = new WebSocket(url);
|
|
307
|
+
socket = ws;
|
|
308
|
+
try {
|
|
309
|
+
await waitOpen(ws);
|
|
310
|
+
const authOk = waitAuthOk(ws);
|
|
311
|
+
send(ws, {
|
|
312
|
+
type: 'auth',
|
|
313
|
+
apiKey,
|
|
314
|
+
companyId,
|
|
315
|
+
names: [...sequences.keys()],
|
|
316
|
+
});
|
|
317
|
+
await authOk;
|
|
318
|
+
attachExecuteHandler(ws, sequences);
|
|
319
|
+
pushOverlays(ws, sequences);
|
|
320
|
+
delayMs = 500;
|
|
321
|
+
console.log(`connected overlay ${url}`);
|
|
322
|
+
const end = await waitSessionEnd(ws);
|
|
323
|
+
if (end === 'replaced')
|
|
324
|
+
throw new OverlayReplacedError();
|
|
325
|
+
console.log('disconnected retrying');
|
|
326
|
+
}
|
|
327
|
+
catch (error) {
|
|
328
|
+
if (error instanceof OverlayReplacedError)
|
|
329
|
+
throw error;
|
|
330
|
+
console.error('connection failed:', error instanceof Error ? error.message : error);
|
|
331
|
+
if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
|
|
332
|
+
ws.close();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
socket = null;
|
|
336
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
337
|
+
delayMs = Math.min(delayMs * 2, 5_000);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import chokidar from 'chokidar';
|
|
3
|
+
import { checkAll, generateAll, generateSequence, listSequenceIds } from '../../builder/codegen/generate.js';
|
|
4
|
+
async function runOnce(repoRoot) {
|
|
5
|
+
const results = await generateAll(repoRoot);
|
|
6
|
+
for (const result of results) {
|
|
7
|
+
console.log(`wrote ${path.relative(repoRoot, result.outPath)}`);
|
|
8
|
+
console.log(`wrote ${path.relative(repoRoot, result.caseContextOutPath)}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/** `sequences/<id>/…` → sequence id */
|
|
12
|
+
function sequenceIdFromPath(repoRoot, file) {
|
|
13
|
+
const rel = path.relative(repoRoot, file);
|
|
14
|
+
const match = /^sequences[/\\]([^/\\]+)(?:[/\\]|$)/.exec(rel);
|
|
15
|
+
return match?.[1] ?? null;
|
|
16
|
+
}
|
|
17
|
+
function isSchemaPath(repoRoot, file) {
|
|
18
|
+
const rel = path.relative(repoRoot, file);
|
|
19
|
+
return rel === 'schema.ts' || rel.startsWith(`schema${path.sep}`) || rel.startsWith('schema/');
|
|
20
|
+
}
|
|
21
|
+
async function runWatch(repoRoot) {
|
|
22
|
+
await runOnce(repoRoot);
|
|
23
|
+
console.log('[type:gen] watching schema/** and sequence sources');
|
|
24
|
+
console.log('[type:gen] sequences:', listSequenceIds(repoRoot).join(', ') || '(none)');
|
|
25
|
+
let timer;
|
|
26
|
+
let pending = {
|
|
27
|
+
all: false,
|
|
28
|
+
sequences: new Set(),
|
|
29
|
+
};
|
|
30
|
+
const flush = () => {
|
|
31
|
+
const job = pending;
|
|
32
|
+
pending = { all: false, sequences: new Set() };
|
|
33
|
+
void (async () => {
|
|
34
|
+
try {
|
|
35
|
+
if (job.all) {
|
|
36
|
+
await runOnce(repoRoot);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
for (const sequenceId of job.sequences) {
|
|
40
|
+
const result = await generateSequence(repoRoot, sequenceId);
|
|
41
|
+
console.log(`wrote ${path.relative(repoRoot, result.outPath)}`);
|
|
42
|
+
console.log(`wrote ${path.relative(repoRoot, result.caseContextOutPath)}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error('[type:gen] regenerate failed:', error instanceof Error ? error.message : error);
|
|
47
|
+
}
|
|
48
|
+
})();
|
|
49
|
+
};
|
|
50
|
+
chokidar
|
|
51
|
+
.watch([path.join(repoRoot, 'schema'), path.join(repoRoot, 'schema.ts'), path.join(repoRoot, 'sequences')], {
|
|
52
|
+
ignoreInitial: true,
|
|
53
|
+
ignored: (p) => p.includes(`${path.sep}__gen${path.sep}`) || p.includes(`${path.sep}build${path.sep}`),
|
|
54
|
+
})
|
|
55
|
+
.on('all', (event, file) => {
|
|
56
|
+
if (!file)
|
|
57
|
+
return;
|
|
58
|
+
if (file.includes(`${path.sep}__gen${path.sep}`))
|
|
59
|
+
return;
|
|
60
|
+
if (file.includes(`${path.sep}build${path.sep}`))
|
|
61
|
+
return;
|
|
62
|
+
const rel = path.relative(repoRoot, file);
|
|
63
|
+
const schemaHit = isSchemaPath(repoRoot, file);
|
|
64
|
+
const sequenceId = sequenceIdFromPath(repoRoot, file);
|
|
65
|
+
if (!schemaHit && !sequenceId)
|
|
66
|
+
return;
|
|
67
|
+
console.log(`[type:gen] ${event} ${rel}`);
|
|
68
|
+
if (schemaHit) {
|
|
69
|
+
pending.all = true;
|
|
70
|
+
}
|
|
71
|
+
else if (sequenceId) {
|
|
72
|
+
pending.sequences.add(sequenceId);
|
|
73
|
+
}
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
timer = setTimeout(flush, 100);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
export async function runTypeGen(repoRoot, options) {
|
|
79
|
+
if (options.checkMode) {
|
|
80
|
+
await checkAll(repoRoot);
|
|
81
|
+
console.log('Generated types are up to date.');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (options.watchMode) {
|
|
85
|
+
await runWatch(repoRoot);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
await runOnce(repoRoot);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CaseContextViewFn } from '../core/functions/define/case-context.js';
|
|
2
|
+
import type { RuntimeSlotsSnapshot } from '../core/slot/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Pluggable host for sequence functions (derived / finishWhen / custom / system).
|
|
5
|
+
*
|
|
6
|
+
* - Local unit tests / company CI: {@link createLocalComputeHost} (direct JS call)
|
|
7
|
+
* - Author FuncMap: `createSourceComputeHost`
|
|
8
|
+
* - Platform backend: QuickJS / Cloudflare Worker hosts with the same `execute` surface
|
|
9
|
+
*/
|
|
10
|
+
export interface ComputeHost {
|
|
11
|
+
/**
|
|
12
|
+
* Invoke a registered function by name, then dispatch on `kind`.
|
|
13
|
+
* Compute/boolCompute/custom: `args[0]` is the raw snapshot (projected internally).
|
|
14
|
+
* `caseContextView`: `args[0]` is the raw snapshot (identity when the sequence has no view).
|
|
15
|
+
*/
|
|
16
|
+
execute(name: string, args?: unknown[]): Promise<unknown>;
|
|
17
|
+
/** Release host-owned resources. No-op when the host has nothing to free. */
|
|
18
|
+
dispose(): void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Name → computeFn. Parameter typed as `never` so company-narrowed
|
|
22
|
+
* `ComputeFn<CaseContext>` maps remain assignable (TS contravariance).
|
|
23
|
+
*/
|
|
24
|
+
export type ComputeFnMap = Record<string, (slots: never) => unknown | Promise<unknown>>;
|
|
25
|
+
export type ComputeHostOrFns = ComputeHost | ComputeFnMap;
|
|
26
|
+
export type LocalComputeHostOptions = {
|
|
27
|
+
/**
|
|
28
|
+
* Optional `(slots) => CaseContext` projector. Registered under the view.ts
|
|
29
|
+
* export name `caseContextView` and invoked before each compute fn
|
|
30
|
+
* (no cross-call cache — snapshot may change mid-recompute).
|
|
31
|
+
*/
|
|
32
|
+
caseContextView?: CaseContextViewFn<RuntimeSlotsSnapshot, unknown>;
|
|
33
|
+
};
|
|
34
|
+
/** In-process host: call plain JS computeFns. No VM, no I/O fence. */
|
|
35
|
+
export declare function createLocalComputeHost(fns: ComputeFnMap, options?: LocalComputeHostOptions): ComputeHost;
|
|
36
|
+
/** Accept either a host or a name→fn map (map is wrapped as local host). */
|
|
37
|
+
export declare function resolveComputeHost(hostOrFns: ComputeHostOrFns, options?: LocalComputeHostOptions): ComputeHost;
|
|
38
|
+
//# sourceMappingURL=compute-host.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
function isComputeHost(value) {
|
|
2
|
+
return typeof value.execute === 'function';
|
|
3
|
+
}
|
|
4
|
+
function requireSlots(name, args) {
|
|
5
|
+
const slots = args[0];
|
|
6
|
+
if (slots == null) {
|
|
7
|
+
throw new Error(`Function ${name}: args[0] (slots) required`);
|
|
8
|
+
}
|
|
9
|
+
return slots;
|
|
10
|
+
}
|
|
11
|
+
/** In-process host: call plain JS computeFns. No VM, no I/O fence. */
|
|
12
|
+
export function createLocalComputeHost(fns, options = {}) {
|
|
13
|
+
if (Object.hasOwn(fns, 'caseContextView')) {
|
|
14
|
+
throw new Error(`computeFn name 'caseContextView' collides with the sequence view`);
|
|
15
|
+
}
|
|
16
|
+
const viewFn = options.caseContextView ?? ((slots) => slots);
|
|
17
|
+
const registry = {
|
|
18
|
+
caseContextView: { kind: 'caseContextView', main: viewFn },
|
|
19
|
+
};
|
|
20
|
+
for (const [name, fn] of Object.entries(fns)) {
|
|
21
|
+
registry[name] = { kind: 'compute', main: fn };
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
async execute(name, args = []) {
|
|
25
|
+
const def = registry[name];
|
|
26
|
+
if (!def) {
|
|
27
|
+
throw new Error(`Missing computeFn implementation: ${name}`);
|
|
28
|
+
}
|
|
29
|
+
const snapshot = requireSlots(name, args);
|
|
30
|
+
if (def.kind === 'caseContextView') {
|
|
31
|
+
return def.main(snapshot);
|
|
32
|
+
}
|
|
33
|
+
return def.main(viewFn(snapshot));
|
|
34
|
+
},
|
|
35
|
+
dispose() { },
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** Accept either a host or a name→fn map (map is wrapped as local host). */
|
|
39
|
+
export function resolveComputeHost(hostOrFns, options) {
|
|
40
|
+
if (isComputeHost(hostOrFns)) {
|
|
41
|
+
return hostOrFns;
|
|
42
|
+
}
|
|
43
|
+
return createLocalComputeHost(hostOrFns, options);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=compute-host.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createLocalComputeHost, resolveComputeHost } from './compute-host.js';
|
|
2
|
+
export type { ComputeFnMap, ComputeHost, ComputeHostOrFns, LocalComputeHostOptions } from './compute-host.js';
|
|
3
|
+
export { createSourceComputeHost } from './source-compute-host.js';
|
|
4
|
+
export type { SourceComputeHost, SourceComputeHostOptions } from './source-compute-host.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type CaseContextViewFn } from '../core/functions/define/case-context.js';
|
|
2
|
+
import type { FuncMap } from '../core/functions/types.js';
|
|
3
|
+
import type { ComputeHost } from './compute-host.js';
|
|
4
|
+
import type { RuntimeSlotsSnapshot } from '../core/slot/types.js';
|
|
5
|
+
import type { ActionDataSlice, ApiHost } from '../fn-context/types.js';
|
|
6
|
+
export type SourceComputeHostOptions = {
|
|
7
|
+
functions: FuncMap;
|
|
8
|
+
caseContextView?: CaseContextViewFn<RuntimeSlotsSnapshot, unknown>;
|
|
9
|
+
/** Override ApiHost; default Fake, or Rpc when runToken + data.config.backendUrl. */
|
|
10
|
+
apiHost?: ApiHost;
|
|
11
|
+
/** ActionDataSlice used to build ctx for custom / system. */
|
|
12
|
+
data?: ActionDataSlice;
|
|
13
|
+
/** When set with data.config.backendUrl, uses Http RpcApiHost. */
|
|
14
|
+
runToken?: string;
|
|
15
|
+
/** Forwarded on every `ctx.api` RPC as `x-request-id`. */
|
|
16
|
+
getRequestId?: () => string | undefined;
|
|
17
|
+
};
|
|
18
|
+
export interface SourceComputeHost extends ComputeHost {
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Local host: call author FuncMap implementations directly (no SequenceBuild.code).
|
|
22
|
+
*
|
|
23
|
+
* Lookup by name, then dispatch on `kind`:
|
|
24
|
+
* - caseContextView: `args[0]` is the raw snapshot (identity when no view).
|
|
25
|
+
* - compute / boolCompute / custom: `args[0]` is the raw snapshot (projected internally).
|
|
26
|
+
* - system: `args[0]` is the params object.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createSourceComputeHost(options: SourceComputeHostOptions): SourceComputeHost;
|
|
29
|
+
//# sourceMappingURL=source-compute-host.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { resolveCaseContextViewFn } from '../core/functions/define/case-context.js';
|
|
2
|
+
import { buildActionContext } from '../fn-context/build-context.js';
|
|
3
|
+
import { createFakeApiHost } from '../fn-context/fake/index.js';
|
|
4
|
+
import { createRpcApiHost } from '../fn-context/rpc/index.js';
|
|
5
|
+
import { makeCtx } from '../fn-context/make-ctx.js';
|
|
6
|
+
function requireSlots(name, args) {
|
|
7
|
+
const slots = args[0];
|
|
8
|
+
if (slots == null) {
|
|
9
|
+
throw new Error(`Function ${name}: args[0] (slots) required`);
|
|
10
|
+
}
|
|
11
|
+
return slots;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Local host: call author FuncMap implementations directly (no SequenceBuild.code).
|
|
15
|
+
*
|
|
16
|
+
* Lookup by name, then dispatch on `kind`:
|
|
17
|
+
* - caseContextView: `args[0]` is the raw snapshot (identity when no view).
|
|
18
|
+
* - compute / boolCompute / custom: `args[0]` is the raw snapshot (projected internally).
|
|
19
|
+
* - system: `args[0]` is the params object.
|
|
20
|
+
*/
|
|
21
|
+
export function createSourceComputeHost(options) {
|
|
22
|
+
const { functions, data } = options;
|
|
23
|
+
if (Object.hasOwn(functions, 'caseContextView')) {
|
|
24
|
+
throw new Error(`functions map already has 'caseContextView'; that name is the sequence view`);
|
|
25
|
+
}
|
|
26
|
+
const apiHost = resolveApiHost(options);
|
|
27
|
+
const viewFn = resolveCaseContextViewFn(options.caseContextView) ?? ((slots) => slots);
|
|
28
|
+
const registry = {
|
|
29
|
+
...functions,
|
|
30
|
+
caseContextView: { kind: 'caseContextView', main: viewFn },
|
|
31
|
+
};
|
|
32
|
+
async function systemContext() {
|
|
33
|
+
return data ? buildActionContext(data, apiHost) : makeCtx();
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
async execute(name, args = []) {
|
|
37
|
+
const def = registry[name];
|
|
38
|
+
if (!def) {
|
|
39
|
+
throw new Error(`Missing function: ${name}`);
|
|
40
|
+
}
|
|
41
|
+
if (def.kind === 'caseContextView') {
|
|
42
|
+
return def.main(requireSlots(name, args));
|
|
43
|
+
}
|
|
44
|
+
if (def.kind === 'compute' || def.kind === 'boolCompute') {
|
|
45
|
+
return def.main(viewFn(requireSlots(name, args)));
|
|
46
|
+
}
|
|
47
|
+
if (def.kind === 'custom') {
|
|
48
|
+
const caseContext = viewFn(requireSlots(name, args));
|
|
49
|
+
const ctx = await systemContext();
|
|
50
|
+
const fn = def.main;
|
|
51
|
+
return fn(caseContext, ctx);
|
|
52
|
+
}
|
|
53
|
+
const params = (args[0] ?? {});
|
|
54
|
+
const ctx = await systemContext();
|
|
55
|
+
return def.main({ ...ctx, params });
|
|
56
|
+
},
|
|
57
|
+
dispose() { },
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function resolveApiHost(options) {
|
|
61
|
+
if (options.apiHost)
|
|
62
|
+
return options.apiHost;
|
|
63
|
+
if (options.runToken && options.data?.config.backendUrl) {
|
|
64
|
+
return createRpcApiHost({
|
|
65
|
+
baseUrl: options.data.config.backendUrl,
|
|
66
|
+
getToken: () => options.runToken,
|
|
67
|
+
getRequestId: options.getRequestId,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return createFakeApiHost();
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=source-compute-host.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type IntegrationTarget = {
|
|
2
|
+
kind: 'account' | 'resource';
|
|
3
|
+
id: string;
|
|
4
|
+
};
|
|
5
|
+
/** Identity fields every script-facing integration stub carries. */
|
|
6
|
+
export type IntegrationAccountFields = {
|
|
7
|
+
readonly target: IntegrationTarget;
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly credentials: Record<string, unknown>;
|
|
10
|
+
readonly config: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=account-fields.d.ts.map
|