@lotics/cli 0.31.0 → 0.32.0

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.
@@ -19,6 +19,32 @@ import { buildStarterTemplate } from "./starter_template.js";
19
19
  import { startDevServer, openBrowser } from "./dev/server.js";
20
20
  import { generateAppWorkflowsDts } from "./generate_app_workflows_dts.js";
21
21
  import { generateAppQueriesDts } from "./generate_app_queries_dts.js";
22
+ /**
23
+ * Resolve the latest published version of a package from the npm registry.
24
+ * Returns null on any failure (network error, 404, malformed payload) so
25
+ * callers can fall back to a static pin rather than crashing `app create`.
26
+ *
27
+ * 1.5s timeout — npm registry is fast when reachable; the fallback is fine
28
+ * the rare times it isn't, and we don't want to block scaffold on a hang.
29
+ */
30
+ async function fetchLatestNpmVersion(packageName) {
31
+ try {
32
+ const controller = new AbortController();
33
+ const timeout = setTimeout(() => controller.abort(), 1500);
34
+ const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
35
+ signal: controller.signal,
36
+ headers: { accept: "application/json" },
37
+ });
38
+ clearTimeout(timeout);
39
+ if (!response.ok)
40
+ return null;
41
+ const body = (await response.json());
42
+ return typeof body.version === "string" ? body.version : null;
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
22
48
  /** Run `tar` and resolve when it exits cleanly. Throws with stderr on failure. */
