@ollie-shop/cli 1.6.0 → 1.7.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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @ollie-shop/cli@1.6.0 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
2
+ > @ollie-shop/cli@1.7.0 build /home/runner/work/ollie-shop/ollie-shop/packages/cli
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/index.tsx
@@ -9,5 +9,5 @@
9
9
  CLI Target: node22
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
- ESM dist/index.js 112.10 KB
13
- ESM ⚡️ Build success in 291ms
12
+ ESM dist/index.js 115.94 KB
13
+ ESM ⚡️ Build success in 278ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @ollie-shop/cli
2
2
 
3
+ ## 1.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3d5d259: Add a `component update` command so custom component props (and name, slot, active, version) can be changed after creation directly from the CLI. Previously the CLI could only `create` and `list` components, forcing prop edits through the Studio UI. Supports partial updates via flags (`--id`, `--name`, `--slot`, `--active`, `--props`, `--version-id`) or a full `--data` JSON payload, mirroring the existing `business-rule update` command.
8
+
9
+ ### Patch Changes
10
+
11
+ - 616e0ea: Fix `deploy --function-id` looking for the function source under `components/`. The deploy bundler was hardcoded to `components/<name>` and always emitted a component-style `index.tsx` (`export { default }`), so function deploys failed with `Component "<name>" not found in components/` — or bundled the wrong entry point. It now resolves function sources from `functions/<name>` and emits a function-style `index.ts` (`export { handler }`) that the builder's `build_function` step expects.
12
+
3
13
  ## 1.6.0
4
14
 
5
15
  ### Minor Changes
package/dist/index.js CHANGED
@@ -47,8 +47,8 @@ function HelpCommand() {
47
47
  /* @__PURE__ */ jsx(Text, { children: "Create or list versions" })
48
48
  ] }),
49
49
  /* @__PURE__ */ jsxs(Box, { children: [
50
- /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "component create|list" }) }),
51
- /* @__PURE__ */ jsx(Text, { children: "Create or list components" })
50
+ /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "component create|update|list" }) }),
51
+ /* @__PURE__ */ jsx(Text, { children: "Create, update or list components" })
52
52
  ] }),
