@karmaniverous/smoz 0.2.0 → 0.2.1

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/README.md CHANGED
@@ -18,17 +18,41 @@ SMOZ is a small, pragmatic toolkit for authoring AWS Lambda handlers with [Middy
18
18
  - HTTP middleware with validation, shaping, errors, CORS, negotiation, and HEAD
19
19
  - Non‑HTTP flows stay lean (no middleware overhead)
20
20
 
21
- ## Quick links
21
+ ## Quick start (from zero)
22
+
23
+ From an empty directory:
24
+
25
+ ```bash
26
+ npx @karmaniverous/smoz init -i
27
+ npx smoz dev -p 3000
28
+ ```
29
+
30
+ - The first command scaffolds a new app and installs dependencies (including a local `smoz` bin).
31
+ - The second command starts the inline local backend and keeps registers + OpenAPI fresh.
32
+ - Open http://localhost:3000/openapi in your browser.
22
33
 
34
+ Prefer `serverless‑offline`?
35
+
36
+ ```bash
37
+ npx smoz dev -l offline -p 3000
38
+ ```
39
+
40
+ Add your first endpoint (avoid clashing with the template’s hello):
41
+
42
+ ```bash
43
+ npx smoz add rest/foo/get
44
+ ```
45
+
46
+ ## Quick links
47
+
23
48
  - [Overview](https://docs.karmanivero.us/smoz/documents/Overview.html)
24
49
  - [Why smoz?](https://docs.karmanivero.us/smoz/documents/Why_smoz_.html)
25
50
  - [Getting started](https://docs.karmanivero.us/smoz/documents/Getting_started.html)
26
51
  - [10-minute tour](https://docs.karmanivero.us/smoz/documents/10%E2%80%91minute_tour.html)
27
- - [HTTP MIddleware](https://docs.karmanivero.us/smoz/documents/HTTP_middleware.html)
52
+ - [HTTP Middleware](https://docs.karmanivero.us/smoz/documents/HTTP_middleware.html)
28
53
  - [Recipes](https://docs.karmanivero.us/smoz/documents/Recipes.html)
29
54
  - [SQS function](https://docs.karmanivero.us/smoz/documents/Recipes.SQS_function.html)
30
- - [Contexts + Cognito authorizer](https://docs.karmanivero.us/smoz/documents/Recipes.Contexts_+_Cognito_authorizer.html)
31
- - [Custom middleware insertion](https://docs.karmanivero.us/smoz/documents/Recipes.Custom_middleware_insertion.html)
55
+ - [Contexts + Cognito authorizer](https://docs.karmanivero.us/smoz/documents/Recipes.Contexts_+_Cognito_authorizer.html) - [Custom middleware insertion](https://docs.karmanivero.us/smoz/documents/Recipes.Custom_middleware_insertion.html)
32
56
  - [Per‑function env](<https://docs.karmanivero.us/smoz/documents/Recipes.Per%E2%80%91function_env_(fnEnvKeys).html>)
33
57
  - [Observability](<https://docs.karmanivero.us/smoz/documents/Recipes.Observability_(requestId_header).html>)
34
58
  - [Troubleshooting](https://docs.karmanivero.us/smoz/documents/Recipes.Troubleshooting.html)
@@ -792,16 +792,6 @@ const launchInline = async (root, opts) => {
792
792
  return { close, restart };
793
793
  };
794
794
 
795
- /**
796
- * smoz init
797
- *
798
- * Scaffolds a new project from packaged templates.
799
- * - Copies ./templates/<template>/ into the target root (default: default)
800
- * - Seeds app/generated/register.*.ts (empty modules) if missing * - Idempotent: copy-if-absent; if a file exists, writes <name>.example alongside
801
- * - Additive merge of template manifest (deps/devDeps/scripts) into package.json
802
- * - Optional dependency installation via --install[=<pm>]
803
- */
804
- const toPosix = (p) => p.split(path.sep).join('/');
805
795
  const writeIfAbsent = async (outFile, content) => {
806
796
  if (fs.existsSync(outFile))
807
797
  return { created: false };
@@ -809,25 +799,6 @@ const writeIfAbsent = async (outFile, content) => {
809
799
  await fs.promises.writeFile(outFile, content, 'utf8');
810
800
  return { created: true };
811
801
  };
812
- const askConflict = async (rl, filePath) => {
813
- const q = `File exists: ${toPosix(filePath)}\n` +
814
- `Choose: [o]verwrite, [e]xample, [s]kip, [O]verwrite all, [E]xample all, [S]kip all: `;
815
- // Single key selection for simplicity
816
- const ans = (await rl.question(q)).trim();
817
- if (/^o$/.test(ans))
818
- return 'overwrite';
819
- if (/^e$/.test(ans))
820
- return 'example';
821
- if (/^s$/.test(ans))
822
- return 'skip';
823
- if (/^O$/.test(ans))
824
- return 'all-overwrite';
825
- if (/^E$/.test(ans))
826
- return 'all-example';
827
- if (/^S$/.test(ans))
828
- return 'all-skip';
829
- return 'example';
830
- };
831
802
  const walk = async (dir, out = []) => {
832
803
  const entries = await fs.promises.readdir(dir, { withFileTypes: true });
833
804
  for (const ent of entries) {
@@ -841,15 +812,6 @@ const walk = async (dir, out = []) => {
841
812
  }
842
813
  return out;
843
814
  };
844
- const resolveTemplatesBase = () => {
845
- // Resolve the templates folder from the CLI package install root,
846
- // not the caller’s project root. This makes --template <name> work
847
- // consistently whether smoz is run from a consuming app or this repo.
848
- // Anchor discovery to this module’s directory.
849
- const here = path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
850
- const pkgRoot = packageDirectory.packageDirectorySync({ cwd: here }) ?? process.cwd(); // conservative fallback
851
- return path.resolve(pkgRoot, 'templates');
852
- };
853
815
  const readJson = async (file) => {
854
816
  try {
855
817
  const data = await fs.promises.readFile(file, 'utf8');
@@ -863,6 +825,79 @@ const writeJson = async (file, obj) => {
863
825
  await fs.promises.mkdir(path.dirname(file), { recursive: true });
864
826
  await fs.promises.writeFile(file, JSON.stringify(obj, null, 2), 'utf8');
865
827
  };
828
+
829
+ const askConflict = async (rl, filePath) => {
830
+ const q = `File exists: ${filePath}\n` +
831
+ `Choose: [o]verwrite, [e]xample, [s]kip, ` +
832
+ `[O]verwrite all, [E]xample all, [S]kip all: `;
833
+ const ans = (await rl.question(q)).trim();
834
+ if (/^o$/.test(ans))
835
+ return 'overwrite';
836
+ if (/^e$/.test(ans))
837
+ return 'example';
838
+ if (/^s$/.test(ans))
839
+ return 'skip';
840
+ if (/^O$/.test(ans))
841
+ return 'all-overwrite';
842
+ if (/^E$/.test(ans))
843
+ return 'all-example';
844
+ if (/^S$/.test(ans))
845
+ return 'all-skip';
846
+ return 'example';
847
+ };
848
+ const copyDirWithConflicts = async (srcDir, dstRoot, created, skipped, examples, opts) => {
849
+ const files = await walk(srcDir);
850
+ let sticky;
851
+ for (const abs of files) {
852
+ const rel = path.relative(srcDir, abs);
853
+ const dest = path.resolve(dstRoot, rel);
854
+ const data = await fs.promises.readFile(abs, 'utf8');
855
+ if (!fs.existsSync(dest)) {
856
+ const { created: c } = await writeIfAbsent(dest, data);
857
+ if (c)
858
+ created.push(path.posix.normalize(dest));
859
+ else
860
+ skipped.push(path.posix.normalize(dest));
861
+ continue;
862
+ }
863
+ // Conflict flow
864
+ let decision = opts.conflict === 'ask' ? 'example' : opts.conflict;
865
+ if (opts.conflict === 'ask' && opts.rl && !sticky) {
866
+ const ans = await askConflict(opts.rl, path.posix.normalize(dest));
867
+ if (ans === 'all-overwrite') {
868
+ sticky = 'overwrite';
869
+ }
870
+ else if (ans === 'all-example') {
871
+ sticky = 'example';
872
+ }
873
+ else if (ans === 'all-skip') {
874
+ sticky = 'skip';
875
+ }
876
+ else {
877
+ decision = ans;
878
+ }
879
+ }
880
+ if (sticky)
881
+ decision = sticky;
882
+ if (decision === 'overwrite') {
883
+ await fs.promises.mkdir(path.dirname(dest), { recursive: true });
884
+ await fs.promises.writeFile(dest, data, 'utf8');
885
+ created.push(path.posix.normalize(dest));
886
+ }
887
+ else if (decision === 'example') {
888
+ const ex = `${dest}.example`;
889
+ const { created: c } = await writeIfAbsent(ex, data);
890
+ if (c)
891
+ examples.push(path.posix.normalize(ex));
892
+ else
893
+ skipped.push(path.posix.normalize(ex));
894
+ }
895
+ else {
896
+ skipped.push(path.posix.normalize(dest));
897
+ }
898
+ }
899
+ };
900
+
866
901
  const detectPm = (root) => {
867
902
  if (fs.existsSync(path.join(root, 'pnpm-lock.yaml')))
868
903
  return 'pnpm';
@@ -889,7 +924,6 @@ const runInstall = (root, pm) => {
889
924
  const known = pm === 'pnpm' || pm === 'yarn' || pm === 'bun' || pm === 'npm';
890
925
  if (!known)
891
926
  return 'unknown-pm';
892
- // Spawn with explicit args; avoid tuple inference that widens types.
893
927
  const res = node_child_process.spawnSync(pm, ['install'], {
894
928
  stdio: 'inherit',
895
929
  cwd: root,
@@ -907,10 +941,10 @@ const runInstall = (root, pm) => {
907
941
  }
908
942
  return 'failed';
909
943
  };
944
+
910
945
  const mergeAdditive = (target, source) => {
911
946
  const merged = [];
912
947
  const mergeKey = (key) => {
913
- // Allow possibly-undefined shapes to satisfy lint (no-unnecessary-condition).
914
948
  const src = source[key] ?? {};
915
949
  const dst = target[key] ?? {};
916
950
  const out = { ...dst };
@@ -948,54 +982,71 @@ const mergeAdditive = (target, source) => {
948
982
  target.scripts = scriptsOut;
949
983
  return merged;
950
984
  };
951
- const copyDirWithConflicts = async (srcDir, dstRoot, created, skipped, examples, opts) => {
952
- const files = await walk(srcDir);
953
- let sticky;
954
- for (const abs of files) {
955
- const rel = path.relative(srcDir, abs);
956
- const dest = path.resolve(dstRoot, rel);
957
- const data = await fs.promises.readFile(abs, 'utf8');
958
- if (!fs.existsSync(dest)) {
959
- const { created: c } = await writeIfAbsent(dest, data);
960
- if (c)
961
- created.push(path.posix.normalize(dest));
962
- else
963
- skipped.push(path.posix.normalize(dest));
964
- continue;
965
- }
966
- // Conflict
967
- let decision = opts.conflict === 'ask' ? 'example' : opts.conflict;
968
- if (opts.conflict === 'ask' && opts.rl && !sticky) {
969
- const ans = await askConflict(opts.rl, dest);
970
- if (ans === 'all-overwrite') {
971
- sticky = 'overwrite';
972
- }
973
- else if (ans === 'all-example') {
974
- sticky = 'example';
975
- }
976
- else if (ans === 'all-skip') {
977
- sticky = 'skip';
978
- }
979
- else {
980
- decision = ans;
981
- }
982
- }
983
- if (sticky)
984
- decision = sticky;
985
- if (decision === 'overwrite') {
986
- await fs.promises.writeFile(dest, data, 'utf8');
987
- created.push(path.posix.normalize(dest));
988
- }
989
- else if (decision === 'example') {
990
- const ex = `${dest}.example`;
991
- await writeIfAbsent(ex, data);
992
- examples.push(path.posix.normalize(ex));
993
- }
994
- else {
995
- skipped.push(path.posix.normalize(dest));
985
+ /**
986
+ * Ensure a runtime dependency on @karmaniverous/smoz is present in the
987
+ * target manifest, using a caret version derived from the toolkit package.
988
+ * Returns the merge descriptor string when added, else undefined.
989
+ */
990
+ const ensureToolkitDependency = async (targetPkg, templatesBase) => {
991
+ try {
992
+ const toolkitRoot = path.dirname(templatesBase);
993
+ const toolkitPkg = await readJson(path.join(toolkitRoot, 'package.json'));
994
+ const verRaw = toolkitPkg?.version?.trim();
995
+ const depVersion = verRaw ? `^${verRaw}` : '^0.0.0';
996
+ const deps = targetPkg.dependencies ?? {};
997
+ if (!deps['@karmaniverous/smoz']) {
998
+ targetPkg.dependencies ??=
999
+ {};
1000
+ targetPkg.dependencies['@karmaniverous/smoz'] = depVersion;
1001
+ return `dependencies:@karmaniverous/smoz@${depVersion}`;
996
1002
  }
997
1003
  }
1004
+ catch {
1005
+ // best-effort; ignore
1006
+ }
1007
+ return undefined;
998
1008
  };
1009
+
1010
+ /**
1011
+ + * Resolve the packaged templates root from the CLI install location,
1012
+ * not the caller's project root. This makes -t <name> work both from
1013
+ * a consuming app and from this repository.
1014
+ + */
1015
+ const resolveTemplatesBase = () => {
1016
+ const here = path.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
1017
+ const pkgRoot = packageDirectory.packageDirectorySync({ cwd: here }) ?? process.cwd();
1018
+ return path.resolve(pkgRoot, 'templates');
1019
+ };
1020
+ const toPosix = (p) =>
1021
+ // local helper for path presentation (not used for FS operations)
1022
+ p.replace(/\\/g, '/');
1023
+
1024
+ const seedRegisterPlaceholders = async (root) => {
1025
+ const genDir = path.join(root, 'app', 'generated');
1026
+ const seeds = [
1027
+ {
1028
+ path: path.join(genDir, 'register.functions.ts'),
1029
+ content: '/* AUTO-GENERATED placeholder; will be rewritten by `smoz register` */\nexport {};\n',
1030
+ },
1031
+ {
1032
+ path: path.join(genDir, 'register.openapi.ts'),
1033
+ content: '/* AUTO-GENERATED placeholder; will be rewritten by `smoz register` */\nexport {};\n',
1034
+ },
1035
+ {
1036
+ path: path.join(genDir, 'register.serverless.ts'),
1037
+ content: '/* AUTO-GENERATED placeholder; will be rewritten by `smoz register` */\nexport {};\n',
1038
+ },
1039
+ ];
1040
+ const created = [];
1041
+ const skipped = [];
1042
+ for (const s of seeds) {
1043
+ const { created: c } = await writeIfAbsent(s.path, s.content);
1044
+ (c ? created : skipped).push(path.posix.normalize(s.path));
1045
+ }
1046
+ return { created, skipped };
1047
+ };
1048
+
1049
+ const toPosixSep = (p) => p.split(path.sep).join('/');
999
1050
  const runInit = async (root, template = 'default', opts) => {
1000
1051
  const created = [];
1001
1052
  const skipped = [];
@@ -1003,7 +1054,7 @@ const runInit = async (root, template = 'default', opts) => {
1003
1054
  const merged = [];
1004
1055
  const optAll = opts ?? {};
1005
1056
  const templatesBase = resolveTemplatesBase();
1006
- // Resolve template source: named template (default/minimal/full) or filesystem path
1057
+ // Resolve template source: named template or filesystem path
1007
1058
  const templateIsPath = fs.existsSync(template) && (await fs.promises.stat(template)).isDirectory();
1008
1059
  const srcBase = templateIsPath
1009
1060
  ? path.resolve(template)
@@ -1012,9 +1063,9 @@ const runInit = async (root, template = 'default', opts) => {
1012
1063
  if (!fs.existsSync(srcBase)) {
1013
1064
  throw new Error(`Template "${template}" not found (path or name). Tried: ${toPosix(srcBase)}.`);
1014
1065
  }
1015
- // 1) Copy shared boilerplate (project) first (idempotent)
1066
+ // 1) Copy shared project boilerplate
1016
1067
  if (fs.existsSync(projectBase)) {
1017
- const rl = optAll.yes
1068
+ const rl = optAll.yes === true
1018
1069
  ? undefined
1019
1070
  : promises.createInterface({ input: node_process.stdin, output: node_process.stdout, terminal: true });
1020
1071
  let policy;
@@ -1027,12 +1078,11 @@ const runInit = async (root, template = 'default', opts) => {
1027
1078
  ? { conflict: policy, rl }
1028
1079
  : { conflict: policy };
1029
1080
  await copyDirWithConflicts(projectBase, root, created, skipped, examples, copyOpts);
1030
- if (rl)
1031
- rl.close();
1081
+ rl?.close();
1032
1082
  }
1033
1083
  // 2) Copy selected template
1034
1084
  {
1035
- const rl = optAll.yes
1085
+ const rl = optAll.yes === true
1036
1086
  ? undefined
1037
1087
  : promises.createInterface({ input: node_process.stdin, output: node_process.stdout, terminal: true });
1038
1088
  let policy;
@@ -1045,12 +1095,9 @@ const runInit = async (root, template = 'default', opts) => {
1045
1095
  ? { conflict: policy, rl }
1046
1096
  : { conflict: policy };
1047
1097
  await copyDirWithConflicts(srcBase, root, created, skipped, examples, copyOpts);
1048
- if (rl)
1049
- rl.close();
1098
+ rl?.close();
1050
1099
  }
1051
- // 2.5) Convert template 'gitignore' into real '.gitignore'
1052
- // NPM often excludes '.gitignore' from published packages; shipping 'gitignore'
1053
- // and converting here ensures downstream projects get a proper .gitignore.
1100
+ // 2.5) Convert template 'gitignore' into a real '.gitignore'
1054
1101
  try {
1055
1102
  const giSrc = path.join(root, 'gitignore');
1056
1103
  const giDot = path.join(root, '.gitignore');
@@ -1060,8 +1107,6 @@ const runInit = async (root, template = 'default', opts) => {
1060
1107
  created.push(path.posix.normalize(giDot));
1061
1108
  }
1062
1109
  else {
1063
- // Both exist: preserve the template as an example (if not already present),
1064
- // then remove the extra 'gitignore' to avoid clutter.
1065
1110
  const example = path.join(root, 'gitignore.example');
1066
1111
  if (!fs.existsSync(example)) {
1067
1112
  const data = await fs.promises.readFile(giSrc, 'utf8');
@@ -1073,36 +1118,19 @@ const runInit = async (root, template = 'default', opts) => {
1073
1118
  }
1074
1119
  }
1075
1120
  catch {
1076
- // best-effort; ignore conversion errors
1121
+ // best-effort
1077
1122
  }
1078
- // Seed app/generated/register.*.ts (empty modules) if missing
1079
- const genDir = path.resolve(root, 'app', 'generated');
1080
- const seeds = [
1081
- {
1082
- path: path.join(genDir, 'register.functions.ts'),
1083
- content: `/* AUTO-GENERATED placeholder; will be rewritten by \`smoz register\` */\nexport {};\n`,
1084
- },
1085
- {
1086
- path: path.join(genDir, 'register.openapi.ts'),
1087
- content: `/* AUTO-GENERATED placeholder; will be rewritten by \`smoz register\` */\nexport {};\n`,
1088
- },
1089
- {
1090
- path: path.join(genDir, 'register.serverless.ts'),
1091
- content: `/* AUTO-GENERATED placeholder; will be rewritten by \`smoz register\` */\nexport {};\n`,
1092
- },
1093
- ];
1094
- for (const s of seeds) {
1095
- const { created: c } = await writeIfAbsent(s.path, s.content);
1096
- if (c)
1097
- created.push(path.posix.normalize(s.path));
1098
- else
1099
- skipped.push(path.posix.normalize(s.path));
1123
+ // Seed app/generated/register.* placeholders
1124
+ {
1125
+ const res = await seedRegisterPlaceholders(root);
1126
+ created.push(...res.created);
1127
+ skipped.push(...res.skipped);
1100
1128
  }
1101
1129
  // 3) package.json presence (create when missing)
1102
1130
  const pkgPath = path.join(root, 'package.json');
1103
1131
  let pkg = await readJson(pkgPath);
1104
1132
  if (!pkg) {
1105
- const name = toPosix(root).split('/').pop() ?? 'smoz-app';
1133
+ const name = toPosixSep(root).split('/').pop() ?? 'smoz-app';
1106
1134
  pkg = {
1107
1135
  name,
1108
1136
  private: true,
@@ -1110,33 +1138,39 @@ const runInit = async (root, template = 'default', opts) => {
1110
1138
  version: '0.0.0',
1111
1139
  scripts: {},
1112
1140
  };
1113
- const dryRunCreate = Boolean(optAll.dryRun);
1114
- if (!dryRunCreate)
1141
+ if (!optAll.dryRun)
1115
1142
  await writeJson(pkgPath, pkg);
1116
1143
  created.push(path.posix.normalize(pkgPath));
1117
- } // 4) Merge manifest (deps/devDeps/scripts) additively
1118
- // Prefer a real package.json in the template; fallback to legacy manifests if absent.
1144
+ }
1145
+ // 4) Merge manifest additively (prefer template's embedded package.json)
1119
1146
  const templatePkgPath = path.resolve(srcBase, 'package.json');
1120
1147
  const manifest = fs.existsSync(templatePkgPath)
1121
1148
  ? await readJson(templatePkgPath)
1122
1149
  : await readJson(path.resolve(templatesBase, '.manifests', `package.${template}.json`));
1150
+ let pkgChanged = false;
1123
1151
  if (manifest) {
1124
- const before = JSON.stringify(pkg);
1125
1152
  const added = mergeAdditive(pkg, manifest);
1126
- merged.push(...added);
1127
- const dryRun = Boolean(optAll.dryRun);
1128
- if (!dryRun && before !== JSON.stringify(pkg)) {
1129
- await writeJson(pkgPath, pkg);
1153
+ if (added.length > 0) {
1154
+ pkgChanged = true;
1155
+ merged.push(...added);
1130
1156
  }
1131
1157
  }
1158
+ // 4.5) Ensure runtime dependency on @karmaniverous/smoz is present
1159
+ {
1160
+ const injected = await ensureToolkitDependency(pkg, templatesBase);
1161
+ if (injected) {
1162
+ merged.push(injected);
1163
+ pkgChanged = true;
1164
+ }
1165
+ }
1166
+ if (!optAll.dryRun && pkgChanged)
1167
+ await writeJson(pkgPath, pkg);
1132
1168
  // 5) Optional install
1133
1169
  let installed = 'skipped';
1134
- // Install policy:
1170
+ // Policy:
1135
1171
  // -y implies auto install unless --no-install
1136
1172
  const installOpt = optAll.install ?? false;
1137
1173
  const impliedAuto = optAll.yes === true && optAll.noInstall !== true && installOpt !== false;
1138
- // Derive package manager to use (explicit string or detected when true),
1139
- // then set installed based on presence of a PM.
1140
1174
  const hasInstallString = typeof installOpt === 'string' && installOpt.trim() !== '';
1141
1175
  const pm = hasInstallString
1142
1176
  ? installOpt
@@ -1144,13 +1178,7 @@ const runInit = async (root, template = 'default', opts) => {
1144
1178
  ? detectPm(root)
1145
1179
  : undefined;
1146
1180
  installed = pm ? runInstall(root, pm) : installed;
1147
- return {
1148
- created,
1149
- skipped,
1150
- examples,
1151
- merged,
1152
- installed,
1153
- };
1181
+ return { created, skipped, examples, merged, installed };
1154
1182
  };
1155
1183
 
1156
1184
  /**
package/package.json CHANGED
@@ -13,7 +13,6 @@
13
13
  "url": "https://github.com/karmaniverous/smoz/issues"
14
14
  },
15
15
  "dependencies": {
16
- "@karmaniverous/cached-axios": "^0.2.0",
17
16
  "@middy/core": "^6.4.5",
18
17
  "@middy/http-content-negotiation": "^6.4.5",
19
18
  "@middy/http-cors": "^6.4.5",
@@ -24,11 +23,11 @@
24
23
  "@middy/http-response-serializer": "^6.4.5",
25
24
  "aws-lambda": "^1.0.7",
26
25
  "chokidar": "^4.0.3",
27
- "commander": "^14.0.0",
26
+ "commander": "^14.0.1",
28
27
  "http-errors": "^2.0.0",
29
28
  "package-directory": "^8.1.0",
30
29
  "radash": "^12.1.1",
31
- "zod": "^4.1.6"
30
+ "zod": "^4.1.8"
32
31
  },
33
32
  "description": "John Galt Services back end.",
34
33
  "devDependencies": {
@@ -48,8 +47,7 @@
48
47
  "eslint-plugin-simple-import-sort": "^12.1.1",
49
48
  "fs-extra": "^11.3.1",
50
49
  "knip": "^5.63.1",
51
- "lefthook": "^1.12.4",
52
- "orval": "^7.11.2",
50
+ "lefthook": "^1.13.0",
53
51
  "pkg-dir": "^9.0.0",
54
52
  "prettier": "^3.6.2",
55
53
  "release-it": "^19.0.4",
@@ -167,7 +165,6 @@
167
165
  "docs": "typedoc",
168
166
  "domain:create": "serverless create_domain",
169
167
  "domain:delete": "serverless delete_domain",
170
- "generate": "cd services/activecampaign && orval",
171
168
  "knip": "knip",
172
169
  "lint": "eslint .",
173
170
  "lint:fix": "eslint --fix .",
@@ -185,5 +182,5 @@
185
182
  "templates:lint": "eslint --fix -c templates/default/eslint.config.ts \"templates/default/**/*.{ts,tsx,js,jsx}\" && eslint --fix templates/default/eslint.config.ts"
186
183
  },
187
184
  "type": "module",
188
- "version": "0.2.0"
185
+ "version": "0.2.1"
189
186
  }