@lotics/cli 0.44.0 → 0.44.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app_commands.d.ts +6 -3
- package/dist/generate_app_workflows_dts.d.ts +7 -0
- package/dist/generate_app_workflows_dts.js +79 -7
- package/dist/generate_app_workflows_dts.test.d.ts +1 -0
- package/dist/generate_app_workflows_dts.test.js +47 -0
- package/dist/src/cli.js +122 -16
- package/dist/starter_template.d.ts +1 -1
- package/dist/starter_template.js +48 -1
- package/dist/starter_template.test.js +20 -0
- package/package.json +1 -1
package/dist/app_commands.d.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { LoticsClient } from "./client.js";
|
|
2
2
|
/**
|
|
3
3
|
* Manifest declaration for one workflow alias:
|
|
4
|
-
* `"alias": { workflow_id, inputs?: { key: { type, … } } }`
|
|
4
|
+
* `"alias": { workflow_id, inputs?: { key: { type, … } }, outputs?: { key: { type, … } } }`
|
|
5
5
|
*
|
|
6
6
|
* When `inputs` is declared, the CLI codegen emits a typed `AppWorkflows[alias]`
|
|
7
|
-
* entry and the server validates payloads against it at execute time.
|
|
8
|
-
* `
|
|
7
|
+
* entry and the server validates payloads against it at execute time. When
|
|
8
|
+
* `outputs` is declared, codegen emits a typed `AppWorkflowResults[alias]` so the
|
|
9
|
+
* app reads a typed `result.data`. Omit either for workflows that take no typed
|
|
10
|
+
* inputs / return no structured data.
|
|
9
11
|
*/
|
|
10
12
|
export type AppWorkflowDeclaration = {
|
|
11
13
|
workflow_id: string;
|
|
12
14
|
inputs?: Record<string, unknown>;
|
|
15
|
+
outputs?: Record<string, unknown>;
|
|
13
16
|
};
|
|
14
17
|
/**
|
|
15
18
|
* Manifest declaration for one named query:
|
|
@@ -27,4 +27,11 @@ export declare function generateAppWorkflowsDts(workflows: Record<string, AppWor
|
|
|
27
27
|
* catches structurally-invalid declarations before they reach us.
|
|
28
28
|
*/
|
|
29
29
|
export declare function inputsToType(inputs: Record<string, unknown>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Map an output `fields` map (also the top-level `outputs`) to a TS object type.
|
|
32
|
+
* Mirrors the backend `outputObjectToTsType`; recursive for nested object/array.
|
|
33
|
+
* Zero-dep like `inputsToType` — pattern-matches on `type`, falls through to
|
|
34
|
+
* `unknown` for shapes the server's schema parse rejects before we see them.
|
|
35
|
+
*/
|
|
36
|
+
export declare function objectFieldsToType(fields: Record<string, unknown>): string;
|
|
30
37
|
export declare function isValidIdentifier(name: string): boolean;
|
|
@@ -34,22 +34,32 @@ declare module "@lotics/app-sdk" {
|
|
|
34
34
|
}
|
|
35
35
|
// Sort alphabetically for deterministic output across regenerations.
|
|
36
36
|
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
37
|
-
const
|
|
37
|
+
const inputLines = [];
|
|
38
|
+
const resultLines = [];
|
|
38
39
|
for (const [alias, declaration] of entries) {
|
|
39
|
-
// declaration is
|
|
40
|
-
//
|
|
41
|
-
// `
|
|
40
|
+
// declaration is `{ workflow_id, inputs?, outputs? }` per manifest schema.
|
|
41
|
+
// No typed inputs ⇒ untyped `Record<string, unknown>` callable. An alias with
|
|
42
|
+
// declared `outputs` also gets an `AppWorkflowResults` entry → typed result.data.
|
|
42
43
|
const valueType = declaration.inputs
|
|
43
44
|
? inputsToType(declaration.inputs)
|
|
44
45
|
: "Record<string, unknown>";
|
|
45
46
|
const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
|
|
46
|
-
|
|
47
|
+
inputLines.push(` ${aliasKey}: ${valueType};`);
|
|
48
|
+
if (declaration.outputs) {
|
|
49
|
+
resultLines.push(` ${aliasKey}: ${objectFieldsToType(declaration.outputs)};`);
|
|
50
|
+
}
|
|
47
51
|
}
|
|
52
|
+
const resultsBlock = resultLines.length > 0
|
|
53
|
+
? `
|
|
54
|
+
interface AppWorkflowResults {
|
|
55
|
+
${resultLines.join("\n")}
|
|
56
|
+
}`
|
|
57
|
+
: "";
|
|
48
58
|
return `${HEADER}
|
|
49
59
|
declare module "@lotics/app-sdk" {
|
|
50
60
|
interface AppWorkflows {
|
|
51
|
-
${
|
|
52
|
-
}
|
|
61
|
+
${inputLines.join("\n")}
|
|
62
|
+
}${resultsBlock}
|
|
53
63
|
}
|
|
54
64
|
`;
|
|
55
65
|
}
|
|
@@ -119,6 +129,68 @@ function inputDeclToTsType(decl) {
|
|
|
119
129
|
return "unknown";
|
|
120
130
|
}
|
|
121
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Map an output `fields` map (also the top-level `outputs`) to a TS object type.
|
|
134
|
+
* Mirrors the backend `outputObjectToTsType`; recursive for nested object/array.
|
|
135
|
+
* Zero-dep like `inputsToType` — pattern-matches on `type`, falls through to
|
|
136
|
+
* `unknown` for shapes the server's schema parse rejects before we see them.
|
|
137
|
+
*/
|
|
138
|
+
export function objectFieldsToType(fields) {
|
|
139
|
+
const parts = [];
|
|
140
|
+
for (const [key, decl] of Object.entries(fields)) {
|
|
141
|
+
if (decl === null || typeof decl !== "object")
|
|
142
|
+
continue;
|
|
143
|
+
const d = decl;
|
|
144
|
+
const optional = d.required === false ? "?" : "";
|
|
145
|
+
const fieldKey = isValidIdentifier(key) ? key : JSON.stringify(key);
|
|
146
|
+
parts.push(`${fieldKey}${optional}: ${outputDeclToTsType(d)}`);
|
|
147
|
+
}
|
|
148
|
+
if (parts.length === 0)
|
|
149
|
+
return "Record<string, never>";
|
|
150
|
+
return `{ ${parts.join("; ")} }`;
|
|
151
|
+
}
|
|
152
|
+
function outputDeclToTsType(decl) {
|
|
153
|
+
const type = decl.type;
|
|
154
|
+
switch (type) {
|
|
155
|
+
case "text":
|
|
156
|
+
case "email":
|
|
157
|
+
case "date":
|
|
158
|
+
case "datetime":
|
|
159
|
+
return "string";
|
|
160
|
+
case "number":
|
|
161
|
+
return "number";
|
|
162
|
+
case "boolean":
|
|
163
|
+
return "boolean";
|
|
164
|
+
case "record_link":
|
|
165
|
+
return decl.multi === true ? "ReadonlyArray<string>" : "string";
|
|
166
|
+
case "select": {
|
|
167
|
+
const options = Array.isArray(decl.options) ? decl.options : [];
|
|
168
|
+
const literals = options
|
|
169
|
+
.map((o) => o !== null && typeof o === "object" && "value" in o && typeof o.value === "string"
|
|
170
|
+
? JSON.stringify(o.value)
|
|
171
|
+
: null)
|
|
172
|
+
.filter((v) => v !== null);
|
|
173
|
+
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
174
|
+
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
175
|
+
}
|
|
176
|
+
case "object": {
|
|
177
|
+
const fields = decl.fields !== null && typeof decl.fields === "object"
|
|
178
|
+
? decl.fields
|
|
179
|
+
: {};
|
|
180
|
+
return objectFieldsToType(fields);
|
|
181
|
+
}
|
|
182
|
+
case "array": {
|
|
183
|
+
const items = decl.items !== null && typeof decl.items === "object"
|
|
184
|
+
? decl.items
|
|
185
|
+
: null;
|
|
186
|
+
return items ? `ReadonlyArray<${outputDeclToTsType(items)}>` : "ReadonlyArray<unknown>";
|
|
187
|
+
}
|
|
188
|
+
case "json":
|
|
189
|
+
return "unknown";
|
|
190
|
+
default:
|
|
191
|
+
return "unknown";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
122
194
|
const IDENTIFIER_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
123
195
|
export function isValidIdentifier(name) {
|
|
124
196
|
return IDENTIFIER_REGEX.test(name);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { generateAppWorkflowsDts } from "./generate_app_workflows_dts.js";
|
|
3
|
+
describe("generateAppWorkflowsDts", () => {
|
|
4
|
+
it("emits no AppWorkflowResults block when no alias declares outputs", () => {
|
|
5
|
+
const dts = generateAppWorkflowsDts({
|
|
6
|
+
issue: { workflow_id: "wfl_1", inputs: { record_id: { type: "text" } } },
|
|
7
|
+
});
|
|
8
|
+
expect(dts).toContain("interface AppWorkflows {");
|
|
9
|
+
expect(dts).toContain("issue: {\n record_id: string;\n };");
|
|
10
|
+
expect(dts).not.toContain("AppWorkflowResults");
|
|
11
|
+
});
|
|
12
|
+
it("emits a typed AppWorkflowResults entry for an alias with a nested outputs schema", () => {
|
|
13
|
+
const dts = generateAppWorkflowsDts({
|
|
14
|
+
computeQuote: {
|
|
15
|
+
workflow_id: "wfl_2",
|
|
16
|
+
outputs: {
|
|
17
|
+
total: { type: "number" },
|
|
18
|
+
note: { type: "text", required: false },
|
|
19
|
+
lines: {
|
|
20
|
+
type: "array",
|
|
21
|
+
items: {
|
|
22
|
+
type: "object",
|
|
23
|
+
fields: { name: { type: "text" }, amount: { type: "number" } },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
expect(dts).toContain("interface AppWorkflowResults {");
|
|
30
|
+
// Nested array-of-object + optional field render correctly.
|
|
31
|
+
expect(dts).toContain("computeQuote: { total: number; note?: string; lines: ReadonlyArray<{ name: string; amount: number }> };");
|
|
32
|
+
// No declared inputs → untyped callable in AppWorkflows.
|
|
33
|
+
expect(dts).toContain("computeQuote: Record<string, unknown>;");
|
|
34
|
+
});
|
|
35
|
+
it("maps record_link/select outputs the same way as inputs", () => {
|
|
36
|
+
const dts = generateAppWorkflowsDts({
|
|
37
|
+
lookup: {
|
|
38
|
+
workflow_id: "wfl_3",
|
|
39
|
+
outputs: {
|
|
40
|
+
owner: { type: "record_link", table_id: "tbl_x" },
|
|
41
|
+
tags: { type: "select", options: [{ label: "A", value: "a" }, { label: "B", value: "b" }], multi: true },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
expect(dts).toContain('owner: string; tags: ReadonlyArray<"a" | "b">');
|
|
46
|
+
});
|
|
47
|
+
});
|
package/dist/src/cli.js
CHANGED
|
@@ -30179,7 +30179,7 @@ import { spawn as spawn2 } from "node:child_process";
|
|
|
30179
30179
|
import { tmpdir } from "node:os";
|
|
30180
30180
|
|
|
30181
30181
|
// src/starter_template.ts
|
|
30182
|
-
var STARTER_FALLBACK_UI_VERSION = "
|
|
30182
|
+
var STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
30183
30183
|
var STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
30184
30184
|
var STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
30185
30185
|
function buildStarterTemplate(args) {
|
|
@@ -30322,6 +30322,34 @@ export default defineConfig({
|
|
|
30322
30322
|
dedupe: ["react", "react-dom", "react-native-web"],
|
|
30323
30323
|
},
|
|
30324
30324
|
optimizeDeps: {
|
|
30325
|
+
// \`recharts\` (used by @lotics/ui chart_* + sparkline) imports
|
|
30326
|
+
// \`es-toolkit/compat/get\` as a default-export CJS module. Vite's dev
|
|
30327
|
+
// server treats \`compat/*\` as ESM and won't synthesize a default,
|
|
30328
|
+
// so \`import get from "es-toolkit/compat/get"\` fails to resolve.
|
|
30329
|
+
// Pre-bundling forces Vite to convert it to an ESM shim with a default
|
|
30330
|
+
// export. Production build (rollup) handles it correctly without this.
|
|
30331
|
+
//
|
|
30332
|
+
// react-native-web itself is force-prebundled (folds its core CJS deps like
|
|
30333
|
+
// @react-native/normalize-colors into one interop'd chunk), and the deep
|
|
30334
|
+
// subpaths it reaches via *default* imports (e.g. @react-native-picker's web
|
|
30335
|
+
// <select> build) are each pre-bundled into an ESM shim \u2014 Vite's dev optimizer
|
|
30336
|
+
// otherwise serves them without a synthesized default export ("does not
|
|
30337
|
+
// provide an export named 'default'"), blanking the iframe. (Production/rollup
|
|
30338
|
+
// resolves the interop already, so this is dev-only.)
|
|
30339
|
+
include: [
|
|
30340
|
+
"recharts", "es-toolkit", "es-toolkit/compat",
|
|
30341
|
+
"react-native-web", "@react-native/normalize-colors",
|
|
30342
|
+
"inline-style-prefixer/lib/createPrefixer",
|
|
30343
|
+
"inline-style-prefixer/lib/plugins/crossFade",
|
|
30344
|
+
"inline-style-prefixer/lib/plugins/imageSet",
|
|
30345
|
+
"inline-style-prefixer/lib/plugins/logical",
|
|
30346
|
+
"inline-style-prefixer/lib/plugins/position",
|
|
30347
|
+
"inline-style-prefixer/lib/plugins/sizing",
|
|
30348
|
+
"inline-style-prefixer/lib/plugins/transition",
|
|
30349
|
+
"postcss-value-parser", "fbjs/lib/invariant", "fbjs/lib/warning",
|
|
30350
|
+
"styleq", "styleq/transform-localize-style",
|
|
30351
|
+
"react", "react-dom", "react-dom/client", "nullthrows",
|
|
30352
|
+
],
|
|
30325
30353
|
// The dep optimizer pre-bundles deps with a SEPARATE esbuild pass that
|
|
30326
30354
|
// top-level \`define\` doesn't always reach, so a pre-bundled RN dep can
|
|
30327
30355
|
// still hit \`__DEV__ is not defined\` under \`lotics app dev\`. Define it
|
|
@@ -30352,6 +30380,25 @@ export default defineConfig({
|
|
|
30352
30380
|
},
|
|
30353
30381
|
test: {
|
|
30354
30382
|
environment: "jsdom",
|
|
30383
|
+
// RN packages ship Flow (\`import typeof\`) in their native source, reached
|
|
30384
|
+
// transitively by RN-Web components (pickers, calendars, anything touching
|
|
30385
|
+
// Animated). Vitest's web optimizer is OFF by default and ignores the
|
|
30386
|
+
// \`optimizeDeps.resolveExtensions\` above, so it pre-bundles the native
|
|
30387
|
+
// \`.js\` / \`src\` Flow files and esbuild can't parse them. Enable it with the
|
|
30388
|
+
// same \`.web.js\`-first resolution so it bundles the compiled web variants
|
|
30389
|
+
// (Picker.web.js, RN-Web dist) \u2014 without this, any test that renders a tree
|
|
30390
|
+
// pulling a Picker / DatePicker fails with "Unexpected token 'typeof'".
|
|
30391
|
+
deps: {
|
|
30392
|
+
optimizer: {
|
|
30393
|
+
web: {
|
|
30394
|
+
enabled: true,
|
|
30395
|
+
include: ["react-native", "react-native-web", "@react-native-picker/picker"],
|
|
30396
|
+
esbuildOptions: {
|
|
30397
|
+
resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js", ".json"],
|
|
30398
|
+
},
|
|
30399
|
+
},
|
|
30400
|
+
},
|
|
30401
|
+
},
|
|
30355
30402
|
},
|
|
30356
30403
|
});
|
|
30357
30404
|
`
|
|
@@ -31078,17 +31125,25 @@ declare module "@lotics/app-sdk" {
|
|
|
31078
31125
|
`;
|
|
31079
31126
|
}
|
|
31080
31127
|
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
31081
|
-
const
|
|
31128
|
+
const inputLines = [];
|
|
31129
|
+
const resultLines = [];
|
|
31082
31130
|
for (const [alias, declaration] of entries) {
|
|
31083
31131
|
const valueType = declaration.inputs ? inputsToType(declaration.inputs) : "Record<string, unknown>";
|
|
31084
31132
|
const aliasKey = isValidIdentifier(alias) ? alias : JSON.stringify(alias);
|
|
31085
|
-
|
|
31133
|
+
inputLines.push(` ${aliasKey}: ${valueType};`);
|
|
31134
|
+
if (declaration.outputs) {
|
|
31135
|
+
resultLines.push(` ${aliasKey}: ${objectFieldsToType(declaration.outputs)};`);
|
|
31136
|
+
}
|
|
31086
31137
|
}
|
|
31138
|
+
const resultsBlock = resultLines.length > 0 ? `
|
|
31139
|
+
interface AppWorkflowResults {
|
|
31140
|
+
${resultLines.join("\n")}
|
|
31141
|
+
}` : "";
|
|
31087
31142
|
return `${HEADER}
|
|
31088
31143
|
declare module "@lotics/app-sdk" {
|
|
31089
31144
|
interface AppWorkflows {
|
|
31090
|
-
${
|
|
31091
|
-
}
|
|
31145
|
+
${inputLines.join("\n")}
|
|
31146
|
+
}${resultsBlock}
|
|
31092
31147
|
}
|
|
31093
31148
|
`;
|
|
31094
31149
|
}
|
|
@@ -31145,6 +31200,54 @@ function inputDeclToTsType(decl) {
|
|
|
31145
31200
|
return "unknown";
|
|
31146
31201
|
}
|
|
31147
31202
|
}
|
|
31203
|
+
function objectFieldsToType(fields) {
|
|
31204
|
+
const parts = [];
|
|
31205
|
+
for (const [key, decl] of Object.entries(fields)) {
|
|
31206
|
+
if (decl === null || typeof decl !== "object") continue;
|
|
31207
|
+
const d = decl;
|
|
31208
|
+
const optional = d.required === false ? "?" : "";
|
|
31209
|
+
const fieldKey = isValidIdentifier(key) ? key : JSON.stringify(key);
|
|
31210
|
+
parts.push(`${fieldKey}${optional}: ${outputDeclToTsType(d)}`);
|
|
31211
|
+
}
|
|
31212
|
+
if (parts.length === 0) return "Record<string, never>";
|
|
31213
|
+
return `{ ${parts.join("; ")} }`;
|
|
31214
|
+
}
|
|
31215
|
+
function outputDeclToTsType(decl) {
|
|
31216
|
+
const type = decl.type;
|
|
31217
|
+
switch (type) {
|
|
31218
|
+
case "text":
|
|
31219
|
+
case "email":
|
|
31220
|
+
case "date":
|
|
31221
|
+
case "datetime":
|
|
31222
|
+
return "string";
|
|
31223
|
+
case "number":
|
|
31224
|
+
return "number";
|
|
31225
|
+
case "boolean":
|
|
31226
|
+
return "boolean";
|
|
31227
|
+
case "record_link":
|
|
31228
|
+
return decl.multi === true ? "ReadonlyArray<string>" : "string";
|
|
31229
|
+
case "select": {
|
|
31230
|
+
const options = Array.isArray(decl.options) ? decl.options : [];
|
|
31231
|
+
const literals = options.map(
|
|
31232
|
+
(o) => o !== null && typeof o === "object" && "value" in o && typeof o.value === "string" ? JSON.stringify(o.value) : null
|
|
31233
|
+
).filter((v) => v !== null);
|
|
31234
|
+
const inner = literals.length > 0 ? literals.join(" | ") : "string";
|
|
31235
|
+
return decl.multi === true ? `ReadonlyArray<${inner}>` : inner;
|
|
31236
|
+
}
|
|
31237
|
+
case "object": {
|
|
31238
|
+
const fields = decl.fields !== null && typeof decl.fields === "object" ? decl.fields : {};
|
|
31239
|
+
return objectFieldsToType(fields);
|
|
31240
|
+
}
|
|
31241
|
+
case "array": {
|
|
31242
|
+
const items = decl.items !== null && typeof decl.items === "object" ? decl.items : null;
|
|
31243
|
+
return items ? `ReadonlyArray<${outputDeclToTsType(items)}>` : "ReadonlyArray<unknown>";
|
|
31244
|
+
}
|
|
31245
|
+
case "json":
|
|
31246
|
+
return "unknown";
|
|
31247
|
+
default:
|
|
31248
|
+
return "unknown";
|
|
31249
|
+
}
|
|
31250
|
+
}
|
|
31148
31251
|
var IDENTIFIER_REGEX = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
31149
31252
|
function isValidIdentifier(name) {
|
|
31150
31253
|
return IDENTIFIER_REGEX.test(name);
|
|
@@ -39145,7 +39248,7 @@ var drawingParser = new XMLParser({
|
|
|
39145
39248
|
processEntities: false,
|
|
39146
39249
|
isArray: (tagName) => tagName === "xdr:twoCellAnchor" || tagName === "xdr:oneCellAnchor" || tagName === "Relationship"
|
|
39147
39250
|
});
|
|
39148
|
-
function parseSheet(sheetXml, sheetRelsXml, styles, sharedStrings, theme, indexedColors, zipEntries, date1904) {
|
|
39251
|
+
function parseSheet(sheetXml, sheetRelsXml, styles, sharedStrings, theme, indexedColors, zipEntries, date1904, maxRows = MAX_ROWS_PER_SHEET) {
|
|
39149
39252
|
const doc = xmlParser4.parse(sheetXml);
|
|
39150
39253
|
const worksheet = doc?.["worksheet"];
|
|
39151
39254
|
if (!worksheet) return emptySheet("Sheet");
|
|
@@ -39163,7 +39266,7 @@ function parseSheet(sheetXml, sheetRelsXml, styles, sharedStrings, theme, indexe
|
|
|
39163
39266
|
maxCol,
|
|
39164
39267
|
maxContentWidth,
|
|
39165
39268
|
rowOutlineLevels
|
|
39166
|
-
} = parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultRowHeight, date1904);
|
|
39269
|
+
} = parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultRowHeight, date1904, maxRows);
|
|
39167
39270
|
const colCount = Math.max(parsedCols.length > 0 ? parsedCols[parsedCols.length - 1].max : 0, maxCol);
|
|
39168
39271
|
const columns = buildColumns(parsedCols, colHidden, maxContentWidth, colCount, defaultColWidth);
|
|
39169
39272
|
const mergedCells = parseMergedCells(worksheet);
|
|
@@ -39345,7 +39448,7 @@ function buildColumns(colDefs, colHidden, maxContentWidth, colCount, _defaultCol
|
|
|
39345
39448
|
}
|
|
39346
39449
|
return columns;
|
|
39347
39450
|
}
|
|
39348
|
-
function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultRowHeight, date1904) {
|
|
39451
|
+
function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultRowHeight, date1904, maxRows) {
|
|
39349
39452
|
const sheetData = worksheet["sheetData"];
|
|
39350
39453
|
if (!sheetData) {
|
|
39351
39454
|
return {
|
|
@@ -39383,7 +39486,7 @@ function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultR
|
|
|
39383
39486
|
}
|
|
39384
39487
|
if (rowEl["@_hidden"] === "1" || rowEl["@_hidden"] === "true") continue;
|
|
39385
39488
|
rowCount++;
|
|
39386
|
-
if (rowCount >
|
|
39489
|
+
if (rowCount > maxRows) continue;
|
|
39387
39490
|
const height = rowEl["@_ht"] ? parseFloat(rowEl["@_ht"]) : defaultRowHeight;
|
|
39388
39491
|
const cellArr = rowEl["c"];
|
|
39389
39492
|
const cells = [];
|
|
@@ -39412,7 +39515,7 @@ function parseSheetData(worksheet, styles, sharedStrings, hyperlinkMap, defaultR
|
|
|
39412
39515
|
return {
|
|
39413
39516
|
rows,
|
|
39414
39517
|
totalRowCount,
|
|
39415
|
-
truncated: rowCount >
|
|
39518
|
+
truncated: rowCount > maxRows,
|
|
39416
39519
|
maxCol,
|
|
39417
39520
|
maxContentWidth,
|
|
39418
39521
|
rowOutlineLevels
|
|
@@ -39454,10 +39557,12 @@ function parseCell(cellEl, styles, sharedStrings, hyperlinkMap, date1904) {
|
|
|
39454
39557
|
if (fEl !== void 0 && fEl !== null) {
|
|
39455
39558
|
if (typeof fEl === "object") {
|
|
39456
39559
|
const fObj = fEl;
|
|
39457
|
-
|
|
39560
|
+
const text = fObj["#text"];
|
|
39561
|
+
formula = text == null ? void 0 : String(text);
|
|
39458
39562
|
if (fObj["@_t"] === "array") {
|
|
39459
39563
|
isArrayFormula = true;
|
|
39460
|
-
|
|
39564
|
+
const ref2 = fObj["@_ref"];
|
|
39565
|
+
arrayRange = ref2 == null ? void 0 : String(ref2);
|
|
39461
39566
|
if (formula?.startsWith("{") && formula.endsWith("}")) {
|
|
39462
39567
|
formula = formula.slice(1, -1);
|
|
39463
39568
|
}
|
|
@@ -41026,11 +41131,11 @@ function ensureVmlArray(val) {
|
|
|
41026
41131
|
}
|
|
41027
41132
|
|
|
41028
41133
|
// ../xlsx/src/excel_parser.ts
|
|
41029
|
-
function parseExcelBuffer(arrayBuffer) {
|
|
41134
|
+
function parseExcelBuffer(arrayBuffer, options) {
|
|
41030
41135
|
const zip = unzipXlsx(arrayBuffer);
|
|
41031
|
-
return parseExcelFromZip(zip);
|
|
41136
|
+
return parseExcelFromZip(zip, options);
|
|
41032
41137
|
}
|
|
41033
|
-
function parseExcelFromZip(zip) {
|
|
41138
|
+
function parseExcelFromZip(zip, options) {
|
|
41034
41139
|
const themeEntry = findEntry(zip, "xl/theme/theme1.xml");
|
|
41035
41140
|
const theme = themeEntry ? parseTheme(decodeUtf8(themeEntry)) : { colors: defaultThemeColors(), majorFont: "Calibri", minorFont: "Calibri" };
|
|
41036
41141
|
const stylesEntry = findEntry(zip, "xl/styles.xml");
|
|
@@ -41092,7 +41197,8 @@ function parseExcelFromZip(zip) {
|
|
|
41092
41197
|
theme,
|
|
41093
41198
|
styles.indexedColors,
|
|
41094
41199
|
zip,
|
|
41095
|
-
workbookInfo.date1904
|
|
41200
|
+
workbookInfo.date1904,
|
|
41201
|
+
options?.maxRowsPerSheet
|
|
41096
41202
|
);
|
|
41097
41203
|
parsed.name = sheetInfo.name;
|
|
41098
41204
|
if (isHidden) parsed.hidden = true;
|
|
@@ -39,7 +39,7 @@ export interface StarterFile {
|
|
|
39
39
|
* scaffolds resolve the live version via `fetchLatestNpmVersion` and only
|
|
40
40
|
* fall back here when the lookup fails.
|
|
41
41
|
*/
|
|
42
|
-
export declare const STARTER_FALLBACK_UI_VERSION = "
|
|
42
|
+
export declare const STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
43
43
|
export declare const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
44
44
|
/**
|
|
45
45
|
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|
package/dist/starter_template.js
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* scaffolds resolve the live version via `fetchLatestNpmVersion` and only
|
|
36
36
|
* fall back here when the lookup fails.
|
|
37
37
|
*/
|
|
38
|
-
export const STARTER_FALLBACK_UI_VERSION = "
|
|
38
|
+
export const STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
39
39
|
export const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
40
40
|
/**
|
|
41
41
|
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|
|
@@ -184,6 +184,34 @@ export default defineConfig({
|
|
|
184
184
|
dedupe: ["react", "react-dom", "react-native-web"],
|
|
185
185
|
},
|
|
186
186
|
optimizeDeps: {
|
|
187
|
+
// \`recharts\` (used by @lotics/ui chart_* + sparkline) imports
|
|
188
|
+
// \`es-toolkit/compat/get\` as a default-export CJS module. Vite's dev
|
|
189
|
+
// server treats \`compat/*\` as ESM and won't synthesize a default,
|
|
190
|
+
// so \`import get from "es-toolkit/compat/get"\` fails to resolve.
|
|
191
|
+
// Pre-bundling forces Vite to convert it to an ESM shim with a default
|
|
192
|
+
// export. Production build (rollup) handles it correctly without this.
|
|
193
|
+
//
|
|
194
|
+
// react-native-web itself is force-prebundled (folds its core CJS deps like
|
|
195
|
+
// @react-native/normalize-colors into one interop'd chunk), and the deep
|
|
196
|
+
// subpaths it reaches via *default* imports (e.g. @react-native-picker's web
|
|
197
|
+
// <select> build) are each pre-bundled into an ESM shim — Vite's dev optimizer
|
|
198
|
+
// otherwise serves them without a synthesized default export ("does not
|
|
199
|
+
// provide an export named 'default'"), blanking the iframe. (Production/rollup
|
|
200
|
+
// resolves the interop already, so this is dev-only.)
|
|
201
|
+
include: [
|
|
202
|
+
"recharts", "es-toolkit", "es-toolkit/compat",
|
|
203
|
+
"react-native-web", "@react-native/normalize-colors",
|
|
204
|
+
"inline-style-prefixer/lib/createPrefixer",
|
|
205
|
+
"inline-style-prefixer/lib/plugins/crossFade",
|
|
206
|
+
"inline-style-prefixer/lib/plugins/imageSet",
|
|
207
|
+
"inline-style-prefixer/lib/plugins/logical",
|
|
208
|
+
"inline-style-prefixer/lib/plugins/position",
|
|
209
|
+
"inline-style-prefixer/lib/plugins/sizing",
|
|
210
|
+
"inline-style-prefixer/lib/plugins/transition",
|
|
211
|
+
"postcss-value-parser", "fbjs/lib/invariant", "fbjs/lib/warning",
|
|
212
|
+
"styleq", "styleq/transform-localize-style",
|
|
213
|
+
"react", "react-dom", "react-dom/client", "nullthrows",
|
|
214
|
+
],
|
|
187
215
|
// The dep optimizer pre-bundles deps with a SEPARATE esbuild pass that
|
|
188
216
|
// top-level \`define\` doesn't always reach, so a pre-bundled RN dep can
|
|
189
217
|
// still hit \`__DEV__ is not defined\` under \`lotics app dev\`. Define it
|
|
@@ -214,6 +242,25 @@ export default defineConfig({
|
|
|
214
242
|
},
|
|
215
243
|
test: {
|
|
216
244
|
environment: "jsdom",
|
|
245
|
+
// RN packages ship Flow (\`import typeof\`) in their native source, reached
|
|
246
|
+
// transitively by RN-Web components (pickers, calendars, anything touching
|
|
247
|
+
// Animated). Vitest's web optimizer is OFF by default and ignores the
|
|
248
|
+
// \`optimizeDeps.resolveExtensions\` above, so it pre-bundles the native
|
|
249
|
+
// \`.js\` / \`src\` Flow files and esbuild can't parse them. Enable it with the
|
|
250
|
+
// same \`.web.js\`-first resolution so it bundles the compiled web variants
|
|
251
|
+
// (Picker.web.js, RN-Web dist) — without this, any test that renders a tree
|
|
252
|
+
// pulling a Picker / DatePicker fails with "Unexpected token 'typeof'".
|
|
253
|
+
deps: {
|
|
254
|
+
optimizer: {
|
|
255
|
+
web: {
|
|
256
|
+
enabled: true,
|
|
257
|
+
include: ["react-native", "react-native-web", "@react-native-picker/picker"],
|
|
258
|
+
esbuildOptions: {
|
|
259
|
+
resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js", ".json"],
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
217
264
|
},
|
|
218
265
|
});
|
|
219
266
|
`,
|
|
@@ -18,6 +18,14 @@ describe("buildStarterTemplate", () => {
|
|
|
18
18
|
expect(config).toContain('__DEV__: "false"');
|
|
19
19
|
expect(config).toContain('".web.js"');
|
|
20
20
|
});
|
|
21
|
+
test("vite.config.ts force-prebundles react-native-web (else a DatePicker/Picker blanks the dev iframe)", () => {
|
|
22
|
+
// RN-Web does `import normalizeColor from "@react-native/normalize-colors"`
|
|
23
|
+
// (nested CJS). Un-prebundled, the dev server serves it as ESM with no
|
|
24
|
+
// default → "does not provide an export named 'default'" + blank iframe the
|
|
25
|
+
// moment a colour-touching component mounts. Prebundling interops it.
|
|
26
|
+
const config = fileNamed(buildStarterTemplate(baseArgs), "vite.config.ts");
|
|
27
|
+
expect(config).toMatch(/include:\s*\[[^\]]*"react-native-web"/);
|
|
28
|
+
});
|
|
21
29
|
test("vite.config.ts defines the RN __DEV__ global (else the bundle throws at load → blank iframe)", () => {
|
|
22
30
|
// `__DEV__` is a Metro-injected global that RN-ecosystem deps reference at
|
|
23
31
|
// module-eval time (e.g. @react-native-picker's UnimplementedView, pulled in
|
|
@@ -31,6 +39,18 @@ describe("buildStarterTemplate", () => {
|
|
|
31
39
|
// deps under `lotics app dev`).
|
|
32
40
|
expect(config).toMatch(/esbuildOptions:\s*\{\s*define:\s*\{\s*__DEV__:\s*"false"/);
|
|
33
41
|
});
|
|
42
|
+
test("vite.config.ts enables the vitest web optimizer with .web.js resolution (else tests rendering a Picker/DatePicker fail on Flow)", () => {
|
|
43
|
+
// Vitest pre-bundles deps with its OWN esbuild optimizer, which is OFF by
|
|
44
|
+
// default and ignores the top-level `optimizeDeps.resolveExtensions`. Without
|
|
45
|
+
// it enabled + given `.web.js` first, it grabs react-native's native `.js` /
|
|
46
|
+
// src Flow files (`import typeof`) that esbuild can't parse — so any test that
|
|
47
|
+
// renders a tree pulling a Picker/DatePicker dies with "Unexpected token 'typeof'".
|
|
48
|
+
const config = fileNamed(buildStarterTemplate(baseArgs), "vite.config.ts");
|
|
49
|
+
expect(config).toMatch(/test:\s*\{[\s\S]*environment:\s*"jsdom"/);
|
|
50
|
+
expect(config).toMatch(/optimizer:\s*\{\s*web:\s*\{\s*enabled:\s*true/);
|
|
51
|
+
// The web optimizer carries the same `.web.js`-first resolution as the build.
|
|
52
|
+
expect(config).toMatch(/web:\s*\{[\s\S]*resolveExtensions:[\s\S]*"\.web\.js"/);
|
|
53
|
+
});
|
|
34
54
|
test("vite.config.ts maps the RN `global` to globalThis (else rn-web Animated throws when a spring is torn down)", () => {
|
|
35
55
|
// react-native-web's Animated reads `global.cancelAnimationFrame` (free var)
|
|
36
56
|
// when a spring animation is interrupted — e.g. toggling @lotics/ui's Switch.
|