@sheetxl/runner 0.8.0-beta.1 → 0.8.0-beta.3

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 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, target: RunTarget | RunTarget[], options?: RunOptions) => Promise<void>;
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, 'scripts/transform.ts', {
32
- args: ['region=EU']
43
+ await Runner.run(wb, { raw: ['--region', 'EU'], kv: { region: 'EU' }, _: [] }, {
44
+ target: 'scripts/transform.ts'
33
45
  });
34
46
 
35
47
  ```
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license @sheetxl/runner - SheetXL - Programmatic execution for SheetXL scripts - v0.8.0-beta.1
2
+ * @license @sheetxl/runner - SheetXL - Programmatic execution for SheetXL scripts - v0.8.0-beta.3
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 i = Object.defineProperty;
12
- function s(e, n = "SHEETXL_IN") {
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 o = ((t, e) => {
17
+ var a = ((t, e) => {
18
18
  let n = {};
19
- for (var r in t) i(n, r, {
19
+ for (var r in t) o(n, r, {
20
20
  get: t[r],
21
21
  enumerable: !0
22
22
  });
23
- return e || i(n, Symbol.toStringTag, { value: "Module" }), n;
23
+ return e || o(n, Symbol.toStringTag, { value: "Module" }), n;
24
24
  })({
25
- DEFAULT_DESCRIPTION: () => g,
26
- classify: () => m,
27
- prepare: () => y
28
- }), a = null;
29
- function c() {
30
- return a || (a = (async () => {
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
- })()), a;
38
+ })()), c;
38
39
  }
39
- async function l(t) {
40
- const e = await c();
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 d(t, e) {
60
- const n = e.getStart(t);
61
- let r = t.text.slice(n, e.end);
62
- const i = e.declarationList.declarations;
63
- for (let e = i.length - 1; e >= 0; e--) {
64
- const s = i[e].initializer;
65
- if (!s) continue;
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
- return f(r);
62
+ t.copy(r, n.end), t.terminate();
70
63
  }
71
- async function m(t) {
72
- const e = await l(t);
73
- return e ? function(t, e) {
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
- }(e.ts, e.sf) ? "script" : "snippet" : function(t) {
81
- return /^\s*export\s+default\b/m.test(t) || /^\s*export\s*\{[^}]*\bas\s+default\b/m.test(t);
82
- }(t) ? "script" : "snippet";
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, g = "Run Snippet", h = (t, e, n = [], r) => [
85
- ...n,
86
- ...r ? [r] : [],
87
- "export default async function __snippet(ctx: SheetXL.IScript.ScriptContext): Promise<void> {",
88
- " const { wb, log, warn, error, args } = ctx as any;",
89
- " let __result: any = undefined;",
90
- t,
91
- " ;",
92
- e,
93
- "};"
94
- ].join("\n");
95
- async function y(t, e) {
96
- const n = e?.epilogue ?? " return __result;", r = await l(t);
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 c() ? void 0 : "TypeScript is unavailable, so this snippet ran without implicit await or a result value. Install the `typescript` peer dependency to restore them.";
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: h(t, n),
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: i, sf: s } = r, o = s.statements, a = function(t, e) {
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 i = n[r];
111
- if (i.kind !== t.SyntaxKind.MultiLineCommentTrivia) continue;
112
- const s = e.text.slice(i.pos, i.end);
113
- if (s.startsWith("/**")) return s;
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
- }(i, s), m = a ? p(a) + 1 : 0;
116
- if (1 === o.length && i.isBlock(o[0])) return {
117
- source: h(` __result = await (\n${t}\n );`, n, [], a),
118
- kind: "expression",
119
- lineOffset: w + m + 1
120
- };
121
- const { body: g, hoisted: y } = function(t, e, n) {
122
- const r = e.text, i = e.statements;
123
- let s = -1;
124
- for (let e = i.length - 1; e >= 0; e--) if (i[e].kind !== t.SyntaxKind.EmptyStatement) {
125
- t.isExpressionStatement(i[e]) && (s = e);
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 o = [], a = [];
129
- let c = 0;
130
- for (let l = 0; l < i.length; l++) {
131
- const m = i[l], w = m.getStart(e);
132
- a.push(r.slice(c, w)), c = m.end;
133
- const g = r.slice(w, m.end);
134
- if (t.isImportDeclaration(m) || t.isImportEqualsDeclaration(m)) o.push(g), a.push("\n".repeat(p(g)));
135
- else if (t.isVariableStatement(m)) a.push(d(e, m));
136
- else {
137
- if (t.isExpressionStatement(m)) {
138
- const t = u(e, m.expression);
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 a.push(r.slice(c)), {
146
- body: a.join(""),
147
- hoisted: o
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
- }(i, s, "__result"), x = y.length > 0 ? p(y.join("\n")) + y.length : 0;
235
+ return D += t.text.length + 1, e;
236
+ });
150
237
  return {
151
- source: h(g, n, y, a),
238
+ source: k,
152
239
  kind: "statements",
153
- lineOffset: w + x + m
240
+ lineOffset: w + b + c,
241
+ spans: [...R, ...S.map((t) => ({
242
+ ...t,
243
+ at: t.at + K
244
+ }))]
154
245
  };
155
246
  }
156
- async function x(t, e, n = "Runner timed out") {
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 i = new Promise((t, i) => r = setTimeout(() => i(new Error(n)), e));
258
+ const s = new Promise((t, s) => r = setTimeout(() => s(new Error(n)), e));
160
259
  try {
161
- return await Promise.race([t, i]);
260
+ return await Promise.race([t, s]);
162
261
  } finally {
163
262
  clearTimeout(r);
164
263
  }
165
264
  }
166
- var S = null;
167
- function v() {
168
- return S || (S = (async () => {
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
- })()), S;
270
+ })()), k;
172
271
  }
173
- var b = "data:text/typescript,", $ = "data:text/javascript,", E = null;
174
- function _() {
175
- return E || (E = (async () => {
176
- const { registerHooks: e, stripTypeScriptTypes: r } = await import("node:module"), i = r;
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, i) {
179
- const s = r.parentURL;
180
- if (s && (s.startsWith(b) || s.startsWith($))) {
181
- const s = n(t.join(process.cwd(), "_inline_.ts")).href;
182
- return i(e, {
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: s
283
+ parentURL: o
185
284
  });
186
285
  }
187
- return i(e, r);
286
+ return s(e, r);
188
287
  },
189
288
  load(t, e, n) {
190
- if (t.startsWith(b)) {
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 = i(e, { mode: "transform" });
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
- })()), E;
307
+ })()), I;
209
308
  }
210
- async function R(e) {
211
- const r = "string" == typeof (i = e) ? i.startsWith("file:") ? i : n(t.resolve(process.cwd(), i)).href : i.href;
212
- var i;
213
- const s = t.extname(new URL(r).pathname).toLowerCase();
214
- return ".ts" !== s && ".tsx" !== s || await v(), import(r);
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 k(t, e) {
217
- const n = e.notifier ?? (await import("@sheetxl/sdk")).Notifier, i = r({
218
- debug: (t) => n.debug(t),
219
- log: (t) => n.log(t),
220
- info: (t) => n.log(t),
221
- warn: (t) => n.warn(t),
222
- error: (t) => n.error(t)
223
- }, { render: e.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.args ?? {
325
+ args: e ?? {
227
326
  raw: [],
228
327
  kv: {},
229
328
  _: []
230
329
  },
231
330
  wb: t,
232
- log: i.log,
233
- warn: i.warn,
234
- error: i.error
331
+ log: o.log,
332
+ warn: o.warn,
333
+ error: o.error
235
334
  },
236
- channels: i,
237
- notifier: n
335
+ channels: o,
336
+ notifier: s
238
337
  };
239
338
  }
240
- var C = { run: async (n, r, i = {}) => {
241
- const s = Array.isArray(r) ? r : [r], o = await Promise.all(s.map((n) => (async (n, r = {}) => {
242
- let i = null;
243
- const s = (t, e) => {
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) i = n;
347
+ if ("function" == typeof n) s = n;
249
348
  else if ("string" == typeof n || n instanceof URL) {
250
- let o = null;
251
- if ("string" != typeof n || /^[a-z]+:\/\//i.test(n)) o = await R(n);
349
+ let i = null;
350
+ if ("string" != typeof n || /^[a-z]+:\/\//i.test(n)) i = await C(n);
252
351
  else {
253
- const i = t.resolve(r.cwd ?? process.cwd(), n);
254
- if (!e.existsSync(i)) throw new Error(`Script not found: ${i}`);
255
- e.accessSync(i, e.constants.R_OK), o = await R(i);
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
- i = s(o, String(n));
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 v(), await _(), e = `${b}${encodeURIComponent(n.code)}`, r = await import(e)) : (await _(), e = `${$}${encodeURIComponent(n.code)}`, r = await import(e)), i = s(r, t);
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
- i = n.runner;
363
+ s = n.runner;
265
364
  }
266
- return i;
267
- })(n, i))), a = Date.now();
268
- let c = n, l = null;
269
- const u = o.length;
270
- for (let t = 0; t < u; t++) {
271
- const e = o[t], { ctx: n, channels: r, notifier: s } = await k(c, i);
272
- l = s;
273
- const a = Date.now();
274
- let f;
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
- f = await x(Promise.resolve().then(() => e(n)), i.timeoutMs, "Runner timed out");
376
+ m = await b(null !== h ? Promise.resolve(h.doBatch(y, "Run Script")) : y(), o.timeoutMs, "Runner timed out");
277
377
  } finally {
278
- await r.flush();
378
+ await i.flush({ timeoutMs: s(o.logTimeoutMs) });
279
379
  }
280
- const p = Date.now();
281
- null != f && f.isIWorkbook && (c = f), s.debug(`Run ${1 === u ? "" : `${t + 1}/${u} `}completed in ${p - a} ms.`);
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 (u > 1 && l) {
383
+ if (d > 1 && f) {
284
384
  const t = Date.now();
285
- l.debug(`All runs completed in ${t - a} ms.`);
385
+ f.debug(`All runs completed in ${t - l} ms.`);
286
386
  }
287
- return c ?? void 0;
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 { o as Snippet, C as default, s as resolvePath };
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.8.0-beta.1",
4
+ "version": "0.8.0-beta.3",
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.8.0-beta.1",
40
- "@sheetxl/primitives": "^0.8.0-beta.1",
41
- "@sheetxl/scripting": "^0.8.0-beta.1",
42
- "@sheetxl/sdk": "^0.8.0-beta.1",
43
- "@sheetxl/utils": "^0.8.0-beta.1"
39
+ "@sheetxl/io": "^0.8.0-beta.3",
40
+ "@sheetxl/primitives": "^0.8.0-beta.3",
41
+ "@sheetxl/scripting": "^0.8.0-beta.3",
42
+ "@sheetxl/sdk": "^0.8.0-beta.3",
43
+ "@sheetxl/utils": "^0.8.0-beta.3"
44
44
  },
45
45
  "peerDependencies": {
46
46
  "typescript": "^5.9.0 || ^6.0.0"
@@ -1,14 +1,12 @@
1
1
  import { Runner } from './Types';
2
- /** Core: run targets as a serial pipeline, each receiving the current workbook.
2
+ /**
3
+ * Runs the pipeline with the `SheetXL` global installed.
3
4
  *
4
- * Pipeline semantics:
5
- * - Targets run **serially** in order, never in parallel.
6
- * - Each target receives a fresh `RunContext` built with the **current** workbook.
7
- * - If a target returns an `IWorkbook`, that becomes the workbook for all subsequent targets.
8
- * - `args` are shared across all targets — they represent pipeline-level configuration,
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: {
@@ -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
- /** `script` when the source has a top-level default export; `snippet` otherwise. */
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 — subtract from a compiler
59
- * diagnostic's line to point back into the snippet. Exact for every kind: the rewrite emits the
60
- * original trivia, so no line moves.
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
@@ -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) => Promise<void | IWorkbook>;
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
- args?: ArgV;
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, target: RunTarget | RunTarget[], options?: RunOptions) => Promise<void | IWorkbook>;
148
+ export type Runner = (workbook: IWorkbook | null, args: ArgV | undefined, options: RunOptions) => Promise<IWorkbook | any | void>;
95
149
  //# sourceMappingURL=Types.d.ts.map