@sheetxl/runner 0.7.28-beta.4 → 0.8.0-beta.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/README.md +15 -3
- package/node/esm/index.mjs +263 -160
- package/package.json +6 -6
- package/types/src/Runner.d.ts +7 -9
- package/types/src/Snippet.d.ts +38 -5
- package/types/src/Types.d.ts +58 -4
package/README.md
CHANGED
|
@@ -19,7 +19,19 @@ npm i -D @sheetxl/runner
|
|
|
19
19
|
## Using the runner
|
|
20
20
|
|
|
21
21
|
``` ts
|
|
22
|
-
export type Runner = (workbook: IWorkbook,
|
|
22
|
+
export type Runner = (workbook: IWorkbook | null, args: ArgV | undefined, options: RunOptions) => Promise<IWorkbook | any | void>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The two data inputs are positional — the workbook and the args. Everything else is host wiring and
|
|
26
|
+
lives in `RunOptions`, `target` included.
|
|
27
|
+
|
|
28
|
+
`run` hands back whatever the script returned: a tally, a range, nothing. A returned `IWorkbook` is
|
|
29
|
+
also the workbook the run ended on — the one to write for `--save`/`--out` — so a host that needs it
|
|
30
|
+
reads it off the result:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const result = await Runner.run(wb, args, { target: 'scripts/transform.ts' });
|
|
34
|
+
const ended = (result as IWorkbook)?.isIWorkbook ? result as IWorkbook : wb;
|
|
23
35
|
```
|
|
24
36
|
|
|
25
37
|
``` ts
|
|
@@ -28,8 +40,8 @@ import { Runner } from '@sheetxl/runner';
|
|
|
28
40
|
|
|
29
41
|
const wb = new Workbook();
|
|
30
42
|
|
|
31
|
-
await Runner.run(wb, '
|
|
32
|
-
|
|
43
|
+
await Runner.run(wb, { raw: ['--region', 'EU'], kv: { region: 'EU' }, _: [] }, {
|
|
44
|
+
target: 'scripts/transform.ts'
|
|
33
45
|
});
|
|
34
46
|
|
|
35
47
|
```
|
package/node/esm/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license @sheetxl/runner - SheetXL - Programmatic execution for SheetXL scripts - v0.
|
|
2
|
+
* @license @sheetxl/runner - SheetXL - Programmatic execution for SheetXL scripts - v0.8.0-beta.2
|
|
3
3
|
*
|
|
4
4
|
* (C) 2026-present SheetXL Inc. & Michael T. Ford
|
|
5
5
|
* License: The license can be found at https://www.sheetxl.com/license.
|
|
@@ -7,37 +7,38 @@
|
|
|
7
7
|
import t from "node:path";
|
|
8
8
|
import e from "node:fs";
|
|
9
9
|
import { pathToFileURL as n } from "node:url";
|
|
10
|
-
import { createLogChannels as r } from "@sheetxl/utils";
|
|
11
|
-
var
|
|
12
|
-
function
|
|
10
|
+
import { createLogChannels as r, resolveLogTimeout as s } from "@sheetxl/utils";
|
|
11
|
+
var o = Object.defineProperty;
|
|
12
|
+
function i(e, n = "SHEETXL_IN") {
|
|
13
13
|
if (t.isAbsolute(e)) return e;
|
|
14
14
|
const r = n ? process.env[n] : void 0;
|
|
15
15
|
return t.resolve(r ?? process.cwd(), e);
|
|
16
16
|
}
|
|
17
|
-
var
|
|
17
|
+
var a = ((t, e) => {
|
|
18
18
|
let n = {};
|
|
19
|
-
for (var r in t)
|
|
19
|
+
for (var r in t) o(n, r, {
|
|
20
20
|
get: t[r],
|
|
21
21
|
enumerable: !0
|
|
22
22
|
});
|
|
23
|
-
return e ||
|
|
23
|
+
return e || o(n, Symbol.toStringTag, { value: "Module" }), n;
|
|
24
24
|
})({
|
|
25
|
-
DEFAULT_DESCRIPTION: () =>
|
|
26
|
-
classify: () =>
|
|
27
|
-
prepare: () =>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
DEFAULT_DESCRIPTION: () => S,
|
|
26
|
+
classify: () => x,
|
|
27
|
+
prepare: () => E,
|
|
28
|
+
toSnippetOffset: () => _
|
|
29
|
+
}), c = null;
|
|
30
|
+
function l() {
|
|
31
|
+
return c || (c = (async () => {
|
|
31
32
|
try {
|
|
32
33
|
const t = await import("typescript");
|
|
33
34
|
return t.default ?? t;
|
|
34
35
|
} catch {
|
|
35
36
|
return null;
|
|
36
37
|
}
|
|
37
|
-
})()),
|
|
38
|
+
})()), c;
|
|
38
39
|
}
|
|
39
|
-
async function
|
|
40
|
-
const e = await
|
|
40
|
+
async function u(t) {
|
|
41
|
+
const e = await l();
|
|
41
42
|
if (!e) return null;
|
|
42
43
|
const n = e.createSourceFile("snippet.ts", t, e.ScriptTarget.Latest, !1, e.ScriptKind.TS);
|
|
43
44
|
return (n.parseDiagnostics?.length ?? 0) > 0 ? null : {
|
|
@@ -45,154 +46,252 @@ async function l(t) {
|
|
|
45
46
|
sf: n
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
|
-
function u(t, e) {
|
|
49
|
-
return t.text.slice(e.getStart(t), e.end);
|
|
50
|
-
}
|
|
51
|
-
function f(t) {
|
|
52
|
-
return t.endsWith(";") ? t : `${t};`;
|
|
53
|
-
}
|
|
54
49
|
function p(t) {
|
|
55
50
|
let e = 0;
|
|
56
51
|
for (let n = 0; n < t.length; n++) "\n" === t[n] && e++;
|
|
57
52
|
return e;
|
|
58
53
|
}
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
const o = s.getStart(t) - n, a = s.end - n;
|
|
67
|
-
r = `${r.slice(0, o)}await (${r.slice(o, a)})${r.slice(a)}`;
|
|
54
|
+
function f(t, e, n) {
|
|
55
|
+
let r = n.getStart(e);
|
|
56
|
+
for (const s of n.declarationList.declarations) {
|
|
57
|
+
const n = s.initializer;
|
|
58
|
+
if (!n) continue;
|
|
59
|
+
const o = n.getStart(e);
|
|
60
|
+
t.copy(r, o), t.add("await ("), t.copy(o, n.end), t.add(")"), r = n.end;
|
|
68
61
|
}
|
|
69
|
-
|
|
62
|
+
t.copy(r, n.end), t.terminate();
|
|
70
63
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
var d = class {
|
|
65
|
+
constructor(t) {
|
|
66
|
+
this._code = t, this.spans = [], this._parts = [], this._length = 0;
|
|
67
|
+
}
|
|
68
|
+
add(t) {
|
|
69
|
+
this._parts.push(t), this._length += t.length;
|
|
70
|
+
}
|
|
71
|
+
copy(t, e) {
|
|
72
|
+
e <= t || (this.spans.push({
|
|
73
|
+
at: this._length,
|
|
74
|
+
from: t,
|
|
75
|
+
length: e - t
|
|
76
|
+
}), this.add(this._code.slice(t, e)));
|
|
77
|
+
}
|
|
78
|
+
terminate() {
|
|
79
|
+
this._parts[this._parts.length - 1]?.endsWith(";") || this.add(";");
|
|
80
|
+
}
|
|
81
|
+
toString() {
|
|
82
|
+
return this._parts.join("");
|
|
83
|
+
}
|
|
84
|
+
}, m = "await wb?.whenCalculationReady?.(); ";
|
|
85
|
+
function h(t, e) {
|
|
86
|
+
const n = e.statements;
|
|
87
|
+
let r = n.length - 1;
|
|
88
|
+
for (; r >= 0 && n[r].kind === t.SyntaxKind.EmptyStatement;) r--;
|
|
89
|
+
if (r < 0) return null;
|
|
90
|
+
const s = n[r];
|
|
91
|
+
if ((t.isExpressionStatement(s) || t.isExportAssignment(s) && !s.isExportEquals) && ((e) => {
|
|
92
|
+
for (; t.isParenthesizedExpression(e);) e = e.expression;
|
|
93
|
+
return t.isArrowFunction(e) || t.isFunctionExpression(e);
|
|
94
|
+
})(s.expression)) return {
|
|
95
|
+
index: r,
|
|
96
|
+
from: s.expression.getStart(e),
|
|
97
|
+
to: s.expression.end
|
|
98
|
+
};
|
|
99
|
+
if (t.isFunctionDeclaration(s) && s.body) {
|
|
100
|
+
const n = t.getModifiers(s) ?? [], o = n.some((e) => e.kind === t.SyntaxKind.ExportKeyword);
|
|
101
|
+
if (n.some((e) => e.kind === t.SyntaxKind.DefaultKeyword) && !s.name || !o && function(t, e) {
|
|
102
|
+
let n = -1;
|
|
103
|
+
for (let r = 0; r < e.statements.length; r++) {
|
|
104
|
+
const s = e.statements[r];
|
|
105
|
+
if (s.kind !== t.SyntaxKind.EmptyStatement && !t.isImportDeclaration(s) && !t.isImportEqualsDeclaration(s)) {
|
|
106
|
+
if (-1 !== n) return -1;
|
|
107
|
+
n = r;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return n;
|
|
111
|
+
}(t, e) === r) {
|
|
112
|
+
let o = s.getStart(e);
|
|
113
|
+
for (const e of n) e.kind !== t.SyntaxKind.ExportKeyword && e.kind !== t.SyntaxKind.DefaultKeyword || (o = e.end);
|
|
114
|
+
for (; o < s.end && /\s/.test(e.text[o]);) o++;
|
|
115
|
+
return {
|
|
116
|
+
index: r,
|
|
117
|
+
from: o,
|
|
118
|
+
to: s.end
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
var y = "const __entry: (ctx: SheetXL.IScript.ScriptContext) => unknown = (", g = (t) => `); ${t} = await __entry(ctx);`;
|
|
125
|
+
async function x(t) {
|
|
126
|
+
const e = await u(t);
|
|
127
|
+
if (!e) return function(t) {
|
|
128
|
+
return /^\s*export\s+default\b/m.test(t) || /^\s*export\s*\{[^}]*\bas\s+default\b/m.test(t);
|
|
129
|
+
}(t) ? "script" : "snippet";
|
|
130
|
+
const { ts: n, sf: r } = e;
|
|
131
|
+
return function(t, e) {
|
|
74
132
|
for (const n of e.statements) {
|
|
75
133
|
if (t.isExportAssignment(n) && !n.isExportEquals) return !0;
|
|
76
134
|
if (t.canHaveModifiers(n) && t.getModifiers(n)?.some((e) => e.kind === t.SyntaxKind.DefaultKeyword)) return !0;
|
|
77
135
|
if (t.isExportDeclaration(n) && n.exportClause && t.isNamedExports(n.exportClause) && n.exportClause.elements.some((t) => "default" === t.name.text)) return !0;
|
|
78
136
|
}
|
|
79
137
|
return !1;
|
|
80
|
-
}(
|
|
81
|
-
|
|
82
|
-
|
|
138
|
+
}(n, r) && !function(t, e) {
|
|
139
|
+
const n = h(t, e);
|
|
140
|
+
return !!n && e.statements.every((e, r) => r === n.index || !t.isExportAssignment(e) && !t.isExportDeclaration(e) && !(t.canHaveModifiers(e) && t.getModifiers(e)?.some((e) => e.kind === t.SyntaxKind.ExportKeyword)));
|
|
141
|
+
}(n, r) ? "script" : "snippet";
|
|
83
142
|
}
|
|
84
|
-
var w = 3,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
143
|
+
var w = 3, S = "Run Snippet", v = (t, e, n = [], r) => {
|
|
144
|
+
const s = [
|
|
145
|
+
...n,
|
|
146
|
+
...r ? [r] : [],
|
|
147
|
+
"export default async function __snippet(ctx: SheetXL.IScript.ScriptContext): Promise<void> {",
|
|
148
|
+
" const { wb, log, warn, error, args } = ctx;",
|
|
149
|
+
" let __result: any = undefined;"
|
|
150
|
+
].join("\n") + "\n";
|
|
151
|
+
return {
|
|
152
|
+
source: s + [
|
|
153
|
+
t,
|
|
154
|
+
" ;",
|
|
155
|
+
e,
|
|
156
|
+
"};"
|
|
157
|
+
].join("\n"),
|
|
158
|
+
bodyStart: s.length
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
async function E(t, e) {
|
|
162
|
+
const n = e?.epilogue ?? " return __result;", r = await u(t);
|
|
97
163
|
if (!r) {
|
|
98
|
-
const e = await
|
|
164
|
+
const e = await l() ? void 0 : "TypeScript is unavailable, so this snippet ran without implicit await or a result value. Install the `typescript` peer dependency to restore them.", { source: r, bodyStart: s } = v(t, n);
|
|
99
165
|
return {
|
|
100
|
-
source:
|
|
166
|
+
source: r,
|
|
101
167
|
kind: "verbatim",
|
|
102
168
|
lineOffset: w,
|
|
169
|
+
spans: [{
|
|
170
|
+
at: s,
|
|
171
|
+
from: 0,
|
|
172
|
+
length: t.length
|
|
173
|
+
}],
|
|
103
174
|
warning: e
|
|
104
175
|
};
|
|
105
176
|
}
|
|
106
|
-
const { ts:
|
|
177
|
+
const { ts: s, sf: o } = r, i = o.statements, a = function(t, e) {
|
|
107
178
|
if (!e.statements[0]) return;
|
|
108
179
|
const n = t.getLeadingCommentRanges(e.text, 0);
|
|
109
180
|
if (n?.length) for (let r = n.length - 1; r >= 0; r--) {
|
|
110
|
-
const
|
|
111
|
-
if (
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
181
|
+
const s = n[r];
|
|
182
|
+
if (s.kind !== t.SyntaxKind.MultiLineCommentTrivia) continue;
|
|
183
|
+
const o = e.text.slice(s.pos, s.end);
|
|
184
|
+
if (o.startsWith("/**")) return o;
|
|
114
185
|
}
|
|
115
|
-
}(
|
|
116
|
-
if (1 ===
|
|
117
|
-
source:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
186
|
+
}(s, o), c = a ? p(a) + 1 : 0;
|
|
187
|
+
if (1 === i.length && s.isBlock(i[0])) {
|
|
188
|
+
const { source: r, bodyStart: s } = v(` __result = await (
|
|
189
|
+
${t}\n );`, n, [], a);
|
|
190
|
+
return {
|
|
191
|
+
source: r,
|
|
192
|
+
kind: "expression",
|
|
193
|
+
lineOffset: w + c + 1,
|
|
194
|
+
spans: [{
|
|
195
|
+
at: s + 21,
|
|
196
|
+
from: 0,
|
|
197
|
+
length: t.length
|
|
198
|
+
}]
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
const { body: x, spans: S, hoisted: E } = function(t, e, n) {
|
|
202
|
+
const r = e.text, s = e.statements;
|
|
203
|
+
let o = -1;
|
|
204
|
+
for (let e = s.length - 1; e >= 0; e--) if (s[e].kind !== t.SyntaxKind.EmptyStatement) {
|
|
205
|
+
t.isExpressionStatement(s[e]) && (o = e);
|
|
126
206
|
break;
|
|
127
207
|
}
|
|
128
|
-
const
|
|
129
|
-
let
|
|
130
|
-
for (let
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
a.push(l === s ? `${n} = await (${t});` : `await (${t});`);
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
a.push(f(g));
|
|
208
|
+
const i = h(t, e), a = [], c = new d(r);
|
|
209
|
+
let l = 0, u = !1;
|
|
210
|
+
for (let d = 0; d < s.length; d++) {
|
|
211
|
+
const h = s[d], x = h.getStart(e);
|
|
212
|
+
if (c.copy(l, x), l = h.end, t.isImportDeclaration(h) || t.isImportEqualsDeclaration(h)) {
|
|
213
|
+
const t = r.slice(x, h.end);
|
|
214
|
+
a.push({
|
|
215
|
+
text: t,
|
|
216
|
+
from: x
|
|
217
|
+
}), c.add("\n".repeat(p(t)));
|
|
218
|
+
continue;
|
|
143
219
|
}
|
|
220
|
+
u && c.add(m), u = !0, i?.index !== d ? t.isVariableStatement(h) ? f(c, e, h) : t.isExpressionStatement(h) ? (c.add(d === o ? `${n} = await (` : "await ("), c.copy(h.expression.getStart(e), h.expression.end), c.add(");")) : (c.copy(x, h.end), c.terminate()) : (c.add(y), c.copy(i.from, i.to), c.add(g(n)));
|
|
144
221
|
}
|
|
145
|
-
return
|
|
146
|
-
body:
|
|
147
|
-
|
|
222
|
+
return c.copy(l, r.length), {
|
|
223
|
+
body: c.toString(),
|
|
224
|
+
spans: c.spans,
|
|
225
|
+
hoisted: a
|
|
226
|
+
};
|
|
227
|
+
}(s, o, "__result"), _ = E.map((t) => t.text), b = E.length > 0 ? p(_.join("\n")) + E.length : 0, { source: k, bodyStart: K } = v(x, n, _, a);
|
|
228
|
+
let D = 0;
|
|
229
|
+
const R = E.map((t) => {
|
|
230
|
+
const e = {
|
|
231
|
+
at: D,
|
|
232
|
+
from: t.from,
|
|
233
|
+
length: t.text.length
|
|
148
234
|
};
|
|
149
|
-
|
|
235
|
+
return D += t.text.length + 1, e;
|
|
236
|
+
});
|
|
150
237
|
return {
|
|
151
|
-
source:
|
|
238
|
+
source: k,
|
|
152
239
|
kind: "statements",
|
|
153
|
-
lineOffset: w +
|
|
240
|
+
lineOffset: w + b + c,
|
|
241
|
+
spans: [...R, ...S.map((t) => ({
|
|
242
|
+
...t,
|
|
243
|
+
at: t.at + K
|
|
244
|
+
}))]
|
|
154
245
|
};
|
|
155
246
|
}
|
|
156
|
-
|
|
247
|
+
function _(t, e) {
|
|
248
|
+
let n = null;
|
|
249
|
+
for (const r of t.spans) {
|
|
250
|
+
if (r.at > e) return null !== n && e < n.at + n.length ? n.from + (e - n.at) : r.from;
|
|
251
|
+
n = r;
|
|
252
|
+
}
|
|
253
|
+
return n ? n.from + Math.min(e - n.at, n.length) : 0;
|
|
254
|
+
}
|
|
255
|
+
async function b(t, e, n = "Runner timed out") {
|
|
157
256
|
if (!e || e <= 0) return t;
|
|
158
257
|
let r;
|
|
159
|
-
const
|
|
258
|
+
const s = new Promise((t, s) => r = setTimeout(() => s(new Error(n)), e));
|
|
160
259
|
try {
|
|
161
|
-
return await Promise.race([t,
|
|
260
|
+
return await Promise.race([t, s]);
|
|
162
261
|
} finally {
|
|
163
262
|
clearTimeout(r);
|
|
164
263
|
}
|
|
165
264
|
}
|
|
166
|
-
var
|
|
167
|
-
function
|
|
168
|
-
return
|
|
265
|
+
var k = null;
|
|
266
|
+
function K() {
|
|
267
|
+
return k || (k = (async () => {
|
|
169
268
|
const { register: t } = await import("tsx/esm/api");
|
|
170
269
|
t({ tsconfig: !1 });
|
|
171
|
-
})()),
|
|
270
|
+
})()), k;
|
|
172
271
|
}
|
|
173
|
-
var
|
|
174
|
-
function
|
|
175
|
-
return
|
|
176
|
-
const { registerHooks: e, stripTypeScriptTypes: r } = await import("node:module"),
|
|
272
|
+
var D = "data:text/typescript,", R = "data:text/javascript,", I = null;
|
|
273
|
+
function $() {
|
|
274
|
+
return I || (I = (async () => {
|
|
275
|
+
const { registerHooks: e, stripTypeScriptTypes: r } = await import("node:module"), s = r;
|
|
177
276
|
e({
|
|
178
|
-
resolve(e, r,
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
const
|
|
182
|
-
return
|
|
277
|
+
resolve(e, r, s) {
|
|
278
|
+
const o = r.parentURL;
|
|
279
|
+
if (o && (o.startsWith(D) || o.startsWith(R))) {
|
|
280
|
+
const o = n(t.join(process.cwd(), "_inline_.ts")).href;
|
|
281
|
+
return s(e, {
|
|
183
282
|
...r,
|
|
184
|
-
parentURL:
|
|
283
|
+
parentURL: o
|
|
185
284
|
});
|
|
186
285
|
}
|
|
187
|
-
return
|
|
286
|
+
return s(e, r);
|
|
188
287
|
},
|
|
189
288
|
load(t, e, n) {
|
|
190
|
-
if (t.startsWith(
|
|
289
|
+
if (t.startsWith(D)) {
|
|
191
290
|
const e = decodeURIComponent(t.slice(21)), n = process.emitWarning;
|
|
192
291
|
let r;
|
|
193
292
|
process.emitWarning = () => {};
|
|
194
293
|
try {
|
|
195
|
-
r =
|
|
294
|
+
r = s(e, { mode: "transform" });
|
|
196
295
|
} finally {
|
|
197
296
|
process.emitWarning = n;
|
|
198
297
|
}
|
|
@@ -205,85 +304,89 @@ function _() {
|
|
|
205
304
|
return n(t, e);
|
|
206
305
|
}
|
|
207
306
|
});
|
|
208
|
-
})()),
|
|
307
|
+
})()), I;
|
|
209
308
|
}
|
|
210
|
-
async function
|
|
211
|
-
const r = "string" == typeof (
|
|
212
|
-
var
|
|
213
|
-
const
|
|
214
|
-
return ".ts" !==
|
|
309
|
+
async function C(e) {
|
|
310
|
+
const r = "string" == typeof (s = e) ? s.startsWith("file:") ? s : n(t.resolve(process.cwd(), s)).href : s.href;
|
|
311
|
+
var s;
|
|
312
|
+
const o = t.extname(new URL(r).pathname).toLowerCase();
|
|
313
|
+
return ".ts" !== o && ".tsx" !== o || await K(), import(r);
|
|
215
314
|
}
|
|
216
|
-
async function
|
|
217
|
-
const
|
|
218
|
-
debug: (t) =>
|
|
219
|
-
log: (t) =>
|
|
220
|
-
info: (t) =>
|
|
221
|
-
warn: (t) =>
|
|
222
|
-
error: (t) =>
|
|
223
|
-
}, { render:
|
|
315
|
+
async function L(t, e, n) {
|
|
316
|
+
const s = n.notifier ?? (await import("@sheetxl/sdk")).Notifier, o = r({
|
|
317
|
+
debug: (t) => s.debug(t),
|
|
318
|
+
log: (t) => s.log(t),
|
|
319
|
+
info: (t) => s.log(t),
|
|
320
|
+
warn: (t) => s.warn(t),
|
|
321
|
+
error: (t) => s.error(t)
|
|
322
|
+
}, { render: n.render });
|
|
224
323
|
return {
|
|
225
324
|
ctx: {
|
|
226
|
-
args: e
|
|
325
|
+
args: e ?? {
|
|
227
326
|
raw: [],
|
|
228
327
|
kv: {},
|
|
229
328
|
_: []
|
|
230
329
|
},
|
|
231
330
|
wb: t,
|
|
232
|
-
log:
|
|
233
|
-
warn:
|
|
234
|
-
error:
|
|
331
|
+
log: o.log,
|
|
332
|
+
warn: o.warn,
|
|
333
|
+
error: o.error
|
|
235
334
|
},
|
|
236
|
-
channels:
|
|
237
|
-
notifier:
|
|
335
|
+
channels: o,
|
|
336
|
+
notifier: s
|
|
238
337
|
};
|
|
239
338
|
}
|
|
240
|
-
var
|
|
241
|
-
const
|
|
242
|
-
let
|
|
243
|
-
const
|
|
339
|
+
var T = async (n, r, o) => {
|
|
340
|
+
const i = o.target, a = Array.isArray(i) ? i : [i], c = await Promise.all(a.map((n) => (async (n, r = {}) => {
|
|
341
|
+
let s = null;
|
|
342
|
+
const o = (t, e) => {
|
|
244
343
|
let n = t?.default;
|
|
245
344
|
if (n && "function" != typeof n && "function" == typeof n.default && (n = n.default), "function" != typeof n) throw new Error(`Script '${e}' has no runnable entry: export a default function, e.g. \`export default function main(ctx) { … }\`.`);
|
|
246
345
|
return n;
|
|
247
346
|
};
|
|
248
|
-
if ("function" == typeof n)
|
|
347
|
+
if ("function" == typeof n) s = n;
|
|
249
348
|
else if ("string" == typeof n || n instanceof URL) {
|
|
250
|
-
let
|
|
251
|
-
if ("string" != typeof n || /^[a-z]+:\/\//i.test(n))
|
|
349
|
+
let i = null;
|
|
350
|
+
if ("string" != typeof n || /^[a-z]+:\/\//i.test(n)) i = await C(n);
|
|
252
351
|
else {
|
|
253
|
-
const
|
|
254
|
-
if (!e.existsSync(
|
|
255
|
-
e.accessSync(
|
|
352
|
+
const s = t.resolve(r.cwd ?? process.cwd(), n);
|
|
353
|
+
if (!e.existsSync(s)) throw new Error(`Script not found: ${s}`);
|
|
354
|
+
e.accessSync(s, e.constants.R_OK), i = await C(s);
|
|
256
355
|
}
|
|
257
|
-
|
|
356
|
+
s = o(i, String(n));
|
|
258
357
|
} else if ("code" in n) {
|
|
259
358
|
const t = n.filename ?? "inline.ts";
|
|
260
359
|
let e, r = null;
|
|
261
|
-
t.endsWith(".ts") || t.endsWith(".tsx") ? (await
|
|
360
|
+
t.endsWith(".ts") || t.endsWith(".tsx") ? (await K(), await $(), e = `${D}${encodeURIComponent(n.code)}`, r = await import(e)) : (await $(), e = `${R}${encodeURIComponent(n.code)}`, r = await import(e)), s = o(r, t);
|
|
262
361
|
} else {
|
|
263
362
|
if (!("runner" in n)) throw new Error("Invalid run target specified.");
|
|
264
|
-
|
|
363
|
+
s = n.runner;
|
|
265
364
|
}
|
|
266
|
-
return
|
|
267
|
-
})(n,
|
|
268
|
-
let
|
|
269
|
-
const
|
|
270
|
-
for (let t = 0; t <
|
|
271
|
-
const e =
|
|
272
|
-
|
|
273
|
-
const
|
|
274
|
-
let
|
|
365
|
+
return s;
|
|
366
|
+
})(n, o))), l = Date.now();
|
|
367
|
+
let u, p = n, f = null;
|
|
368
|
+
const d = c.length;
|
|
369
|
+
for (let t = 0; t < d; t++) {
|
|
370
|
+
const e = c[t], { ctx: n, channels: i, notifier: a } = await L(p, r, o);
|
|
371
|
+
f = a;
|
|
372
|
+
const l = Date.now();
|
|
373
|
+
let m;
|
|
374
|
+
const h = p, y = () => Promise.resolve().then(() => e(n));
|
|
275
375
|
try {
|
|
276
|
-
|
|
376
|
+
m = await b(null !== h ? Promise.resolve(h.doBatch(y, "Run Script")) : y(), o.timeoutMs, "Runner timed out");
|
|
277
377
|
} finally {
|
|
278
|
-
await
|
|
378
|
+
await i.flush({ timeoutMs: s(o.logTimeoutMs) });
|
|
279
379
|
}
|
|
280
|
-
const
|
|
281
|
-
null !=
|
|
380
|
+
const g = Date.now();
|
|
381
|
+
u = m, null != m && m.isIWorkbook && (p = m), a.debug(`Run ${1 === d ? "" : `${t + 1}/${d} `}completed in ${g - l} ms.`);
|
|
282
382
|
}
|
|
283
|
-
if (
|
|
383
|
+
if (d > 1 && f) {
|
|
284
384
|
const t = Date.now();
|
|
285
|
-
|
|
385
|
+
f.debug(`All runs completed in ${t - l} ms.`);
|
|
286
386
|
}
|
|
287
|
-
return
|
|
387
|
+
return u;
|
|
388
|
+
}, W = { run: async (t, e, n) => {
|
|
389
|
+
const { withScriptRuntime: r } = await import("@sheetxl/sdk");
|
|
390
|
+
return r(() => T(t, e, n));
|
|
288
391
|
} };
|
|
289
|
-
export {
|
|
392
|
+
export { a as Snippet, W as default, i as resolvePath };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sheetxl/runner",
|
|
3
3
|
"description": "SheetXL - Programmatic execution for SheetXL scripts",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.8.0-beta.2",
|
|
5
5
|
"license": "SEE LICENSE IN <LICENSE>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -36,11 +36,11 @@
|
|
|
36
36
|
"runner"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@sheetxl/io": "^0.
|
|
40
|
-
"@sheetxl/primitives": "^0.
|
|
41
|
-
"@sheetxl/scripting": "^0.
|
|
42
|
-
"@sheetxl/sdk": "^0.
|
|
43
|
-
"@sheetxl/utils": "^0.
|
|
39
|
+
"@sheetxl/io": "^0.8.0-beta.2",
|
|
40
|
+
"@sheetxl/primitives": "^0.8.0-beta.2",
|
|
41
|
+
"@sheetxl/scripting": "^0.8.0-beta.2",
|
|
42
|
+
"@sheetxl/sdk": "^0.8.0-beta.2",
|
|
43
|
+
"@sheetxl/utils": "^0.8.0-beta.2"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"typescript": "^5.9.0 || ^6.0.0"
|
package/types/src/Runner.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { Runner } from './Types';
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Runs the pipeline with the `SheetXL` global installed.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* not per-step input. This follows the standard Unix convention where environment
|
|
10
|
-
* variables / flags are visible to every process in a pipeline.
|
|
11
|
-
* - Returns the final workbook (which may differ from the one passed in).
|
|
5
|
+
* @remarks
|
|
6
|
+
* The declarations a script is written against promise `SheetXL.ISheet.Visibility.Hidden` is a
|
|
7
|
+
* VALUE. A compiled macro gets it from the macro runtime; source run here is type-stripped and
|
|
8
|
+
* imported, so nothing would define it. The install spans module LOADING too, not only the call —
|
|
9
|
+
* a script may read an enum at its top level — and is removed however the run ends.
|
|
12
10
|
*/
|
|
13
11
|
export declare const run: Runner;
|
|
14
12
|
declare const _default: {
|
package/types/src/Snippet.d.ts
CHANGED
|
@@ -9,7 +9,16 @@
|
|
|
9
9
|
* contract promises two things a bare paste into an async function does not give them —
|
|
10
10
|
* 1. **implicit await**: nothing in a snippet is ever written `await`, yet a promise-returning
|
|
11
11
|
* statement must have settled before the next one runs;
|
|
12
|
-
* 2. **the final expression is the result**: exactly like a REPL, or `node -p
|
|
12
|
+
* 2. **the final expression is the result**: exactly like a REPL, or `node -p`;
|
|
13
|
+
* 3. **values are current**: calculation is asynchronous, so a formula set by one statement is
|
|
14
|
+
* settled before the next statement runs — `setFormula('=1+1')` then `getValue()` reads `2`,
|
|
15
|
+
* the way it reads in Excel, rather than the `null` a bare paste would see.
|
|
16
|
+
*
|
|
17
|
+
* **A snippet that is a function is called.** Its value is the function — `async ({ wb }) => …` as
|
|
18
|
+
* the final expression, or a snippet that is only `function main(ctx) { … }` — so it is called with
|
|
19
|
+
* the context, typed as a script's entry would be, and its return is the result. `export default` in
|
|
20
|
+
* front of an arrow or an anonymous function, with nothing else exported, is ceremony for the same
|
|
21
|
+
* thing and runs the same way: the script form is kept for what needs it, a named entry or exports.
|
|
13
22
|
*
|
|
14
23
|
* This is the same problem Node's REPL solves in `lib/internal/repl/await.js`, and it is solved the
|
|
15
24
|
* same way: **parse for real, then rewrite the top-level statements from the AST.** Statement
|
|
@@ -32,9 +41,21 @@
|
|
|
32
41
|
* context parameter) so the identical source compiles on every path: the CLI's type-stripping
|
|
33
42
|
* loader ignores the annotations, and the `ScriptCompiler`'s linter requires them.
|
|
34
43
|
*/
|
|
44
|
+
/**
|
|
45
|
+
* A run of the author's own text inside an emitted source: `length` characters that sit at `at`
|
|
46
|
+
* in the emitted text and came from `from` in the snippet.
|
|
47
|
+
*/
|
|
48
|
+
export interface Span {
|
|
49
|
+
at: number;
|
|
50
|
+
from: number;
|
|
51
|
+
length: number;
|
|
52
|
+
}
|
|
35
53
|
/** How a source will run — see the module remarks for the contract. */
|
|
36
54
|
export type SourceForm = 'script' | 'snippet';
|
|
37
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* `script` when the source has a top-level default export; `snippet` otherwise — and when that
|
|
57
|
+
* export is only an entry function (`export default async ({ wb }) => …`), which runs as a snippet.
|
|
58
|
+
*/
|
|
38
59
|
export declare function classify(code: string): Promise<SourceForm>;
|
|
39
60
|
export interface WrapOptions {
|
|
40
61
|
/**
|
|
@@ -55,11 +76,13 @@ export interface Prepared {
|
|
|
55
76
|
*/
|
|
56
77
|
kind: 'expression' | 'statements' | 'verbatim';
|
|
57
78
|
/**
|
|
58
|
-
* Lines of wrapper prologue before the first line of user code
|
|
59
|
-
*
|
|
60
|
-
*
|
|
79
|
+
* Lines of wrapper prologue before the first line of user code. Exact for every kind: the
|
|
80
|
+
* rewrite emits the original trivia, so no line moves. It places LINES only — to point a
|
|
81
|
+
* compiler position back into the snippet, line and column, use {@link toSnippetOffset}.
|
|
61
82
|
*/
|
|
62
83
|
lineOffset: number;
|
|
84
|
+
/** Where the author's text sits within {@link source}, in source order. */
|
|
85
|
+
spans: Span[];
|
|
63
86
|
/**
|
|
64
87
|
* Set only when the contract could not be honored — no implicit await, no result. Callers should
|
|
65
88
|
* surface it; a snippet quietly returning `undefined` is worse than one that failed.
|
|
@@ -86,4 +109,14 @@ export declare const DEFAULT_DESCRIPTION = "Run Snippet";
|
|
|
86
109
|
* sidesteps any position mapping — the whole snippet is the expression.
|
|
87
110
|
*/
|
|
88
111
|
export declare function prepare(code: string, options?: WrapOptions): Promise<Prepared>;
|
|
112
|
+
/**
|
|
113
|
+
* Where an offset into a prepared {@link Prepared.source} sits in the snippet the author wrote.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* Exact wherever the offset falls in the author's own text, which is where a compiler puts a
|
|
117
|
+
* diagnostic about it. An offset in text the wrapper added has no place in the snippet, so it
|
|
118
|
+
* answers with the nearest thing that does: the author's next character, or the end of their last
|
|
119
|
+
* text when nothing of theirs follows.
|
|
120
|
+
*/
|
|
121
|
+
export declare function toSnippetOffset(prepared: Prepared, offset: number): number;
|
|
89
122
|
//# sourceMappingURL=Snippet.d.ts.map
|
package/types/src/Types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IWorkbook } from '@sheetxl/sdk';
|
|
2
|
-
import { LogRenderer } from '@sheetxl/utils';
|
|
2
|
+
import { LogRenderer, MaybePromise } from '@sheetxl/utils';
|
|
3
3
|
export type Notifier = typeof import('@sheetxl/sdk').Notifier;
|
|
4
4
|
/**
|
|
5
5
|
* Arguments passed after `--` on the CLI (`sheetxl -f <script> [workbook] -- [args...]`).
|
|
@@ -48,6 +48,9 @@ export type RunContext = {
|
|
|
48
48
|
* Variadic and value-taking, like `console.log`: `log('rows:', n)` and `log(range.getStats())`
|
|
49
49
|
* both work. An SDK object renders as its markdown report when the host supplies a renderer
|
|
50
50
|
* (the CLI does); otherwise it falls back to JSON.
|
|
51
|
+
*
|
|
52
|
+
* A promise is awaited and logs as its settled value, so `log(sheet.evaluate(…))` needs no
|
|
53
|
+
* `await`. Output order is still call order.
|
|
51
54
|
*/
|
|
52
55
|
readonly log: (...args: any[]) => void;
|
|
53
56
|
/** Diagnostics: something surprising but survivable. Travels separately from `log` (stderr). */
|
|
@@ -57,8 +60,19 @@ export type RunContext = {
|
|
|
57
60
|
};
|
|
58
61
|
/**
|
|
59
62
|
* The function signature for a script run by the CLI or the MCP `run` tool.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* The return value is the run's RESULT, the same contract a snippet's final expression has, so it
|
|
66
|
+
* is deliberately unconstrained — a tally, an `ICellRange`, a `getStats()` object. Returning
|
|
67
|
+
* nothing is normal: a run whose output is its `log` has no answer to hand back.
|
|
68
|
+
*
|
|
69
|
+
* An `IWorkbook` is the one return that is also *structural*: it becomes the workbook the next
|
|
70
|
+
* stage runs against, on top of being the result. Nothing else is inspected — an array is just an
|
|
71
|
+
* array.
|
|
72
|
+
*
|
|
73
|
+
* Sync or async, both real: `function main(ctx): number[]` is as valid as the `async` spelling.
|
|
60
74
|
*/
|
|
61
|
-
export type RunFunction = (ctx: RunContext) =>
|
|
75
|
+
export type RunFunction = (ctx: RunContext) => MaybePromise<IWorkbook | any | void>;
|
|
62
76
|
/**
|
|
63
77
|
* Possible targets for the runner to execute
|
|
64
78
|
*/
|
|
@@ -69,7 +83,17 @@ export type RunTarget = string | URL | {
|
|
|
69
83
|
runner: RunFunction;
|
|
70
84
|
};
|
|
71
85
|
export interface RunOptions {
|
|
72
|
-
|
|
86
|
+
/**
|
|
87
|
+
* What to run — a path, a URL, inline code, or a function.
|
|
88
|
+
*
|
|
89
|
+
* @remarks
|
|
90
|
+
* An array runs as a serial pipeline: each stage gets the workbook the one before it left, and
|
|
91
|
+
* the run answers with the LAST stage's return value. That makes a chain lossy in one corner —
|
|
92
|
+
* an early stage that swapped the workbook is only visible if the last stage hands it back — so
|
|
93
|
+
* a host that needs per-stage reporting calls `run` once per target instead, which is what the
|
|
94
|
+
* CLI does (it owns the error report for the target that failed).
|
|
95
|
+
*/
|
|
96
|
+
target: RunTarget | RunTarget[];
|
|
73
97
|
/** The host's output sink (toasts on a UI host, the console on the CLI). Not exposed to scripts. */
|
|
74
98
|
notifier?: Notifier;
|
|
75
99
|
/**
|
|
@@ -82,6 +106,18 @@ export interface RunOptions {
|
|
|
82
106
|
render?: LogRenderer;
|
|
83
107
|
signal?: AbortSignal;
|
|
84
108
|
timeoutMs?: number;
|
|
109
|
+
/**
|
|
110
|
+
* How long a finished stage waits for what it logged to settle, in milliseconds. `0` waits
|
|
111
|
+
* indefinitely.
|
|
112
|
+
*
|
|
113
|
+
* @remarks
|
|
114
|
+
* `log(promise)` prints what the promise settles to, so a stage is not done until its log is. A
|
|
115
|
+
* value still unsettled when this elapses prints as `[pending]`; nothing is thrown and the rest
|
|
116
|
+
* of the log is kept. Independent of `timeoutMs`, which bounds the script itself.
|
|
117
|
+
*
|
|
118
|
+
* @defaultValue `DEFAULT_LOG_TIMEOUT_MS` (10 seconds)
|
|
119
|
+
*/
|
|
120
|
+
logTimeoutMs?: number;
|
|
85
121
|
cwd?: string;
|
|
86
122
|
ts?: {
|
|
87
123
|
enabled?: boolean;
|
|
@@ -90,6 +126,24 @@ export interface RunOptions {
|
|
|
90
126
|
}
|
|
91
127
|
/**
|
|
92
128
|
* Run any RunFunction with a built in context.
|
|
129
|
+
*
|
|
130
|
+
* @remarks
|
|
131
|
+
* The two data inputs are positional — the workbook the run is against, and the args it was given.
|
|
132
|
+
* Everything else is host wiring and lives in {@link RunOptions}, including `target`, which is why
|
|
133
|
+
* the options are required rather than optional.
|
|
134
|
+
*
|
|
135
|
+
* **Hands back exactly what the entry returned**, undisturbed — see {@link RunFunction}. A host
|
|
136
|
+
* that also needs the workbook the run ended on reads it off that value when it is one:
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* const result = await run(wb, args, { target: 'transform.ts' });
|
|
140
|
+
* const ended = (result as IWorkbook)?.isIWorkbook ? result as IWorkbook : wb;
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* Always a promise, even for a sync entry: resolving a target loads modules.
|
|
144
|
+
*
|
|
145
|
+
* `args` is spelled `ArgV | undefined` rather than `args?` because TypeScript will not let an
|
|
146
|
+
* optional parameter precede a required one — a run with no args passes `undefined` for it.
|
|
93
147
|
*/
|
|
94
|
-
export type Runner = (workbook: IWorkbook | null,
|
|
148
|
+
export type Runner = (workbook: IWorkbook | null, args: ArgV | undefined, options: RunOptions) => Promise<IWorkbook | any | void>;
|
|
95
149
|
//# sourceMappingURL=Types.d.ts.map
|