23
49
  function runTar(args, cwd) {
24
50
  return new Promise((resolve, reject) => {
@@ -148,11 +174,20 @@ export async function appCreate(client, args) {
148
174
  // Server-side row first — if this fails, no local files have been touched.
149
175
  const app = await client.createApp({ name: args.name });
150
176
  console.error(`Created app: ${app.name} (${app.id})`);
177
+ // Resolve latest versions in parallel; null falls back to the static pin
178
+ // baked into the starter template. Lookups time out at 1.5s so an offline
179
+ // create still succeeds (with the fallback) instead of hanging.
180
+ const [uiLatest, sdkLatest] = await Promise.all([
181
+ fetchLatestNpmVersion("@lotics/ui"),
182
+ fetchLatestNpmVersion("@lotics/app-sdk"),
183
+ ]);
151
184
  // Scaffold the starter into the target directory.
152
185
  const files = buildStarterTemplate({
153
186
  app_name: args.name,
154
187
  app_id: app.id,
155
188
  workspace_id: app.workspace_id,
189
+ ui_version: uiLatest ? `^${uiLatest}` : undefined,
190
+ sdk_version: sdkLatest ? `^${sdkLatest}` : undefined,
156
191
  });
157
192
  for (const file of files) {
158
193
  const fullPath = path.join(targetPath, file.path);
@@ -29,8 +29,24 @@ export interface StarterFile {
29
29
  path: string;
30
30
  content: string;
31
31
  }
32
+ /**
33
+ * Pin defaults for `@lotics/ui` and `@lotics/app-sdk` in the scaffolded
34
+ * package.json. Used when `lotics app create` can't resolve the latest
35
+ * versions from npm (no network, registry blip) — keeps `app create`
36
+ * working offline at the cost of a stale starter pin.
37
+ *
38
+ * Bump these in tandem with new releases of either package. Production
39
+ * scaffolds resolve the live version via `fetchLatestNpmVersion` and only
40
+ * fall back here when the lookup fails.
41
+ */
42
+ export declare const STARTER_FALLBACK_UI_VERSION = "1.8.0";
43
+ export declare const STARTER_FALLBACK_SDK_VERSION = "0.8.0";
32
44
  export declare function buildStarterTemplate(args: {
33
45
  app_name: string;
34
46
  app_id: string;
35
47
  workspace_id: string;
48
+ /** @lotics/ui version range, e.g. "^1.8.0". Defaults to the fallback pin. */
49
+ ui_version?: string;
50
+ /** @lotics/app-sdk version range, e.g. "^0.7.0". Defaults to the fallback pin. */
51
+ sdk_version?: string;
36
52
  }): StarterFile[];
@@ -25,7 +25,21 @@
25
25
  * - oxlint + vitest + tsc out of the box, plus a GitHub Actions CI
26
26
  * workflow that runs all three on every PR.
27
27
  */
28
+ /**
29
+ * Pin defaults for `@lotics/ui` and `@lotics/app-sdk` in the scaffolded
30
+ * package.json. Used when `lotics app create` can't resolve the latest
31
+ * versions from npm (no network, registry blip) — keeps `app create`
32
+ * working offline at the cost of a stale starter pin.
33
+ *
34
+ * Bump these in tandem with new releases of either package. Production
35
+ * scaffolds resolve the live version via `fetchLatestNpmVersion` and only
36
+ * fall back here when the lookup fails.
37
+ */
38
+ export const STARTER_FALLBACK_UI_VERSION = "1.8.0";
39
+ export const STARTER_FALLBACK_SDK_VERSION = "0.8.0";
28
40
  export function buildStarterTemplate(args) {
41
+ const uiVersion = args.ui_version ?? `^${STARTER_FALLBACK_UI_VERSION}`;
42
+ const sdkVersion = args.sdk_version ?? `^${STARTER_FALLBACK_SDK_VERSION}`;
29
43
  const sanitizedPkgName = args.app_name
30
44
  .toLowerCase()
31
45
  .replace(/[^a-z0-9-]/g, "-")
@@ -50,8 +64,8 @@ export function buildStarterTemplate(args) {
50
64
  test: "vitest run",
51
65
  },
52
66
  dependencies: {
53
- "@lotics/app-sdk": "^0.7.0",
54
- "@lotics/ui": "^1.3.0",
67
+ "@lotics/app-sdk": sdkVersion,
68
+ "@lotics/ui": uiVersion,
55
69
  "@react-native-picker/picker": "^2.7.0",
56
70
  "expo-image": "~3.0.9",
57
71
  "lucide-react": "^0.562.0",
@@ -145,6 +159,15 @@ export default defineConfig({
145
159
  // Pin React (and RN-Web) to a single instance shared by every chunk.
146
160
  dedupe: ["react", "react-dom", "react-native-web"],
147
161
  },
162
+ optimizeDeps: {
163
+ // \`recharts\` (used by @lotics/ui chart_* + sparkline) imports
164
+ // \`es-toolkit/compat/get\` as a default-export CJS module. Vite's dev
165
+ // server treats \`compat/*\` as ESM and won't synthesize a default,
166
+ // so \`import get from "es-toolkit/compat/get"\` fails to resolve.
167
+ // Pre-bundling forces Vite to convert it to an ESM shim with a default
168
+ // export. Production build (rollup) handles it correctly without this.
169
+ include: ["recharts", "es-toolkit", "es-toolkit/compat"],
170
+ },
148
171
  build: {
149
172
  outDir: "dist",
150
173
  sourcemap: true,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,32 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { buildStarterTemplate, STARTER_FALLBACK_SDK_VERSION, STARTER_FALLBACK_UI_VERSION, } from "./starter_template.js";
3
+ function fileNamed(files, path) {
4
+ const f = files.find((f) => f.path === path);
5
+ if (!f)
6
+ throw new Error(`Starter template missing file: ${path}`);
7
+ return f.content;
8
+ }
9
+ describe("buildStarterTemplate", () => {
10
+ const baseArgs = { app_name: "Demo", app_id: "app_test", workspace_id: "wsp_test" };
11
+ test("vite.config.ts pre-bundles recharts / es-toolkit for the dev server", () => {
12
+ const config = fileNamed(buildStarterTemplate(baseArgs), "vite.config.ts");
13
+ // Without this, `lotics app dev` crashes on `es-toolkit/compat/get` no
14
+ // default export when the app imports any @lotics/ui chart component.
15
+ expect(config).toMatch(/optimizeDeps:\s*\{/);
16
+ expect(config).toContain('"recharts"');
17
+ expect(config).toContain('"es-toolkit"');
18
+ expect(config).toContain('"es-toolkit/compat"');
19
+ });
20
+ test("package.json uses caller-supplied versions for @lotics/ui and @lotics/app-sdk", () => {
21
+ const pkg = JSON.parse(fileNamed(buildStarterTemplate({ ...baseArgs, ui_version: "^9.9.9", sdk_version: "^2.2.2" }), "package.json"));
22
+ expect(pkg.dependencies["@lotics/ui"]).toBe("^9.9.9");
23
+ expect(pkg.dependencies["@lotics/app-sdk"]).toBe("^2.2.2");
24
+ });
25
+ test("package.json falls back to the static pins when versions are omitted", () => {
26
+ // Used when `lotics app create` cannot reach the npm registry to resolve
27
+ // `latest`. Keeps `app create` working offline at the cost of a stale pin.
28
+ const pkg = JSON.parse(fileNamed(buildStarterTemplate(baseArgs), "package.json"));
29
+ expect(pkg.dependencies["@lotics/ui"]).toBe(`^${STARTER_FALLBACK_UI_VERSION}`);
30
+ expect(pkg.dependencies["@lotics/app-sdk"]).toBe(`^${STARTER_FALLBACK_SDK_VERSION}`);
31
+ });
32
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lotics/cli",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Lotics SDK and CLI for AI agents",
5
5
  "type": "module",
6
6
  "bin": {