@ollie-shop/cli 1.2.2 → 1.4.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.
- package/.env.example +9 -5
- package/.turbo/turbo-build.log +3 -3
- package/CHANGELOG.md +57 -0
- package/CONTEXT.md +11 -3
- package/README.md +7 -10
- package/dist/index.js +323 -76
- package/package.json +4 -5
- package/src/cli.tsx +8 -1
- package/src/commands/function-cmd.ts +42 -10
- package/src/commands/help.tsx +12 -1
- package/src/commands/start.tsx +60 -44
- package/src/commands/store-cmd.ts +14 -4
- package/src/commands/whoami.ts +70 -9
- package/src/core/function.ts +1 -1
- package/src/core/schema.ts +8 -3
- package/src/core/store.ts +50 -3
- package/src/utils/auth.ts +4 -0
- package/src/utils/esbuild.ts +99 -15
- package/src/utils/parse-args.ts +2 -0
- package/src/utils/supabase.ts +66 -11
- package/tsup.config.ts +7 -0
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ function HelpCommand() {
|
|
|
36
36
|
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", gap: 0, children: [
|
|
37
37
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
38
38
|
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "whoami" }) }),
|
|
39
|
-
/* @__PURE__ */ jsx(Text, { children: "Show current user and
|
|
39
|
+
/* @__PURE__ */ jsx(Text, { children: "Show current user (and linked store/org from ollie.json)" })
|
|
40
40
|
] }),
|
|
41
41
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
42
42
|
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "green", children: "store create|list" }) }),
|
|
@@ -107,15 +107,21 @@ function HelpCommand() {
|
|
|
107
107
|
"{stage}",
|
|
108
108
|
".json)"
|
|
109
109
|
] })
|
|
110
|
+
] }),
|
|
111
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
112
|
+
/* @__PURE__ */ jsx(Box, { width: 24, children: /* @__PURE__ */ jsx(Text, { color: "yellow", children: "--no-open" }) }),
|
|
113
|
+
/* @__PURE__ */ jsx(Text, { children: "start: don't auto-open Studio (also honored via CI env)" })
|
|
110
114
|
] })
|
|
111
115
|
] }),
|
|
112
116
|
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Examples:" }) }),
|
|
113
117
|
/* @__PURE__ */ jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [
|
|
114
118
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop login" }),
|
|
115
119
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop start --stage dev" }),
|
|
120
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop start --no-open" }),
|
|
116
121
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop whoami -o json" }),
|
|
117
122
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "$ ollieshop schema store.create" }),
|
|
118
123
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: '$ ollieshop store create --name "My Store" --platform vtex --platform-store-id mystore' }),
|
|
124
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: '$ ollieshop store create --org UUID --name "My Store" --platform vtex --platform-store-id mystore' }),
|
|
119
125
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
120
126
|
"$ ollieshop store create --data",
|
|
121
127
|
" ",
|
|
@@ -304,6 +310,9 @@ async function getCurrentUser() {
|
|
|
304
310
|
if (!credentials) return null;
|
|
305
311
|
try {
|
|
306
312
|
const decoded = jwtDecode(credentials.accessToken);
|
|
313
|
+
if (decoded.exp && decoded.exp * 1e3 <= Date.now()) {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
307
316
|
return decoded.email ? { email: decoded.email } : null;
|
|
308
317
|
} catch {
|
|
309
318
|
return null;
|
|
@@ -439,6 +448,7 @@ function resolveStage(cliStage) {
|
|
|
439
448
|
}
|
|
440
449
|
|
|
441
450
|
// src/utils/esbuild.ts
|
|
451
|
+
import { watch } from "fs";
|
|
442
452
|
import fs4 from "fs/promises";
|
|
443
453
|
import http from "http";
|
|
444
454
|
import path4 from "path";
|
|
@@ -663,16 +673,69 @@ async function createBuildContext(components, options = {}) {
|
|
|
663
673
|
});
|
|
664
674
|
return ctx;
|
|
665
675
|
}
|
|
666
|
-
async function startDevServer(
|
|
667
|
-
const {
|
|
676
|
+
async function startDevServer(options = {}) {
|
|
677
|
+
const {
|
|
678
|
+
port = 4e3,
|
|
679
|
+
host = "localhost",
|
|
680
|
+
cwd = process.cwd(),
|
|
681
|
+
stage,
|
|
682
|
+
onRequest,
|
|
683
|
+
onBuildEnd
|
|
684
|
+
} = options;
|
|
668
685
|
const servedir = path4.join(cwd, "node_modules/.ollie", "build");
|
|
669
|
-
|
|
686
|
+
const componentsDir = path4.join(cwd, "components");
|
|
670
687
|
const internalPort = port + 1;
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
688
|
+
let ctx = null;
|
|
689
|
+
let entryNames = /* @__PURE__ */ new Set();
|
|
690
|
+
async function buildAndServe(components) {
|
|
691
|
+
entryNames = new Set(components.map((c) => c.name));
|
|
692
|
+
ctx = await createBuildContext(components, { cwd, stage, onBuildEnd });
|
|
693
|
+
await ctx.rebuild();
|
|
694
|
+
await ctx.watch();
|
|
695
|
+
await ctx.serve({ port: internalPort, host, servedir, onRequest });
|
|
696
|
+
}
|
|
697
|
+
async function recreate() {
|
|
698
|
+
const components = await discoverComponents({ cwd, stage });
|
|
699
|
+
const oldCtx = ctx;
|
|
700
|
+
ctx = null;
|
|
701
|
+
if (oldCtx) await oldCtx.dispose();
|
|
702
|
+
await buildAndServe(components);
|
|
703
|
+
}
|
|
704
|
+
await buildAndServe(await discoverComponents({ cwd, stage }));
|
|
705
|
+
async function currentComponentNames() {
|
|
706
|
+
try {
|
|
707
|
+
const entries = await glob("*/index.tsx", { cwd: componentsDir });
|
|
708
|
+
return new Set(entries.map((e) => path4.dirname(e)));
|
|
709
|
+
} catch {
|
|
710
|
+
return /* @__PURE__ */ new Set();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
let watchTimer = null;
|
|
714
|
+
let recreating = false;
|
|
715
|
+
let pending = false;
|
|
716
|
+
async function maybeRecreate() {
|
|
717
|
+
if (recreating) {
|
|
718
|
+
pending = true;
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
recreating = true;
|
|
722
|
+
try {
|
|
723
|
+
do {
|
|
724
|
+
pending = false;
|
|
725
|
+
const names = await currentComponentNames();
|
|
726
|
+
const changed = names.size !== entryNames.size || [...names].some((n) => !entryNames.has(n));
|
|
727
|
+
if (!changed) break;
|
|
728
|
+
await recreate();
|
|
729
|
+
} while (pending);
|
|
730
|
+
} catch (err) {
|
|
731
|
+
console.error("[DevServer] Failed to reload components:", err);
|
|
732
|
+
} finally {
|
|
733
|
+
recreating = false;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
const componentsWatcher = watch(componentsDir, { recursive: true }, () => {
|
|
737
|
+
if (watchTimer) clearTimeout(watchTimer);
|
|
738
|
+
watchTimer = setTimeout(maybeRecreate, 150);
|
|
676
739
|
});
|
|
677
740
|
const proxyServer = http.createServer(async (req, res) => {
|
|
678
741
|
const url = new URL(req.url || "/", `http://${host}:${port}`);
|
|
@@ -756,6 +819,16 @@ async function startDevServer(ctx, options = {}) {
|
|
|
756
819
|
existingMeta = JSON.parse(content);
|
|
757
820
|
} catch {
|
|
758
821
|
}
|
|
822
|
+
if (typeof updates.id === "string" && typeof existingMeta.id === "string" && existingMeta.id !== updates.id) {
|
|
823
|
+
res.statusCode = 409;
|
|
824
|
+
res.setHeader("Content-Type", "application/json");
|
|
825
|
+
res.end(
|
|
826
|
+
JSON.stringify({
|
|
827
|
+
error: `id mismatch: ${componentName} is linked to ${existingMeta.id}`
|
|
828
|
+
})
|
|
829
|
+
);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
759
832
|
const newMeta = { ...existingMeta, ...updates };
|
|
760
833
|
await fs4.writeFile(metaPath, JSON.stringify(newMeta, null, 2));
|
|
761
834
|
res.setHeader("Content-Type", "application/json");
|
|
@@ -817,9 +890,14 @@ async function startDevServer(ctx, options = {}) {
|
|
|
817
890
|
return {
|
|
818
891
|
host,
|
|
819
892
|
port,
|
|
893
|
+
rebuild: async () => {
|
|
894
|
+
await ctx?.rebuild();
|
|
895
|
+
},
|
|
820
896
|
stop: async () => {
|
|
897
|
+
if (watchTimer) clearTimeout(watchTimer);
|
|
898
|
+
componentsWatcher.close();
|
|
821
899
|
proxyServer.close();
|
|
822
|
-
await ctx
|
|
900
|
+
await ctx?.dispose();
|
|
823
901
|
}
|
|
824
902
|
};
|
|
825
903
|
}
|
|
@@ -869,9 +947,11 @@ function StartCommand({ args }) {
|
|
|
869
947
|
const [buildCount, setBuildCount] = useState2(0);
|
|
870
948
|
const [lastBuildTime, setLastBuildTime] = useState2(null);
|
|
871
949
|
const logIdRef = useRef(0);
|
|
872
|
-
const
|
|
950
|
+
const rebuildRef = useRef(null);
|
|
873
951
|
const stopRef = useRef(null);
|
|
874
952
|
const stage = resolveStage(parseArg(args, "--stage", "-s"));
|
|
953
|
+
const noOpen = args.includes("--no-open") || Boolean(process.env.CI);
|
|
954
|
+
const isInteractive = Boolean(process.stdin.isTTY);
|
|
875
955
|
const addLog = useCallback((log) => {
|
|
876
956
|
setLogs((prev) => {
|
|
877
957
|
const newLog = {
|
|
@@ -919,21 +999,17 @@ function StartCommand({ args }) {
|
|
|
919
999
|
}
|
|
920
1000
|
setComponents(found);
|
|
921
1001
|
setState({ status: "building" });
|
|
922
|
-
const
|
|
1002
|
+
const server = await startDevServer({
|
|
1003
|
+
port: PORT,
|
|
923
1004
|
stage,
|
|
1005
|
+
onRequest: handleRequest,
|
|
924
1006
|
onBuildEnd: (updatedComponents) => {
|
|
925
1007
|
setComponents(updatedComponents);
|
|
926
1008
|
setBuildCount((c) => c + 1);
|
|
927
1009
|
setLastBuildTime(/* @__PURE__ */ new Date());
|
|
928
1010
|
}
|
|
929
1011
|
});
|
|
930
|
-
|
|
931
|
-
await ctx.rebuild();
|
|
932
|
-
if (!mounted) return;
|
|
933
|
-
const server = await startDevServer(ctx, {
|
|
934
|
-
port: PORT,
|
|
935
|
-
onRequest: handleRequest
|
|
936
|
-
});
|
|
1012
|
+
rebuildRef.current = server.rebuild;
|
|
937
1013
|
stopRef.current = server.stop;
|
|
938
1014
|
if (!mounted) {
|
|
939
1015
|
await server.stop();
|
|
@@ -946,12 +1022,14 @@ function StartCommand({ args }) {
|
|
|
946
1022
|
storeId: config.storeId,
|
|
947
1023
|
versionId: config.versionId
|
|
948
1024
|
});
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
1025
|
+
if (!noOpen) {
|
|
1026
|
+
const studioUrl = new URL(STUDIO_BASE_URL);
|
|
1027
|
+
studioUrl.searchParams.set("storeId", config.storeId);
|
|
1028
|
+
if (config.versionId) {
|
|
1029
|
+
studioUrl.searchParams.set("versionId", config.versionId);
|
|
1030
|
+
}
|
|
1031
|
+
open(studioUrl.toString());
|
|
953
1032
|
}
|
|
954
|
-
open(studioUrl.toString());
|
|
955
1033
|
} catch (error) {
|
|
956
1034
|
if (!mounted) return;
|
|
957
1035
|
setState({
|
|
@@ -965,23 +1043,26 @@ function StartCommand({ args }) {
|
|
|
965
1043
|
mounted = false;
|
|
966
1044
|
stopRef.current?.();
|
|
967
1045
|
};
|
|
968
|
-
}, [stage, handleRequest]);
|
|
969
|
-
useInput(
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
if (input === "r" && state.status === "running") {
|
|
974
|
-
ctxRef.current?.rebuild();
|
|
975
|
-
}
|
|
976
|
-
if (input === "o" && state.status === "running") {
|
|
977
|
-
const studioUrl = new URL(STUDIO_BASE_URL);
|
|
978
|
-
studioUrl.searchParams.set("storeId", state.storeId);
|
|
979
|
-
if (state.versionId) {
|
|
980
|
-
studioUrl.searchParams.set("versionId", state.versionId);
|
|
1046
|
+
}, [stage, handleRequest, noOpen]);
|
|
1047
|
+
useInput(
|
|
1048
|
+
(input, key) => {
|
|
1049
|
+
if (input === "q" || input === "c" && key.ctrl) {
|
|
1050
|
+
stopRef.current?.().then(() => exit());
|
|
981
1051
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1052
|
+
if (input === "r" && state.status === "running") {
|
|
1053
|
+
rebuildRef.current?.();
|
|
1054
|
+
}
|
|
1055
|
+
if (input === "o" && state.status === "running") {
|
|
1056
|
+
const studioUrl = new URL(STUDIO_BASE_URL);
|
|
1057
|
+
studioUrl.searchParams.set("storeId", state.storeId);
|
|
1058
|
+
if (state.versionId) {
|
|
1059
|
+
studioUrl.searchParams.set("versionId", state.versionId);
|
|
1060
|
+
}
|
|
1061
|
+
open(studioUrl.toString());
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
{ isActive: isInteractive }
|
|
1065
|
+
);
|
|
985
1066
|
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", gap: 1, children: [
|
|
986
1067
|
/* @__PURE__ */ jsx3(Header, {}),
|
|
987
1068
|
state.status === "initializing" && /* @__PURE__ */ jsxs3(Box3, { children: [
|
|
@@ -1012,13 +1093,14 @@ function StartCommand({ args }) {
|
|
|
1012
1093
|
port: state.port,
|
|
1013
1094
|
stage,
|
|
1014
1095
|
storeId: state.storeId,
|
|
1015
|
-
versionId: state.versionId
|
|
1096
|
+
versionId: state.versionId,
|
|
1097
|
+
noOpen
|
|
1016
1098
|
}
|
|
1017
1099
|
),
|
|
1018
1100
|
/* @__PURE__ */ jsx3(ComponentList, { components }),
|
|
1019
1101
|
/* @__PURE__ */ jsx3(BuildInfo, { buildCount, lastBuildTime }),
|
|
1020
1102
|
/* @__PURE__ */ jsx3(RequestLogs, { logs }),
|
|
1021
|
-
/* @__PURE__ */ jsx3(Footer, {})
|
|
1103
|
+
/* @__PURE__ */ jsx3(Footer, { interactive: isInteractive })
|
|
1022
1104
|
] })
|
|
1023
1105
|
] });
|
|
1024
1106
|
}
|
|
@@ -1033,7 +1115,8 @@ function ServerInfo({
|
|
|
1033
1115
|
port,
|
|
1034
1116
|
stage,
|
|
1035
1117
|
storeId,
|
|
1036
|
-
versionId
|
|
1118
|
+
versionId,
|
|
1119
|
+
noOpen
|
|
1037
1120
|
}) {
|
|
1038
1121
|
const studioUrl = new URL(STUDIO_BASE_URL);
|
|
1039
1122
|
studioUrl.searchParams.set("storeId", storeId);
|
|
@@ -1060,7 +1143,8 @@ function ServerInfo({
|
|
|
1060
1143
|
/* @__PURE__ */ jsxs3(Box3, { marginTop: 1, children: [
|
|
1061
1144
|
/* @__PURE__ */ jsx3(Text3, { color: "green", children: "\u2713 " }),
|
|
1062
1145
|
/* @__PURE__ */ jsx3(Text3, { children: "Studio: " }),
|
|
1063
|
-
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "magenta", children: studioUrl.toString() })
|
|
1146
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, color: "magenta", children: studioUrl.toString() }),
|
|
1147
|
+
noOpen && /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: " (auto-open off \u2014 press o)" })
|
|
1064
1148
|
] }),
|
|
1065
1149
|
/* @__PURE__ */ jsx3(Box3, { marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsxs3(Text3, { dimColor: true, children: [
|
|
1066
1150
|
"Components: http://",
|
|
@@ -1144,7 +1228,10 @@ function RequestLogs({ logs }) {
|
|
|
1144
1228
|
] }, log.id)) })
|
|
1145
1229
|
] });
|
|
1146
1230
|
}
|
|
1147
|
-
function Footer() {
|
|
1231
|
+
function Footer({ interactive = true }) {
|
|
1232
|
+
if (!interactive) {
|
|
1233
|
+
return /* @__PURE__ */ jsx3(Box3, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Headless (no TTY) \u2014 Ctrl+C to stop" }) });
|
|
1234
|
+
}
|
|
1148
1235
|
return /* @__PURE__ */ jsxs3(Box3, { marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
1149
1236
|
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Press " }),
|
|
1150
1237
|
/* @__PURE__ */ jsx3(Text3, { bold: true, children: "q" }),
|
|
@@ -1177,7 +1264,11 @@ function App({ command, args }) {
|
|
|
1177
1264
|
}
|
|
1178
1265
|
}
|
|
1179
1266
|
function VersionCommand() {
|
|
1180
|
-
|
|
1267
|
+
const version = "1.4.0" ? "1.4.0" : "unknown";
|
|
1268
|
+
return /* @__PURE__ */ jsx4(Box4, { children: /* @__PURE__ */ jsxs4(Text4, { children: [
|
|
1269
|
+
"ollieshop v",
|
|
1270
|
+
version
|
|
1271
|
+
] }) });
|
|
1181
1272
|
}
|
|
1182
1273
|
function UnknownCommand({ command }) {
|
|
1183
1274
|
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
@@ -1377,7 +1468,8 @@ function parseArgs(argv) {
|
|
|
1377
1468
|
output: flags.output || flags.o || void 0,
|
|
1378
1469
|
dryRun: flags["dry-run"] === true,
|
|
1379
1470
|
fields: flags.fields ? String(flags.fields).split(",").map((f) => f.trim()) : void 0,
|
|
1380
|
-
data: flags.data || flags.d || void 0
|
|
1471
|
+
data: flags.data || flags.d || void 0,
|
|
1472
|
+
stage: flags.stage || flags.s || void 0
|
|
1381
1473
|
};
|
|
1382
1474
|
return { command, subcommand, flags, global, positional };
|
|
1383
1475
|
}
|
|
@@ -1397,13 +1489,33 @@ function getBoolFlag(flags, ...names) {
|
|
|
1397
1489
|
|
|
1398
1490
|
// src/utils/supabase.ts
|
|
1399
1491
|
import { createClient } from "@supabase/supabase-js";
|
|
1492
|
+
var PROD_DEFAULTS = {
|
|
1493
|
+
supabaseUrl: "https://aazahtmqrhjqsyqoqdkm.supabase.co",
|
|
1494
|
+
supabaseAnonKey: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFhemFodG1xcmhqcXN5cW9xZGttIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDg2MzEwOTcsImV4cCI6MjA2NDIwNzA5N30.VuAbAyjDe0HcL09SZtQ-UmP1o7Z6qwGuOtvfFhnyAcM",
|
|
1495
|
+
builderUrl: "https://api.ollie.shop/builder/v1"
|
|
1496
|
+
};
|
|
1497
|
+
function envOrDefault(envValue, buildTimeValue, prodDefault) {
|
|
1498
|
+
return envValue || buildTimeValue || prodDefault;
|
|
1499
|
+
}
|
|
1500
|
+
function unwrapToOne(embed) {
|
|
1501
|
+
const record = Array.isArray(embed) ? embed[0] : embed;
|
|
1502
|
+
return record && typeof record === "object" ? record : null;
|
|
1503
|
+
}
|
|
1400
1504
|
async function getAuthenticatedClient() {
|
|
1401
1505
|
const credentials = await getCredentials();
|
|
1402
1506
|
if (!credentials) {
|
|
1403
1507
|
throw new Error("Not authenticated. Run `ollieshop login` first.");
|
|
1404
1508
|
}
|
|
1405
|
-
const supabaseUrl =
|
|
1406
|
-
|
|
1509
|
+
const supabaseUrl = envOrDefault(
|
|
1510
|
+
process.env.OLLIE_SUPABASE_URL,
|
|
1511
|
+
"https://aazahtmqrhjqsyqoqdkm.supabase.co",
|
|
1512
|
+
PROD_DEFAULTS.supabaseUrl
|
|
1513
|
+
);
|
|
1514
|
+
const supabaseAnonKey = envOrDefault(
|
|
1515
|
+
process.env.OLLIE_SUPABASE_ANON_KEY,
|
|
1516
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFhemFodG1xcmhqcXN5cW9xZGttIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDg2MzEwOTcsImV4cCI6MjA2NDIwNzA5N30.VuAbAyjDe0HcL09SZtQ-UmP1o7Z6qwGuOtvfFhnyAcM",
|
|
1517
|
+
PROD_DEFAULTS.supabaseAnonKey
|
|
1518
|
+
);
|
|
1407
1519
|
const client = createClient(supabaseUrl, supabaseAnonKey, {
|
|
1408
1520
|
auth: {
|
|
1409
1521
|
autoRefreshToken: false,
|
|
@@ -1417,7 +1529,11 @@ async function getAuthenticatedClient() {
|
|
|
1417
1529
|
return client;
|
|
1418
1530
|
}
|
|
1419
1531
|
function getBuilderUrl() {
|
|
1420
|
-
return
|
|
1532
|
+
return envOrDefault(
|
|
1533
|
+
process.env.OLLIE_BUILDER_URL,
|
|
1534
|
+
"",
|
|
1535
|
+
PROD_DEFAULTS.builderUrl
|
|
1536
|
+
);
|
|
1421
1537
|
}
|
|
1422
1538
|
async function getAuthToken() {
|
|
1423
1539
|
const credentials = await getCredentials();
|
|
@@ -1426,17 +1542,35 @@ async function getAuthToken() {
|
|
|
1426
1542
|
}
|
|
1427
1543
|
return credentials.accessToken;
|
|
1428
1544
|
}
|
|
1429
|
-
async function getOrganizationId(client) {
|
|
1430
|
-
const { data:
|
|
1545
|
+
async function getOrganizationId(client, preferredOrgId) {
|
|
1546
|
+
const { data: orgs, error } = await client.from("organizations").select("id, name").order("created_at", { ascending: true });
|
|
1431
1547
|
if (error) {
|
|
1432
1548
|
throw new Error(`Failed to get organization: ${error.message}`);
|
|
1433
1549
|
}
|
|
1434
|
-
if (!
|
|
1550
|
+
if (!orgs || orgs.length === 0) {
|
|
1435
1551
|
throw new Error(
|
|
1436
1552
|
"No organization found for your account. Create one at https://admin.ollie.shop"
|
|
1437
1553
|
);
|
|
1438
1554
|
}
|
|
1439
|
-
|
|
1555
|
+
if (preferredOrgId) {
|
|
1556
|
+
const match = orgs.find((o) => o.id === preferredOrgId);
|
|
1557
|
+
if (!match) {
|
|
1558
|
+
const list = orgs.map((o) => ` - ${o.id} ${o.name}`).join("\n");
|
|
1559
|
+
throw new Error(
|
|
1560
|
+
`Organization ${preferredOrgId} not found or you are not a member. Orgs you can access:
|
|
1561
|
+
${list}`
|
|
1562
|
+
);
|
|
1563
|
+
}
|
|
1564
|
+
return match.id;
|
|
1565
|
+
}
|
|
1566
|
+
if (orgs.length > 1) {
|
|
1567
|
+
const list = orgs.map((o) => ` - ${o.id} ${o.name}`).join("\n");
|
|
1568
|
+
throw new Error(
|
|
1569
|
+
`You belong to multiple organizations. Pass --org <id> to select one:
|
|
1570
|
+
${list}`
|
|
1571
|
+
);
|
|
1572
|
+
}
|
|
1573
|
+
return orgs[0].id;
|
|
1440
1574
|
}
|
|
1441
1575
|
|
|
1442
1576
|
// src/utils/validate.ts
|
|
@@ -1663,8 +1797,11 @@ var componentListSchema = z2.object({
|
|
|
1663
1797
|
var functionCreateSchema = z2.object({
|
|
1664
1798
|
versionId: z2.string().uuid().describe("Parent version UUID"),
|
|
1665
1799
|
name: z2.string().min(1).describe("Function name"),
|
|
1666
|
-
trigger: z2.
|
|
1667
|
-
"
|
|
1800
|
+
trigger: z2.object({
|
|
1801
|
+
url: z2.string().min(1).describe("Trigger URL \u2014 absolute http(s) URL"),
|
|
1802
|
+
expression: z2.string().min(1).describe(`JSONata-like match expression (e.g. 'method in ["GET"]')`)
|
|
1803
|
+
}).optional().describe(
|
|
1804
|
+
"Function trigger \u2014 both url and expression are required together"
|
|
1668
1805
|
),
|
|
1669
1806
|
active: z2.boolean().default(false).describe("Whether function is active (default: false)"),
|
|
1670
1807
|
onError: z2.enum(["throw", "skip"]).optional().describe("Error handling: throw (default) or skip"),
|
|
@@ -2087,6 +2224,36 @@ async function listFunctions(client, storeId, versionId) {
|
|
|
2087
2224
|
|
|
2088
2225
|
// src/commands/function-cmd.ts
|
|
2089
2226
|
var ON_ERROR_VALUES = ["throw", "skip"];
|
|
2227
|
+
function parseTriggerFromFlags(urlFlag, expressionFlag) {
|
|
2228
|
+
if (urlFlag === void 0 && expressionFlag === void 0) return void 0;
|
|
2229
|
+
if (urlFlag === void 0 || expressionFlag === void 0) {
|
|
2230
|
+
throw new Error(
|
|
2231
|
+
"Both --trigger-url and --trigger-expression are required when configuring a trigger."
|
|
2232
|
+
);
|
|
2233
|
+
}
|
|
2234
|
+
return {
|
|
2235
|
+
url: validateTriggerUrl(urlFlag, "trigger-url"),
|
|
2236
|
+
expression: validateRequired(expressionFlag, "trigger-expression")
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
2239
|
+
function parseTriggerFromData(raw) {
|
|
2240
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
2241
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
2242
|
+
throw new Error(
|
|
2243
|
+
'Invalid trigger: must be an object with "url" and "expression" string properties.'
|
|
2244
|
+
);
|
|
2245
|
+
}
|
|
2246
|
+
const obj = raw;
|
|
2247
|
+
if (typeof obj.url !== "string" || typeof obj.expression !== "string") {
|
|
2248
|
+
throw new Error(
|
|
2249
|
+
'Invalid trigger: both "url" and "expression" must be non-empty strings.'
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
return {
|
|
2253
|
+
url: validateTriggerUrl(obj.url, "trigger.url"),
|
|
2254
|
+
expression: validateRequired(obj.expression, "trigger.expression")
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2090
2257
|
async function functionCommand(parsed2) {
|
|
2091
2258
|
const sub = parsed2.subcommand;
|
|
2092
2259
|
if (sub === "create") return functionCreateCommand(parsed2);
|
|
@@ -2105,13 +2272,14 @@ async function functionCreateCommand(parsed2) {
|
|
|
2105
2272
|
input = {
|
|
2106
2273
|
versionId: validateUuid(raw.versionId, "versionId"),
|
|
2107
2274
|
name: validateRequired(raw.name, "name"),
|
|
2108
|
-
trigger:
|
|
2275
|
+
trigger: parseTriggerFromData(raw.trigger),
|
|
2109
2276
|
active: raw.active === true,
|
|
2110
2277
|
onError: raw.onError !== void 0 && raw.onError !== null ? validateEnum(String(raw.onError), ON_ERROR_VALUES, "on-error") : "throw",
|
|
2111
2278
|
priority: raw.priority !== void 0 && raw.priority !== null ? validateInteger(raw.priority, "priority", { min: 0 }) : 0
|
|
2112
2279
|
};
|
|
2113
2280
|
} else {
|
|
2114
|
-
const
|
|
2281
|
+
const triggerUrlFlag = getFlag(parsed2.flags, "trigger-url");
|
|
2282
|
+
const triggerExpressionFlag = getFlag(parsed2.flags, "trigger-expression");
|
|
2115
2283
|
const onErrorFlag = getFlag(parsed2.flags, "on-error");
|
|
2116
2284
|
const priorityFlag = getFlag(parsed2.flags, "priority");
|
|
2117
2285
|
input = {
|
|
@@ -2120,7 +2288,7 @@ async function functionCreateCommand(parsed2) {
|
|
|
2120
2288
|
"version-id"
|
|
2121
2289
|
),
|
|
2122
2290
|
name: validateRequired(getFlag(parsed2.flags, "name", "n"), "name"),
|
|
2123
|
-
trigger:
|
|
2291
|
+
trigger: parseTriggerFromFlags(triggerUrlFlag, triggerExpressionFlag),
|
|
2124
2292
|
active: getBoolFlag(parsed2.flags, "active"),
|
|
2125
2293
|
onError: onErrorFlag !== void 0 ? validateEnum(onErrorFlag, ON_ERROR_VALUES, "on-error") : "throw",
|
|
2126
2294
|
priority: priorityFlag !== void 0 ? validateInteger(priorityFlag, "priority", { min: 0 }) : 0
|
|
@@ -2304,14 +2472,14 @@ async function statusCommand(parsed2) {
|
|
|
2304
2472
|
}
|
|
2305
2473
|
|
|
2306
2474
|
// src/core/store.ts
|
|
2307
|
-
async function createStore(client, input) {
|
|
2475
|
+
async function createStore(client, input, orgId) {
|
|
2308
2476
|
const parsed2 = storeCreateSchema.safeParse(input);
|
|
2309
2477
|
if (!parsed2.success) {
|
|
2310
2478
|
return {
|
|
2311
2479
|
error: { message: parsed2.error.issues.map((i) => i.message).join("; ") }
|
|
2312
2480
|
};
|
|
2313
2481
|
}
|
|
2314
|
-
const organizationId = await getOrganizationId(client);
|
|
2482
|
+
const organizationId = await getOrganizationId(client, orgId);
|
|
2315
2483
|
const { data, error } = await client.from("stores").insert({
|
|
2316
2484
|
name: parsed2.data.name,
|
|
2317
2485
|
platform: parsed2.data.platform,
|
|
@@ -2325,8 +2493,33 @@ async function createStore(client, input) {
|
|
|
2325
2493
|
}
|
|
2326
2494
|
return { data: { id: data.id } };
|
|
2327
2495
|
}
|
|
2328
|
-
async function
|
|
2329
|
-
const
|
|
2496
|
+
async function getStoreById(client, storeId) {
|
|
2497
|
+
const { data, error } = await client.from("stores").select("id, name, organization_id, organizations(id, name)").eq("id", storeId).maybeSingle();
|
|
2498
|
+
if (error) {
|
|
2499
|
+
return { error: { message: error.message } };
|
|
2500
|
+
}
|
|
2501
|
+
if (!data) {
|
|
2502
|
+
return {
|
|
2503
|
+
error: {
|
|
2504
|
+
message: `Store ${storeId} not found or you don't have access to it.`
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
const org = unwrapToOne(
|
|
2509
|
+
data.organizations
|
|
2510
|
+
);
|
|
2511
|
+
const orgName = org?.name ?? null;
|
|
2512
|
+
return {
|
|
2513
|
+
data: {
|
|
2514
|
+
id: data.id,
|
|
2515
|
+
name: data.name,
|
|
2516
|
+
organizationId: data.organization_id,
|
|
2517
|
+
organizationName: orgName
|
|
2518
|
+
}
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
async function listStores(client, orgId) {
|
|
2522
|
+
const organizationId = await getOrganizationId(client, orgId);
|
|
2330
2523
|
const { data, error } = await client.from("stores").select(
|
|
2331
2524
|
"id, name, platform, platform_store_id, organization_id, logo, settings, created_at"
|
|
2332
2525
|
).eq("organization_id", organizationId).order("created_at", { ascending: false });
|
|
@@ -2378,12 +2571,14 @@ async function storeCreateCommand(parsed2) {
|
|
|
2378
2571
|
settings: getFlag(parsed2.flags, "settings")
|
|
2379
2572
|
};
|
|
2380
2573
|
}
|
|
2574
|
+
const orgFlag = getFlag(parsed2.flags, "org");
|
|
2575
|
+
const orgId = orgFlag ? validateUuid(orgFlag, "org") : void 0;
|
|
2381
2576
|
if (parsed2.global.dryRun) {
|
|
2382
|
-
outputDryRun("store.create", input, format);
|
|
2577
|
+
outputDryRun("store.create", { ...input, orgId }, format);
|
|
2383
2578
|
return;
|
|
2384
2579
|
}
|
|
2385
2580
|
const client = await getAuthenticatedClient();
|
|
2386
|
-
const result = await createStore(client, input);
|
|
2581
|
+
const result = await createStore(client, input, orgId);
|
|
2387
2582
|
outputResult(result, format, parsed2.global.fields);
|
|
2388
2583
|
if (result.error) process.exit(1);
|
|
2389
2584
|
} catch (err) {
|
|
@@ -2399,8 +2594,10 @@ async function storeCreateCommand(parsed2) {
|
|
|
2399
2594
|
async function storeListCommand(parsed2) {
|
|
2400
2595
|
const format = detectOutputFormat(parsed2.global.output);
|
|
2401
2596
|
try {
|
|
2597
|
+
const orgFlag = getFlag(parsed2.flags, "org");
|
|
2598
|
+
const orgId = orgFlag ? validateUuid(orgFlag, "org") : void 0;
|
|
2402
2599
|
const client = await getAuthenticatedClient();
|
|
2403
|
-
const result = await listStores(client);
|
|
2600
|
+
const result = await listStores(client, orgId);
|
|
2404
2601
|
outputResult(result, format, parsed2.global.fields);
|
|
2405
2602
|
if (result.error) process.exit(1);
|
|
2406
2603
|
} catch (err) {
|
|
@@ -2524,21 +2721,71 @@ async function whoamiCommand(parsed2) {
|
|
|
2524
2721
|
if (!user) {
|
|
2525
2722
|
outputResult(
|
|
2526
2723
|
{
|
|
2527
|
-
error: {
|
|
2724
|
+
error: {
|
|
2725
|
+
message: "Not logged in or session expired. Run `ollieshop login`."
|
|
2726
|
+
}
|
|
2727
|
+
},
|
|
2728
|
+
format
|
|
2729
|
+
);
|
|
2730
|
+
process.exit(1);
|
|
2731
|
+
}
|
|
2732
|
+
try {
|
|
2733
|
+
const stage = resolveStage(parsed2.global.stage);
|
|
2734
|
+
const config = await loadConfig({ stage });
|
|
2735
|
+
if (!config?.storeId) {
|
|
2736
|
+
outputResult(
|
|
2737
|
+
{
|
|
2738
|
+
data: {
|
|
2739
|
+
email: user.email,
|
|
2740
|
+
hint: "No ollie.json found. Run `ollieshop init` to link a store."
|
|
2741
|
+
}
|
|
2742
|
+
},
|
|
2743
|
+
format,
|
|
2744
|
+
parsed2.global.fields
|
|
2745
|
+
);
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
const client = await getAuthenticatedClient();
|
|
2749
|
+
const result = await getStoreById(client, config.storeId);
|
|
2750
|
+
if (result.error || !result.data) {
|
|
2751
|
+
outputResult(
|
|
2752
|
+
{
|
|
2753
|
+
data: {
|
|
2754
|
+
email: user.email,
|
|
2755
|
+
storeId: config.storeId,
|
|
2756
|
+
store: null,
|
|
2757
|
+
note: result.error?.message ?? "Store not found. Run `ollieshop login` or check the storeId in ollie.json."
|
|
2758
|
+
}
|
|
2759
|
+
},
|
|
2760
|
+
format,
|
|
2761
|
+
parsed2.global.fields
|
|
2762
|
+
);
|
|
2763
|
+
return;
|
|
2764
|
+
}
|
|
2765
|
+
const store = result.data;
|
|
2766
|
+
outputResult(
|
|
2767
|
+
{
|
|
2768
|
+
data: {
|
|
2769
|
+
email: user.email,
|
|
2770
|
+
orgId: store.organizationId,
|
|
2771
|
+
org: store.organizationName,
|
|
2772
|
+
storeId: store.id,
|
|
2773
|
+
store: store.name,
|
|
2774
|
+
...config.versionId ? { versionId: config.versionId } : {}
|
|
2775
|
+
}
|
|
2776
|
+
},
|
|
2777
|
+
format,
|
|
2778
|
+
parsed2.global.fields
|
|
2779
|
+
);
|
|
2780
|
+
} catch (err) {
|
|
2781
|
+
outputResult(
|
|
2782
|
+
{
|
|
2783
|
+
error: { message: err instanceof Error ? err.message : String(err) }
|
|
2528
2784
|
},
|
|
2529
2785
|
format
|
|
2530
2786
|
);
|
|
2531
2787
|
process.exit(1);
|
|
2532
2788
|
}
|
|
2533
|
-
outputResult(
|
|
2534
|
-
{
|
|
2535
|
-
data: {
|
|
2536
|
-
email: user.email
|
|
2537
|
-
}
|
|
2538
|
-
},
|
|
2539
|
-
format,
|
|
2540
|
-
parsed2.global.fields
|
|
2541
|
-
);
|
|
2542
2789
|
}
|
|
2543
2790
|
|
|
2544
2791
|
// src/index.tsx
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ollie-shop/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Ollie Shop CLI - Development tools for custom checkouts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -13,18 +13,17 @@
|
|
|
13
13
|
"archiver": "^7.0.1",
|
|
14
14
|
"esbuild": "^0.24.0",
|
|
15
15
|
"glob": "^11.0.0",
|
|
16
|
-
"ink": "^
|
|
16
|
+
"ink": "^6.8.0",
|
|
17
17
|
"jwt-decode": "^4.0.0",
|
|
18
18
|
"open": "^10.1.0",
|
|
19
|
-
"react": "^
|
|
19
|
+
"react": "^19.2.0",
|
|
20
20
|
"zod": "^3.24.2",
|
|
21
21
|
"zod-to-json-schema": "^3.24.5"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/archiver": "^6.0.3",
|
|
25
|
-
"@types/ink": "^2.0.3",
|
|
26
25
|
"@types/node": "^22.15.23",
|
|
27
|
-
"@types/react": "^
|
|
26
|
+
"@types/react": "^19.2.0",
|
|
28
27
|
"tsup": "^8.0.1",
|
|
29
28
|
"typescript": "^5.7.3"
|
|
30
29
|
},
|