53
53
  /* @__PURE__ */ jsxs(Box, { children: [
54
54
  /* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "function create|list" }) }),
@@ -520,12 +520,19 @@ import path3 from "path";
520
520
  import { PassThrough } from "stream";
521
521
  import archiver from "archiver";
522
522
  async function createComponentBundle(options) {
523
- const { cwd = process.cwd(), componentName } = options;
524
- const componentDir = path3.join(cwd, "components", componentName);
523
+ const {
524
+ cwd = process.cwd(),
525
+ componentName,
526
+ resourceType = "component"
527
+ } = options;
528
+ const sourceDir = resourceType === "function" ? "functions" : "components";
529
+ const componentDir = path3.join(cwd, sourceDir, componentName);
525
530
  try {
526
531
  await fs3.access(componentDir);
527
532
  } catch {
528
- throw new Error(`Component "${componentName}" not found in components/`);
533
+ throw new Error(
534
+ `${resourceType === "function" ? "Function" : "Component"} "${componentName}" not found in ${sourceDir}/`
535
+ );
529
536
  }
530
537
  const packageJsonPath = path3.join(cwd, "package.json");
531
538
  try {
@@ -541,9 +548,15 @@ async function createComponentBundle(options) {
541
548
  archive.on("error", (err) => {
542
549
  output.destroy(err);
543
550
  });
544
- const entryPoint = `export { default } from './components/${componentName}';
551
+ if (resourceType === "function") {
552
+ const entryPoint = `export { handler } from './functions/${componentName}';
553
+ `;
554
+ archive.append(entryPoint, { name: "index.ts" });
555
+ } else {
556
+ const entryPoint = `export { default } from './components/${componentName}';
545
557
  `;
546
- archive.append(entryPoint, { name: "index.tsx" });
558
+ archive.append(entryPoint, { name: "index.tsx" });
559
+ }
547
560
  archive.file(packageJsonPath, { name: "package.json" });
548
561
  const entries = await fs3.readdir(cwd, { withFileTypes: true });
549
562
  for (const entry of entries) {
@@ -1737,7 +1750,7 @@ function App({ command, args }) {
1737
1750
  }
1738
1751
  }
1739
1752
  function VersionCommand() {
1740
- const version = "1.6.0" ? "1.6.0" : "unknown";
1753
+ const version = "1.7.0" ? "1.7.0" : "unknown";
1741
1754
  return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { children: [
1742
1755
  "ollieshop v",
1743
1756
  version
@@ -2263,6 +2276,14 @@ var componentCreateSchema = z2.object({
2263
2276
  active: z2.boolean().default(true).describe("Whether component is active"),
2264
2277
  props: z2.record(z2.unknown()).nullable().default(null).describe("Default component props as JSON object")
2265
2278
  });
2279
+ var componentUpdateSchema = z2.object({
2280
+ id: z2.string().uuid().describe("Component UUID to update"),
2281
+ versionId: z2.string().uuid().optional().describe("Move component to a different version UUID"),
2282
+ name: z2.string().min(1).optional().describe("Component name"),
2283
+ slot: z2.string().min(1).optional().describe("Target slot"),
2284
+ active: z2.boolean().optional().describe("Whether component is active"),
2285
+ props: z2.record(z2.unknown()).nullable().optional().describe("Component props as JSON object (null clears them)")
2286
+ });
2266
2287
  var componentListSchema = z2.object({
2267
2288
  storeId: z2.string().uuid().describe("Store UUID"),
2268
2289
  versionId: z2.string().uuid().optional().describe("Optional version UUID to filter by")
@@ -2294,6 +2315,7 @@ var schemas = {
2294
2315
  },
2295
2316
  component: {
2296
2317
  create: componentCreateSchema,
2318
+ update: componentUpdateSchema,
2297
2319
  list: componentListSchema
2298
2320
  },
2299
2321
  function: {
@@ -2350,6 +2372,32 @@ async function createComponent(client, input) {
2350
2372
  }
2351
2373
  return { data: { id: data.id } };
2352
2374
  }
2375
+ async function updateComponent(client, id, input) {
2376
+ const parsed2 = componentUpdateSchema.safeParse({ id, ...input });
2377
+ if (!parsed2.success) {
2378
+ return {
2379
+ error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
2380
+ };
2381
+ }
2382
+ const updatePayload = {};
2383
+ if (input.name !== void 0) updatePayload.name = input.name;
2384
+ if (input.slot !== void 0) updatePayload.slot = input.slot;
2385
+ if (input.active !== void 0) updatePayload.active = input.active;
2386
+ if (input.versionId !== void 0) updatePayload.version_id = input.versionId;
2387
+ if (input.props !== void 0) updatePayload.props = input.props;
2388
+ if (Object.keys(updatePayload).length === 0) {
2389
+ return {
2390
+ error: {
2391
+ message: "No fields to update. Provide at least one of: --name, --slot, --active, --props, --version-id."
2392
+ }
2393
+ };
2394
+ }
2395
+ const { data, error } = await client.from("components").update(updatePayload).eq("id", id).select("id").single();
2396
+ if (error) {
2397
+ return { error: { message: error.message } };
2398
+ }
2399
+ return { data: { id: data.id } };
2400
+ }
2353
2401
  async function listComponents(client, storeId, versionId) {
2354
2402
  let query = client.from("components").select(
2355
2403
  "id, name, slot, active, version_id, props, created_at, versions!inner(id, name)"
@@ -2368,9 +2416,10 @@ async function listComponents(client, storeId, versionId) {
2368
2416
  async function componentCommand(parsed2) {
2369
2417
  const sub = parsed2.subcommand;
2370
2418
  if (sub === "create") return componentCreateCommand(parsed2);
2419
+ if (sub === "update") return componentUpdateCommand(parsed2);
2371
2420
  if (sub === "list" || sub === "ls") return componentListCommand(parsed2);
2372
2421
  console.error(
2373
- `Unknown component subcommand: ${sub}. Use: component create | component list`
2422
+ `Unknown component subcommand: ${sub}. Use: component create | component update | component list`
2374
2423
  );
2375
2424
  process.exit(1);
2376
2425
  }
@@ -2421,6 +2470,66 @@ async function componentCreateCommand(parsed2) {
2421
2470
  process.exit(1);
2422
2471
  }
2423
2472
  }
2473
+ async function componentUpdateCommand(parsed2) {
2474
+ const format = detectOutputFormat(parsed2.global.output);
2475
+ try {
2476
+ let id;
2477
+ let input;
2478
+ if (parsed2.global.data) {
2479
+ const raw = JSON.parse(parsed2.global.data);
2480
+ id = validateUuid(validateRequired(raw.id, "id"), "id");
2481
+ input = {
2482
+ versionId: raw.versionId !== void 0 ? validateUuid(raw.versionId, "versionId") : void 0,
2483
+ name: raw.name,
2484
+ slot: raw.slot,
2485
+ active: typeof raw.active === "boolean" ? raw.active : void 0,
2486
+ props: raw.props
2487
+ };
2488
+ } else {
2489
+ id = validateUuid(
2490
+ validateRequired(getFlag(parsed2.flags, "id"), "id"),
2491
+ "id"
2492
+ );
2493
+ const versionId = getFlag(parsed2.flags, "version-id");
2494
+ if (versionId) validateUuid(versionId, "version-id");
2495
+ const activeRaw = parsed2.flags.active;
2496
+ let active;
2497
+ if (activeRaw === "true" || activeRaw === true) {
2498
+ active = true;
2499
+ } else if (activeRaw === "false") {
2500
+ active = false;
2501
+ }
2502
+ const propsRaw = getFlag(parsed2.flags, "props");
2503
+ input = {
2504
+ versionId,
2505
+ name: getFlag(parsed2.flags, "name", "n"),
2506
+ slot: getFlag(parsed2.flags, "slot", "s"),
2507
+ active,
2508
+ props: propsRaw ? JSON.parse(propsRaw) : void 0
2509
+ };
2510
+ }
2511
+ if (parsed2.global.dryRun) {
2512
+ outputDryRun(
2513
+ "component.update",
2514
+ { id, ...input },
2515
+ format
2516
+ );
2517
+ return;
2518
+ }
2519
+ const client = await getAuthenticatedClient();
2520
+ const result = await updateComponent(client, id, input);
2521
+ outputResult(result, format, parsed2.global.fields);
2522
+ if (result.error) process.exit(1);
2523
+ } catch (err) {
2524
+ outputResult(
2525
+ {
2526
+ error: { message: err instanceof Error ? err.message : String(err) }
2527
+ },
2528
+ format
2529
+ );
2530
+ process.exit(1);
2531
+ }
2532
+ }
2424
2533
  async function componentListCommand(parsed2) {
2425
2534
  const format = detectOutputFormat(parsed2.global.output);
2426
2535
  try {
@@ -2588,7 +2697,8 @@ async function deployCommand(parsed2) {
2588
2697
  }
2589
2698
  const stream = await createComponentBundle({
2590
2699
  componentName,
2591
- cwd: process.cwd()
2700
+ cwd: process.cwd(),
2701
+ resourceType
2592
2702
  });
2593
2703
  const chunks = [];
2594
2704
  for await (const chunk of stream) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ollie-shop/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Ollie Shop CLI - Development tools for custom checkouts",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,4 +1,8 @@
1
- import { createComponent, listComponents } from "../core/component.js";
1
+ import {
2
+ createComponent,
3
+ listComponents,
4
+ updateComponent,
5
+ } from "../core/component.js";
2
6
  import {
3
7
  detectOutputFormat,
4
8
  outputDryRun,
@@ -11,10 +15,11 @@ import { validateRequired, validateUuid } from "../utils/validate.js";
11
15
  export async function componentCommand(parsed: ParsedArgs): Promise<void> {
12
16
  const sub = parsed.subcommand;
13
17
  if (sub === "create") return componentCreateCommand(parsed);
18
+ if (sub === "update") return componentUpdateCommand(parsed);
14
19
  if (sub === "list" || sub === "ls") return componentListCommand(parsed);
15
20
 
16
21
  console.error(
17
- `Unknown component subcommand: ${sub}. Use: component create | component list`,
22
+ `Unknown component subcommand: ${sub}. Use: component create | component update | component list`,
18
23
  );
19
24
  process.exit(1);
20
25
  }
@@ -79,6 +84,87 @@ async function componentCreateCommand(parsed: ParsedArgs): Promise<void> {
79
84
  }
80
85
  }
81
86
 
87
+ async function componentUpdateCommand(parsed: ParsedArgs): Promise<void> {
88
+ const format = detectOutputFormat(parsed.global.output);
89
+
90
+ try {
91
+ let id: string;
92
+ let input: {
93
+ versionId?: string;
94
+ name?: string;
95
+ slot?: string;
96
+ active?: boolean;
97
+ props?: Record<string, unknown> | null;
98
+ };
99
+
100
+ if (parsed.global.data) {
101
+ const raw = JSON.parse(parsed.global.data);
102
+ id = validateUuid(validateRequired(raw.id, "id"), "id");
103
+ input = {
104
+ versionId:
105
+ raw.versionId !== undefined
106
+ ? validateUuid(raw.versionId, "versionId")
107
+ : undefined,
108
+ name: raw.name,
109
+ slot: raw.slot,
110
+ active: typeof raw.active === "boolean" ? raw.active : undefined,
111
+ props: raw.props,
112
+ };
113
+ } else {
114
+ id = validateUuid(
115
+ validateRequired(getFlag(parsed.flags, "id"), "id"),
116
+ "id",
117
+ );
118
+
119
+ const versionId = getFlag(parsed.flags, "version-id");
120
+ if (versionId) validateUuid(versionId, "version-id");
121
+
122
+ const activeRaw = parsed.flags.active;
123
+ let active: boolean | undefined;
124
+ if (activeRaw === "true" || activeRaw === true) {
125
+ active = true;
126
+ } else if (activeRaw === "false") {
127
+ active = false;
128
+ }
129
+
130
+ const propsRaw = getFlag(parsed.flags, "props");
131
+
132
+ input = {
133
+ versionId,
134
+ name: getFlag(parsed.flags, "name", "n"),
135
+ slot: getFlag(parsed.flags, "slot", "s"),
136
+ active,
137
+ props: propsRaw
138
+ ? (JSON.parse(propsRaw) as Record<string, unknown> | null)
139
+ : undefined,
140
+ };
141
+ }
142
+
143
+ if (parsed.global.dryRun) {
144
+ outputDryRun(
145
+ "component.update",
146
+ { id, ...input } as Record<string, unknown>,
147
+ format,
148
+ );
149
+ return;
150
+ }
151
+
152
+ const client = await getAuthenticatedClient();
153
+ const result = await updateComponent(client, id, input);
154
+
155
+ outputResult(result, format, parsed.global.fields);
156
+ if (result.error) process.exit(1);
157
+ } catch (err) {
158
+ outputResult(
159
+ {
160
+ error: { message: err instanceof Error ? err.message : String(err) },
161
+ },
162
+ format,
163
+ );
164
+ process.exit(1);
165
+ }
166
+ }
167
+
82
168
  async function componentListCommand(parsed: ParsedArgs): Promise<void> {
83
169
  const format = detectOutputFormat(parsed.global.output);
84
170
 
@@ -61,10 +61,11 @@ export async function deployCommand(parsed: ParsedArgs): Promise<void> {
61
61
  timeout = Number(getFlag(parsed.flags, "timeout") ?? "300");
62
62
  }
63
63
 
64
- // Bundle the component
64
+ // Bundle the resource
65
65
  const stream = await createComponentBundle({
66
66
  componentName,
67
67
  cwd: process.cwd(),
68
+ resourceType,
68
69
  });
69
70
 
70
71
  // Collect the zip buffer
@@ -57,9 +57,9 @@ export function HelpCommand() {
57
57
  </Box>
58
58
  <Box>
59
59
  <Box width={24}>
60
- <Text color="green">component create|list</Text>
60
+ <Text color="green">component create|update|list</Text>
61
61
  </Box>
62
- <Text>Create or list components</Text>
62
+ <Text>Create, update or list components</Text>
63
63
  </Box>
64
64
  <Box>
65
65
  <Box width={24}>
@@ -1,5 +1,5 @@
1
1
  import type { SupabaseClient } from "@supabase/supabase-js";
2
- import { componentCreateSchema } from "./schema.js";
2
+ import { componentCreateSchema, componentUpdateSchema } from "./schema.js";
3
3
 
4
4
  export interface CreateComponentInput {
5
5
  versionId: string;
@@ -9,6 +9,14 @@ export interface CreateComponentInput {
9
9
  props?: Record<string, unknown> | null;
10
10
  }
11
11
 
12
+ export interface UpdateComponentInput {
13
+ versionId?: string;
14
+ name?: string;
15
+ slot?: string;
16
+ active?: boolean;
17
+ props?: Record<string, unknown> | null;
18
+ }
19
+
12
20
  export interface ComponentRecord {
13
21
  id: string;
14
22
  name: string;
@@ -50,6 +58,48 @@ export async function createComponent(
50
58
  return { data: { id: data.id } };
51
59
  }
52
60
 
61
+ export async function updateComponent(
62
+ client: SupabaseClient,
63
+ id: string,
64
+ input: UpdateComponentInput,
65
+ ): Promise<{ data?: { id: string }; error?: { message: string } }> {
66
+ const parsed = componentUpdateSchema.safeParse({ id, ...input });
67
+ if (!parsed.success) {
68
+ return {
69
+ error: { message: parsed.error.issues.map((i) => i.message).join("; ") },
70
+ };
71
+ }
72
+
73
+ const updatePayload: Record<string, unknown> = {};
74
+ if (input.name !== undefined) updatePayload.name = input.name;
75
+ if (input.slot !== undefined) updatePayload.slot = input.slot;
76
+ if (input.active !== undefined) updatePayload.active = input.active;
77
+ if (input.versionId !== undefined) updatePayload.version_id = input.versionId;
78
+ if (input.props !== undefined) updatePayload.props = input.props;
79
+
80
+ if (Object.keys(updatePayload).length === 0) {
81
+ return {
82
+ error: {
83
+ message:
84
+ "No fields to update. Provide at least one of: --name, --slot, --active, --props, --version-id.",
85
+ },
86
+ };
87
+ }
88
+
89
+ const { data, error } = await client
90
+ .from("components")
91
+ .update(updatePayload)
92
+ .eq("id", id)
93
+ .select("id")
94
+ .single();
95
+
96
+ if (error) {
97
+ return { error: { message: error.message } };
98
+ }
99
+
100
+ return { data: { id: data.id } };
101
+ }
102
+
53
103
  export async function listComponents(
54
104
  client: SupabaseClient,
55
105
  storeId: string,
@@ -52,6 +52,23 @@ export const componentCreateSchema = z.object({
52
52
  .describe("Default component props as JSON object"),
53
53
  });
54
54
 
55
+ export const componentUpdateSchema = z.object({
56
+ id: z.string().uuid().describe("Component UUID to update"),
57
+ versionId: z
58
+ .string()
59
+ .uuid()
60
+ .optional()
61
+ .describe("Move component to a different version UUID"),
62
+ name: z.string().min(1).optional().describe("Component name"),
63
+ slot: z.string().min(1).optional().describe("Target slot"),
64
+ active: z.boolean().optional().describe("Whether component is active"),
65
+ props: z
66
+ .record(z.unknown())
67
+ .nullable()
68
+ .optional()
69
+ .describe("Component props as JSON object (null clears them)"),
70
+ });
71
+
55
72
  export const componentListSchema = z.object({
56
73
  storeId: z.string().uuid().describe("Store UUID"),
57
74
  versionId: z
@@ -109,6 +126,7 @@ const schemas: Record<string, SchemaMap> = {
109
126
  },
110
127
  component: {
111
128
  create: componentCreateSchema,
129
+ update: componentUpdateSchema,
112
130
  list: componentListSchema,
113
131
  },
114
132
  function: {
@@ -5,11 +5,15 @@ import archiver from "archiver";
5
5
 
6
6
  // TODO: Implement .ollieignore file support for custom exclusions
7
7
 
8
+ export type BundleResourceType = "component" | "function";
9
+
8
10
  export interface BundleOptions {
9
11
  /** Project root directory */
10
12
  cwd?: string;
11
13
  /** Component name (folder name in components/) */
12
14
  componentName: string;
15
+ /** Resource type — decides the source folder and entry point shape */
16
+ resourceType?: BundleResourceType;
13
17
  }
14
18
 
15
19
  export interface BundleResult {
@@ -33,14 +37,21 @@ export interface BundleResult {
33
37
  export async function createComponentBundle(
34
38
  options: BundleOptions,
35
39
  ): Promise<PassThrough> {
36
- const { cwd = process.cwd(), componentName } = options;
40
+ const {
41
+ cwd = process.cwd(),
42
+ componentName,
43
+ resourceType = "component",
44
+ } = options;
45
+
46
+ const sourceDir = resourceType === "function" ? "functions" : "components";
37
47
 
38
- // Verify component exists
39
- const componentDir = path.join(cwd, "components", componentName);
48
+ const componentDir = path.join(cwd, sourceDir, componentName);
40
49
  try {
41
50
  await fs.access(componentDir);
42
51
  } catch {
43
- throw new Error(`Component "${componentName}" not found in components/`);
52
+ throw new Error(
53
+ `${resourceType === "function" ? "Function" : "Component"} "${componentName}" not found in ${sourceDir}/`,
54
+ );
44
55
  }
45
56
 
46
57
  // Verify package.json exists
@@ -67,8 +78,13 @@ export async function createComponentBundle(
67
78
  });
68
79
 
69
80
  // Add synthetic entry point
70
- const entryPoint = `export { default } from './components/${componentName}';\n`;
71
- archive.append(entryPoint, { name: "index.tsx" });
81
+ if (resourceType === "function") {
82
+ const entryPoint = `export { handler } from './functions/${componentName}';\n`;
83
+ archive.append(entryPoint, { name: "index.ts" });
84
+ } else {
85
+ const entryPoint = `export { default } from './components/${componentName}';\n`;
86
+ archive.append(entryPoint, { name: "index.tsx" });
87
+ }
72
88
 
73
89
  // Add package.json
74
90
  archive.file(packageJsonPath, { name: "package.json" });