@lotics/cli 0.36.0 → 0.36.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/dev/wrapper_page.js +16 -0
- package/dist/src/cli.js +40 -2
- package/dist/starter_template.d.ts +10 -0
- package/dist/starter_template.js +33 -2
- package/dist/starter_template.test.js +18 -1
- package/package.json +2 -2
package/dist/dev/wrapper_page.js
CHANGED
|
@@ -145,6 +145,20 @@ export function buildWrapperPage(args) {
|
|
|
145
145
|
return completed.file;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// openExternal — the wrapper page is the top frame (un-sandboxed), so it
|
|
149
|
+
// opens the link directly; NOT forwarded to the RPC endpoint (no server
|
|
150
|
+
// op). Re-validate the scheme; never trust the iframe's payload.
|
|
151
|
+
function handleOpenExternal(payload) {
|
|
152
|
+
var url = payload && payload.url;
|
|
153
|
+
if (typeof url !== "string") throw new Error("openExternal requires a url string");
|
|
154
|
+
var parsed = new URL(url);
|
|
155
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
156
|
+
throw new Error('openExternal: unsupported URL scheme "' + parsed.protocol + '"');
|
|
157
|
+
}
|
|
158
|
+
window.open(parsed.href, "_blank", "noopener,noreferrer");
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
148
162
|
window.addEventListener("message", async function (event) {
|
|
149
163
|
if (event.source !== iframe.contentWindow || event.origin !== VITE_ORIGIN) return;
|
|
150
164
|
const msg = event.data;
|
|
@@ -153,6 +167,8 @@ export function buildWrapperPage(args) {
|
|
|
153
167
|
try {
|
|
154
168
|
const data = msg.op === "upload"
|
|
155
169
|
? await handleUpload(msg.payload)
|
|
170
|
+
: msg.op === "openExternal"
|
|
171
|
+
? handleOpenExternal(msg.payload)
|
|
156
172
|
: await rpc(msg.op, msg.payload);
|
|
157
173
|
const ms = Math.round(performance.now() - startedAt);
|
|
158
174
|
console.debug("[lotics-dev] " + msg.op + " " + ms + "ms", data);
|
package/dist/src/cli.js
CHANGED
|
@@ -32257,6 +32257,7 @@ import { tmpdir } from "node:os";
|
|
|
32257
32257
|
// src/starter_template.ts
|
|
32258
32258
|
var STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
32259
32259
|
var STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
32260
|
+
var STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
32260
32261
|
function buildStarterTemplate(args) {
|
|
32261
32262
|
const uiVersion = args.ui_version ?? `^${STARTER_FALLBACK_UI_VERSION}`;
|
|
32262
32263
|
const sdkVersion = args.sdk_version ?? `^${STARTER_FALLBACK_SDK_VERSION}`;
|
|
@@ -32288,7 +32289,7 @@ function buildStarterTemplate(args) {
|
|
|
32288
32289
|
"lucide-react-native": "^0.562.0",
|
|
32289
32290
|
react: "^19.0.0",
|
|
32290
32291
|
"react-dom": "^19.0.0",
|
|
32291
|
-
"react-native":
|
|
32292
|
+
"react-native": STARTER_REACT_NATIVE_VERSION,
|
|
32292
32293
|
"react-native-svg": "^15.0.0",
|
|
32293
32294
|
"react-native-web": "^0.21.0"
|
|
32294
32295
|
},
|
|
@@ -32366,6 +32367,11 @@ import react from "@vitejs/plugin-react";
|
|
|
32366
32367
|
// is identical to RN-SVG when running under RN-Web.
|
|
32367
32368
|
export default defineConfig({
|
|
32368
32369
|
plugins: [react()],
|
|
32370
|
+
// RN libraries (react-native-web, @react-native-picker's UnimplementedView)
|
|
32371
|
+
// reference the \`__DEV__\` global that Metro injects but Vite does not \u2014
|
|
32372
|
+
// undefined \u21D2 the bundle throws at load and the iframe renders blank. Define
|
|
32373
|
+
// it (false = production RN behaviour) so dev and the deployed build both boot.
|
|
32374
|
+
define: { __DEV__: "false" },
|
|
32369
32375
|
resolve: {
|
|
32370
32376
|
alias: [
|
|
32371
32377
|
{
|
|
@@ -32374,7 +32380,12 @@ export default defineConfig({
|
|
|
32374
32380
|
},
|
|
32375
32381
|
{ find: "react-native", replacement: "react-native-web" },
|
|
32376
32382
|
],
|
|
32377
|
-
|
|
32383
|
+
// \`.web.js\` resolves the web build of RN packages that ship \`X.js\` (native)
|
|
32384
|
+
// beside \`X.web.js\` (web) \u2014 e.g. @react-native-picker/picker, whose compiled
|
|
32385
|
+
// \`Picker.web.js\` renders a real <select>. Without it the extensionless
|
|
32386
|
+
// \`require("./Picker")\` picks native \`Picker.js\` and the standard Picker
|
|
32387
|
+
// renders nothing on web.
|
|
32388
|
+
extensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js"],
|
|
32378
32389
|
// @lotics/ui ships source and is consumed across many subpath entries
|
|
32379
32390
|
// (./card, ./metric, ./use_screen_size, \u2026). Without dedupe, Vite can
|
|
32380
32391
|
// pre-bundle a subpath into its own chunk with a second React copy \u2014 a
|
|
@@ -32390,6 +32401,17 @@ export default defineConfig({
|
|
|
32390
32401
|
// Pre-bundling forces Vite to convert it to an ESM shim with a default
|
|
32391
32402
|
// export. Production build (rollup) handles it correctly without this.
|
|
32392
32403
|
include: ["recharts", "es-toolkit", "es-toolkit/compat"],
|
|
32404
|
+
// The dep optimizer pre-bundles deps with a SEPARATE esbuild pass that
|
|
32405
|
+
// top-level \`define\` doesn't always reach, so a pre-bundled RN dep can
|
|
32406
|
+
// still hit \`__DEV__ is not defined\` under \`lotics app dev\`. Define it
|
|
32407
|
+
// here too \u2014 belt-and-suspenders with the top-level \`define\` above.
|
|
32408
|
+
// The optimizer pre-bundles with a SEPARATE esbuild resolver that ignores
|
|
32409
|
+
// \`resolve.extensions\`, so \`.web.js\` must be repeated here or a pre-bundled
|
|
32410
|
+
// RN dep still resolves \`./Picker\` to the native build.
|
|
32411
|
+
esbuildOptions: {
|
|
32412
|
+
define: { __DEV__: "false" },
|
|
32413
|
+
resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js", ".json"],
|
|
32414
|
+
},
|
|
32393
32415
|
},
|
|
32394
32416
|
build: {
|
|
32395
32417
|
outDir: "dist",
|
|
@@ -32880,6 +32902,20 @@ function buildWrapperPage(args) {
|
|
|
32880
32902
|
return completed.file;
|
|
32881
32903
|
}
|
|
32882
32904
|
|
|
32905
|
+
// openExternal \u2014 the wrapper page is the top frame (un-sandboxed), so it
|
|
32906
|
+
// opens the link directly; NOT forwarded to the RPC endpoint (no server
|
|
32907
|
+
// op). Re-validate the scheme; never trust the iframe's payload.
|
|
32908
|
+
function handleOpenExternal(payload) {
|
|
32909
|
+
var url = payload && payload.url;
|
|
32910
|
+
if (typeof url !== "string") throw new Error("openExternal requires a url string");
|
|
32911
|
+
var parsed = new URL(url);
|
|
32912
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
32913
|
+
throw new Error('openExternal: unsupported URL scheme "' + parsed.protocol + '"');
|
|
32914
|
+
}
|
|
32915
|
+
window.open(parsed.href, "_blank", "noopener,noreferrer");
|
|
32916
|
+
return undefined;
|
|
32917
|
+
}
|
|
32918
|
+
|
|
32883
32919
|
window.addEventListener("message", async function (event) {
|
|
32884
32920
|
if (event.source !== iframe.contentWindow || event.origin !== VITE_ORIGIN) return;
|
|
32885
32921
|
const msg = event.data;
|
|
@@ -32888,6 +32924,8 @@ function buildWrapperPage(args) {
|
|
|
32888
32924
|
try {
|
|
32889
32925
|
const data = msg.op === "upload"
|
|
32890
32926
|
? await handleUpload(msg.payload)
|
|
32927
|
+
: msg.op === "openExternal"
|
|
32928
|
+
? handleOpenExternal(msg.payload)
|
|
32891
32929
|
: await rpc(msg.op, msg.payload);
|
|
32892
32930
|
const ms = Math.round(performance.now() - startedAt);
|
|
32893
32931
|
console.debug("[lotics-dev] " + msg.op + " " + ms + "ms", data);
|
|
@@ -41,6 +41,16 @@ export interface StarterFile {
|
|
|
41
41
|
*/
|
|
42
42
|
export declare const STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
43
43
|
export declare const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
44
|
+
/**
|
|
45
|
+
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|
|
46
|
+
* an app deep-typechecks `@lotics/ui`'s `.tsx` source against the SAME RN types
|
|
47
|
+
* the package is developed and CI'd against. A mismatch surfaces RN-version-
|
|
48
|
+
* sensitive type errors (e.g. `StyleSheet.absoluteFill`, which `frontend/` CI
|
|
49
|
+
* never sees because `packages/ui` is only typechecked through `frontend/`).
|
|
50
|
+
* RN is aliased to react-native-web at runtime, so this pin is effectively
|
|
51
|
+
* types-only.
|
|
52
|
+
*/
|
|
53
|
+
export declare const STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
44
54
|
export declare function buildStarterTemplate(args: {
|
|
45
55
|
app_name: string;
|
|
46
56
|
app_id: string;
|
package/dist/starter_template.js
CHANGED
|
@@ -37,6 +37,16 @@
|
|
|
37
37
|
*/
|
|
38
38
|
export const STARTER_FALLBACK_UI_VERSION = "1.8.0";
|
|
39
39
|
export const STARTER_FALLBACK_SDK_VERSION = "0.11.0";
|
|
40
|
+
/**
|
|
41
|
+
* react-native pin for scaffolded apps. Matches the monorepo frontend's pin so
|
|
42
|
+
* an app deep-typechecks `@lotics/ui`'s `.tsx` source against the SAME RN types
|
|
43
|
+
* the package is developed and CI'd against. A mismatch surfaces RN-version-
|
|
44
|
+
* sensitive type errors (e.g. `StyleSheet.absoluteFill`, which `frontend/` CI
|
|
45
|
+
* never sees because `packages/ui` is only typechecked through `frontend/`).
|
|
46
|
+
* RN is aliased to react-native-web at runtime, so this pin is effectively
|
|
47
|
+
* types-only.
|
|
48
|
+
*/
|
|
49
|
+
export const STARTER_REACT_NATIVE_VERSION = "0.85.3";
|
|
40
50
|
export function buildStarterTemplate(args) {
|
|
41
51
|
const uiVersion = args.ui_version ?? `^${STARTER_FALLBACK_UI_VERSION}`;
|
|
42
52
|
const sdkVersion = args.sdk_version ?? `^${STARTER_FALLBACK_SDK_VERSION}`;
|
|
@@ -72,7 +82,7 @@ export function buildStarterTemplate(args) {
|
|
|
72
82
|
"lucide-react-native": "^0.562.0",
|
|
73
83
|
react: "^19.0.0",
|
|
74
84
|
"react-dom": "^19.0.0",
|
|
75
|
-
"react-native":
|
|
85
|
+
"react-native": STARTER_REACT_NATIVE_VERSION,
|
|
76
86
|
"react-native-svg": "^15.0.0",
|
|
77
87
|
"react-native-web": "^0.21.0",
|
|
78
88
|
},
|
|
@@ -143,6 +153,11 @@ import react from "@vitejs/plugin-react";
|
|
|
143
153
|
// is identical to RN-SVG when running under RN-Web.
|
|
144
154
|
export default defineConfig({
|
|
145
155
|
plugins: [react()],
|
|
156
|
+
// RN libraries (react-native-web, @react-native-picker's UnimplementedView)
|
|
157
|
+
// reference the \`__DEV__\` global that Metro injects but Vite does not —
|
|
158
|
+
// undefined ⇒ the bundle throws at load and the iframe renders blank. Define
|
|
159
|
+
// it (false = production RN behaviour) so dev and the deployed build both boot.
|
|
160
|
+
define: { __DEV__: "false" },
|
|
146
161
|
resolve: {
|
|
147
162
|
alias: [
|
|
148
163
|
{
|
|
@@ -151,7 +166,12 @@ export default defineConfig({
|
|
|
151
166
|
},
|
|
152
167
|
{ find: "react-native", replacement: "react-native-web" },
|
|
153
168
|
],
|
|
154
|
-
|
|
169
|
+
// \`.web.js\` resolves the web build of RN packages that ship \`X.js\` (native)
|
|
170
|
+
// beside \`X.web.js\` (web) — e.g. @react-native-picker/picker, whose compiled
|
|
171
|
+
// \`Picker.web.js\` renders a real <select>. Without it the extensionless
|
|
172
|
+
// \`require("./Picker")\` picks native \`Picker.js\` and the standard Picker
|
|
173
|
+
// renders nothing on web.
|
|
174
|
+
extensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js"],
|
|
155
175
|
// @lotics/ui ships source and is consumed across many subpath entries
|
|
156
176
|
// (./card, ./metric, ./use_screen_size, …). Without dedupe, Vite can
|
|
157
177
|
// pre-bundle a subpath into its own chunk with a second React copy — a
|
|
@@ -167,6 +187,17 @@ export default defineConfig({
|
|
|
167
187
|
// Pre-bundling forces Vite to convert it to an ESM shim with a default
|
|
168
188
|
// export. Production build (rollup) handles it correctly without this.
|
|
169
189
|
include: ["recharts", "es-toolkit", "es-toolkit/compat"],
|
|
190
|
+
// The dep optimizer pre-bundles deps with a SEPARATE esbuild pass that
|
|
191
|
+
// top-level \`define\` doesn't always reach, so a pre-bundled RN dep can
|
|
192
|
+
// still hit \`__DEV__ is not defined\` under \`lotics app dev\`. Define it
|
|
193
|
+
// here too — belt-and-suspenders with the top-level \`define\` above.
|
|
194
|
+
// The optimizer pre-bundles with a SEPARATE esbuild resolver that ignores
|
|
195
|
+
// \`resolve.extensions\`, so \`.web.js\` must be repeated here or a pre-bundled
|
|
196
|
+
// RN dep still resolves \`./Picker\` to the native build.
|
|
197
|
+
esbuildOptions: {
|
|
198
|
+
define: { __DEV__: "false" },
|
|
199
|
+
resolveExtensions: [".web.tsx", ".web.ts", ".web.js", ".tsx", ".ts", ".jsx", ".js", ".json"],
|
|
200
|
+
},
|
|
170
201
|
},
|
|
171
202
|
build: {
|
|
172
203
|
outDir: "dist",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
|
-
import { buildStarterTemplate, STARTER_FALLBACK_SDK_VERSION, STARTER_FALLBACK_UI_VERSION, } from "./starter_template.js";
|
|
2
|
+
import { buildStarterTemplate, STARTER_FALLBACK_SDK_VERSION, STARTER_FALLBACK_UI_VERSION, STARTER_REACT_NATIVE_VERSION, } from "./starter_template.js";
|
|
3
3
|
function fileNamed(files, path) {
|
|
4
4
|
const f = files.find((f) => f.path === path);
|
|
5
5
|
if (!f)
|
|
@@ -17,6 +17,23 @@ describe("buildStarterTemplate", () => {
|
|
|
17
17
|
expect(config).toContain('"es-toolkit"');
|
|
18
18
|
expect(config).toContain('"es-toolkit/compat"');
|
|
19
19
|
});
|
|
20
|
+
test("vite.config.ts defines the RN __DEV__ global (else the bundle throws at load → blank iframe)", () => {
|
|
21
|
+
// `__DEV__` is a Metro-injected global that RN-ecosystem deps reference at
|
|
22
|
+
// module-eval time (e.g. @react-native-picker's UnimplementedView, pulled in
|
|
23
|
+
// via @lotics/ui's DatePicker). Vite doesn't define it; undefined ⇒ a runtime
|
|
24
|
+
// ReferenceError that no typecheck/lint/build catches. Pin it so it can't
|
|
25
|
+
// silently regress and re-blank every app that imports such a dep.
|
|
26
|
+
const config = fileNamed(buildStarterTemplate(baseArgs), "vite.config.ts");
|
|
27
|
+
// Top-level define (covers the prod build + most dev modules)...
|
|
28
|
+
expect(config).toMatch(/define:\s*\{\s*__DEV__:\s*"false"/);
|
|
29
|
+
// ...and the dep-optimizer's separate esbuild pass (covers pre-bundled
|
|
30
|
+
// deps under `lotics app dev`).
|
|
31
|
+
expect(config).toMatch(/esbuildOptions:\s*\{\s*define:\s*\{\s*__DEV__:\s*"false"/);
|
|
32
|
+
});
|
|
33
|
+
test("package.json pins react-native to the monorepo version (so apps typecheck @lotics/ui against the same RN types)", () => {
|
|
34
|
+
const pkg = JSON.parse(fileNamed(buildStarterTemplate(baseArgs), "package.json"));
|
|
35
|
+
expect(pkg.dependencies["react-native"]).toBe(STARTER_REACT_NATIVE_VERSION);
|
|
36
|
+
});
|
|
20
37
|
test("package.json uses caller-supplied versions for @lotics/ui and @lotics/app-sdk", () => {
|
|
21
38
|
const pkg = JSON.parse(fileNamed(buildStarterTemplate({ ...baseArgs, ui_version: "^9.9.9", sdk_version: "^2.2.2" }), "package.json"));
|
|
22
39
|
expect(pkg.dependencies["@lotics/ui"]).toBe("^9.9.9");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lotics/cli",
|
|
3
|
-
"version": "0.36.
|
|
3
|
+
"version": "0.36.2",
|
|
4
4
|
"description": "Lotics SDK and CLI for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@lotics/xlsx": "*",
|
|
25
25
|
"@types/node": "^22.15.21",
|
|
26
26
|
"esbuild": "^0.25.0",
|
|
27
|
-
"vitest": "^4.
|
|
27
|
+
"vitest": "^4.1.7"
|
|
28
28
|
},
|
|
29
29
|
"keywords": [
|
|
30
30
|
"lotics",
|