@karmaniverous/smoz 0.1.8 → 0.2.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/dist/cli/index.cjs +191 -59
- package/package.json +2 -2
- package/templates/{minimal → default}/app/config/app.config.ts +50 -50
- package/templates/{minimal → default}/app/config/openapi.ts +45 -45
- package/templates/{full → default}/app/functions/rest/hello/get/handler.ts +18 -18
- package/templates/{full → default}/app/functions/rest/hello/get/lambda.ts +28 -28
- package/templates/{minimal → default}/app/functions/rest/hello/get/openapi.ts +23 -23
- package/templates/{minimal → default}/app/functions/rest/openapi/get/handler.ts +22 -22
- package/templates/{minimal → default}/app/functions/rest/openapi/get/lambda.ts +29 -29
- package/templates/{full → default}/app/functions/rest/openapi/get/openapi.ts +20 -20
- package/templates/{project → default}/eslint.config.ts +6 -15
- package/templates/default/package.json +44 -0
- package/templates/default/serverless.ts +113 -0
- package/templates/default/tsconfig.eslint.json +13 -0
- package/templates/{minimal → default}/types/registers.d.ts +11 -11
- package/templates/.check/eslint.minimal.config.ts +0 -41
- package/templates/.check/eslint.templates.config.ts +0 -60
- package/templates/.check/tsconfig.eslintconfig.json +0 -6
- package/templates/.check/tsconfig.minimal.json +0 -14
- package/templates/.manifests/package.full.json +0 -22
- package/templates/.manifests/package.minimal.json +0 -22
- package/templates/.manifests/package.project.json +0 -24
- package/templates/full/app/config/app.config.ts +0 -50
- package/templates/full/app/config/openapi.ts +0 -44
- package/templates/full/app/functions/rest/hello/get/openapi.ts +0 -19
- package/templates/full/app/functions/rest/openapi/get/handler.ts +0 -20
- package/templates/full/app/functions/rest/openapi/get/lambda.ts +0 -29
- package/templates/full/app/functions/sqs/tick/handler.ts +0 -7
- package/templates/full/app/functions/sqs/tick/lambda.ts +0 -24
- package/templates/full/app/functions/sqs/tick/serverless.ts +0 -8
- package/templates/full/serverless.ts +0 -33
- package/templates/full/tsconfig.json +0 -25
- package/templates/full/types/registers.d.ts +0 -11
- package/templates/minimal/app/functions/rest/hello/get/handler.ts +0 -18
- package/templates/minimal/app/functions/rest/hello/get/lambda.ts +0 -28
- package/templates/minimal/app/functions/rest/openapi/get/openapi.ts +0 -20
- package/templates/minimal/app/generated/openapi.json +0 -8
- package/templates/minimal/serverless.ts +0 -34
- package/templates/minimal/tsconfig.json +0 -25
- /package/templates/{project → default}/.prettierrc.json +0 -0
- /package/templates/{project → default}/.vscode/extensions.json +0 -0
- /package/templates/{project → default}/.vscode/settings.json +0 -0
- /package/templates/{project → default}/README.md +0 -0
- /package/templates/{full → default}/app/generated/openapi.json +0 -0
- /package/templates/{project → default}/gitignore +0 -0
- /package/templates/{project → default}/test/smoke.test.ts +0 -0
- /package/templates/{project → default}/tsconfig.base.json +0 -0
- /package/templates/{project → default}/tsconfig.json +0 -0
- /package/templates/{project → default}/tsdoc.json +0 -0
- /package/templates/{project → default}/typedoc.json +0 -0
- /package/templates/{project → default}/vitest.config.ts +0 -0
package/dist/cli/index.cjs
CHANGED
|
@@ -9,6 +9,8 @@ var node_url = require('node:url');
|
|
|
9
9
|
var chokidar = require('chokidar');
|
|
10
10
|
var node_child_process = require('node:child_process');
|
|
11
11
|
var os = require('node:os');
|
|
12
|
+
var node_process = require('node:process');
|
|
13
|
+
var promises = require('node:readline/promises');
|
|
12
14
|
|
|
13
15
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
14
16
|
/**
|
|
@@ -794,8 +796,7 @@ const launchInline = async (root, opts) => {
|
|
|
794
796
|
* smoz init
|
|
795
797
|
*
|
|
796
798
|
* Scaffolds a new project from packaged templates.
|
|
797
|
-
* - Copies ./templates
|
|
798
|
-
* - Copies ./templates/<template>/ into the target root (default: minimal)
|
|
799
|
+
* - Copies ./templates/<template>/ into the target root (default: default)
|
|
799
800
|
* - Seeds app/generated/register.*.ts (empty modules) if missing * - Idempotent: copy-if-absent; if a file exists, writes <name>.example alongside
|
|
800
801
|
* - Additive merge of template manifest (deps/devDeps/scripts) into package.json
|
|
801
802
|
* - Optional dependency installation via --install[=<pm>]
|
|
@@ -808,6 +809,25 @@ const writeIfAbsent = async (outFile, content) => {
|
|
|
808
809
|
await fs.promises.writeFile(outFile, content, 'utf8');
|
|
809
810
|
return { created: true };
|
|
810
811
|
};
|
|
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
|
+
};
|
|
811
831
|
const walk = async (dir, out = []) => {
|
|
812
832
|
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
813
833
|
for (const ent of entries) {
|
|
@@ -928,48 +948,106 @@ const mergeAdditive = (target, source) => {
|
|
|
928
948
|
target.scripts = scriptsOut;
|
|
929
949
|
return merged;
|
|
930
950
|
};
|
|
931
|
-
const
|
|
951
|
+
const copyDirWithConflicts = async (srcDir, dstRoot, created, skipped, examples, opts) => {
|
|
932
952
|
const files = await walk(srcDir);
|
|
953
|
+
let sticky;
|
|
933
954
|
for (const abs of files) {
|
|
934
955
|
const rel = path.relative(srcDir, abs);
|
|
935
956
|
const dest = path.resolve(dstRoot, rel);
|
|
936
957
|
const data = await fs.promises.readFile(abs, 'utf8');
|
|
937
|
-
if (fs.existsSync(dest)) {
|
|
938
|
-
const ex = `${dest}.example`;
|
|
939
|
-
if (!fs.existsSync(ex)) {
|
|
940
|
-
await writeIfAbsent(ex, data);
|
|
941
|
-
examples.push(path.posix.normalize(ex));
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
skipped.push(path.posix.normalize(dest));
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
else {
|
|
958
|
+
if (!fs.existsSync(dest)) {
|
|
948
959
|
const { created: c } = await writeIfAbsent(dest, data);
|
|
949
960
|
if (c)
|
|
950
961
|
created.push(path.posix.normalize(dest));
|
|
951
962
|
else
|
|
952
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));
|
|
953
996
|
}
|
|
954
997
|
}
|
|
955
998
|
};
|
|
956
|
-
const runInit = async (root, template = '
|
|
999
|
+
const runInit = async (root, template = 'default', opts) => {
|
|
957
1000
|
const created = [];
|
|
958
1001
|
const skipped = [];
|
|
959
1002
|
const examples = [];
|
|
960
1003
|
const merged = [];
|
|
1004
|
+
const optAll = opts ?? {};
|
|
961
1005
|
const templatesBase = resolveTemplatesBase();
|
|
962
|
-
|
|
1006
|
+
// Resolve template source: named template (default/minimal/full) or filesystem path
|
|
1007
|
+
const templateIsPath = fs.existsSync(template) && (await fs.promises.stat(template)).isDirectory();
|
|
1008
|
+
const srcBase = templateIsPath
|
|
1009
|
+
? path.resolve(template)
|
|
1010
|
+
: path.resolve(templatesBase, template);
|
|
963
1011
|
const projectBase = path.resolve(templatesBase, 'project');
|
|
964
1012
|
if (!fs.existsSync(srcBase)) {
|
|
965
|
-
throw new Error(`Template "${template}" not found
|
|
1013
|
+
throw new Error(`Template "${template}" not found (path or name). Tried: ${toPosix(srcBase)}.`);
|
|
966
1014
|
}
|
|
967
1015
|
// 1) Copy shared boilerplate (project) first (idempotent)
|
|
968
1016
|
if (fs.existsSync(projectBase)) {
|
|
969
|
-
|
|
1017
|
+
const rl = optAll.yes
|
|
1018
|
+
? undefined
|
|
1019
|
+
: promises.createInterface({ input: node_process.stdin, output: node_process.stdout, terminal: true });
|
|
1020
|
+
let policy;
|
|
1021
|
+
const c = optAll.conflict;
|
|
1022
|
+
if (c === 'overwrite' || c === 'example' || c === 'skip' || c === 'ask')
|
|
1023
|
+
policy = c;
|
|
1024
|
+
else
|
|
1025
|
+
policy = optAll.yes ? 'example' : 'ask';
|
|
1026
|
+
const copyOpts = rl
|
|
1027
|
+
? { conflict: policy, rl }
|
|
1028
|
+
: { conflict: policy };
|
|
1029
|
+
await copyDirWithConflicts(projectBase, root, created, skipped, examples, copyOpts);
|
|
1030
|
+
if (rl)
|
|
1031
|
+
rl.close();
|
|
970
1032
|
}
|
|
971
1033
|
// 2) Copy selected template
|
|
972
|
-
|
|
1034
|
+
{
|
|
1035
|
+
const rl = optAll.yes
|
|
1036
|
+
? undefined
|
|
1037
|
+
: promises.createInterface({ input: node_process.stdin, output: node_process.stdout, terminal: true });
|
|
1038
|
+
let policy;
|
|
1039
|
+
const c = optAll.conflict;
|
|
1040
|
+
if (c === 'overwrite' || c === 'example' || c === 'skip' || c === 'ask')
|
|
1041
|
+
policy = c;
|
|
1042
|
+
else
|
|
1043
|
+
policy = optAll.yes ? 'example' : 'ask';
|
|
1044
|
+
const copyOpts = rl
|
|
1045
|
+
? { conflict: policy, rl }
|
|
1046
|
+
: { conflict: policy };
|
|
1047
|
+
await copyDirWithConflicts(srcBase, root, created, skipped, examples, copyOpts);
|
|
1048
|
+
if (rl)
|
|
1049
|
+
rl.close();
|
|
1050
|
+
}
|
|
973
1051
|
// 2.5) Convert template 'gitignore' into real '.gitignore'
|
|
974
1052
|
// NPM often excludes '.gitignore' from published packages; shipping 'gitignore'
|
|
975
1053
|
// and converting here ensures downstream projects get a proper .gitignore.
|
|
@@ -1020,55 +1098,59 @@ const runInit = async (root, template = 'minimal', opts) => {
|
|
|
1020
1098
|
else
|
|
1021
1099
|
skipped.push(path.posix.normalize(s.path));
|
|
1022
1100
|
}
|
|
1023
|
-
// 3) package.json presence (
|
|
1101
|
+
// 3) package.json presence (create when missing)
|
|
1024
1102
|
const pkgPath = path.join(root, 'package.json');
|
|
1025
1103
|
let pkg = await readJson(pkgPath);
|
|
1026
1104
|
if (!pkg) {
|
|
1027
|
-
const
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
// 4) Merge manifest (deps/devDeps/scripts) additively
|
|
1048
|
-
const manifestPath = path.resolve(templatesBase, '.manifests', `package.${template}.json`);
|
|
1049
|
-
const manifest = await readJson(manifestPath);
|
|
1105
|
+
const name = toPosix(root).split('/').pop() ?? 'smoz-app';
|
|
1106
|
+
pkg = {
|
|
1107
|
+
name,
|
|
1108
|
+
private: true,
|
|
1109
|
+
type: 'module',
|
|
1110
|
+
version: '0.0.0',
|
|
1111
|
+
scripts: {},
|
|
1112
|
+
};
|
|
1113
|
+
const dryRunCreate = Boolean(optAll.dryRun);
|
|
1114
|
+
if (!dryRunCreate)
|
|
1115
|
+
await writeJson(pkgPath, pkg);
|
|
1116
|
+
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.
|
|
1119
|
+
const templatePkgPath = path.resolve(srcBase, 'package.json');
|
|
1120
|
+
const manifest = fs.existsSync(templatePkgPath)
|
|
1121
|
+
? await readJson(templatePkgPath)
|
|
1122
|
+
: await readJson(path.resolve(templatesBase, '.manifests', `package.${template}.json`));
|
|
1050
1123
|
if (manifest) {
|
|
1051
1124
|
const before = JSON.stringify(pkg);
|
|
1052
1125
|
const added = mergeAdditive(pkg, manifest);
|
|
1053
1126
|
merged.push(...added);
|
|
1054
|
-
const dryRun =
|
|
1127
|
+
const dryRun = Boolean(optAll.dryRun);
|
|
1055
1128
|
if (!dryRun && before !== JSON.stringify(pkg)) {
|
|
1056
1129
|
await writeJson(pkgPath, pkg);
|
|
1057
1130
|
}
|
|
1058
1131
|
}
|
|
1059
1132
|
// 5) Optional install
|
|
1060
1133
|
let installed = 'skipped';
|
|
1061
|
-
|
|
1134
|
+
// Install policy:
|
|
1135
|
+
// -y implies auto install unless --no-install
|
|
1136
|
+
const installOpt = optAll.install ?? false;
|
|
1137
|
+
const impliedAuto = optAll.yes === true && optAll.noInstall !== true && installOpt !== false;
|
|
1062
1138
|
// Derive package manager to use (explicit string or detected when true),
|
|
1063
1139
|
// then set installed based on presence of a PM.
|
|
1064
1140
|
const hasInstallString = typeof installOpt === 'string' && installOpt.trim() !== '';
|
|
1065
1141
|
const pm = hasInstallString
|
|
1066
1142
|
? installOpt
|
|
1067
|
-
: installOpt === true
|
|
1143
|
+
: installOpt === true || impliedAuto
|
|
1068
1144
|
? detectPm(root)
|
|
1069
1145
|
: undefined;
|
|
1070
1146
|
installed = pm ? runInstall(root, pm) : installed;
|
|
1071
|
-
return {
|
|
1147
|
+
return {
|
|
1148
|
+
created,
|
|
1149
|
+
skipped,
|
|
1150
|
+
examples,
|
|
1151
|
+
merged,
|
|
1152
|
+
installed,
|
|
1153
|
+
};
|
|
1072
1154
|
};
|
|
1073
1155
|
|
|
1074
1156
|
/**
|
|
@@ -1089,6 +1171,22 @@ const readPkg = (root) => {
|
|
|
1089
1171
|
return {};
|
|
1090
1172
|
}
|
|
1091
1173
|
};
|
|
1174
|
+
const readSmozConfig = (root) => {
|
|
1175
|
+
try {
|
|
1176
|
+
const p = path.join(root, 'smoz.config.json');
|
|
1177
|
+
if (!fs.existsSync(p))
|
|
1178
|
+
return {};
|
|
1179
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
1180
|
+
const parsed = JSON.parse(raw);
|
|
1181
|
+
if (parsed && typeof parsed === 'object') {
|
|
1182
|
+
return parsed;
|
|
1183
|
+
}
|
|
1184
|
+
return {};
|
|
1185
|
+
}
|
|
1186
|
+
catch {
|
|
1187
|
+
return {};
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1092
1190
|
const detectPackageManager = () => {
|
|
1093
1191
|
const ua = process.env.npm_config_user_agent ?? '';
|
|
1094
1192
|
if (ua.includes('pnpm'))
|
|
@@ -1134,7 +1232,8 @@ const main = () => {
|
|
|
1134
1232
|
program
|
|
1135
1233
|
.name('smoz')
|
|
1136
1234
|
.description('SMOZ CLI')
|
|
1137
|
-
|
|
1235
|
+
// Add -v alias for version in addition to default --version behavior
|
|
1236
|
+
.version(pkg.version ?? '0.0.0', '-v, --version', 'output the version');
|
|
1138
1237
|
program
|
|
1139
1238
|
.command('add')
|
|
1140
1239
|
.argument('<spec>', 'Add function: HTTP <eventType>/<segments...>/<method> or non-HTTP <eventType>/<segments...>')
|
|
@@ -1155,16 +1254,46 @@ const main = () => {
|
|
|
1155
1254
|
});
|
|
1156
1255
|
program
|
|
1157
1256
|
.command('init')
|
|
1158
|
-
.description('Scaffold a new SMOZ app from packaged templates (default:
|
|
1159
|
-
.option('--template <
|
|
1160
|
-
.option('--init', 'Create a minimal package.json if missing')
|
|
1257
|
+
.description('Scaffold a new SMOZ app from packaged templates (default: default)')
|
|
1258
|
+
.option('-t, --template <nameOrPath>', 'Template name or directory path', 'default')
|
|
1161
1259
|
.option('-i, --install [pm]', 'Install dependencies (optionally specify pm: npm|pnpm|yarn|bun)')
|
|
1162
|
-
.option('--
|
|
1260
|
+
.option('--no-install', 'Skip dependency installation (overrides -y)', false)
|
|
1261
|
+
.option('-y, --yes', 'Skip prompts (non-interactive)', false)
|
|
1163
1262
|
.option('--dry-run', 'Show planned actions without writing', false)
|
|
1164
1263
|
.action(async (opts) => {
|
|
1165
1264
|
try {
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1265
|
+
const cfg = readSmozConfig(root).cliDefaults?.init ?? {};
|
|
1266
|
+
// Resolve template: CLI > config > default
|
|
1267
|
+
const tpl = typeof opts.template === 'string'
|
|
1268
|
+
? opts.template
|
|
1269
|
+
: typeof cfg.template === 'string'
|
|
1270
|
+
? cfg.template
|
|
1271
|
+
: 'default';
|
|
1272
|
+
// Resolve install behavior: CLI > --no-install > config
|
|
1273
|
+
let install = opts.install;
|
|
1274
|
+
if (opts.noInstall === true) {
|
|
1275
|
+
install = false;
|
|
1276
|
+
}
|
|
1277
|
+
else if (install === undefined) {
|
|
1278
|
+
const d = cfg.install;
|
|
1279
|
+
install =
|
|
1280
|
+
d === 'auto'
|
|
1281
|
+
? true
|
|
1282
|
+
: d === 'none'
|
|
1283
|
+
? false
|
|
1284
|
+
: typeof d === 'string'
|
|
1285
|
+
? d
|
|
1286
|
+
: undefined;
|
|
1287
|
+
}
|
|
1288
|
+
const conflict = typeof opts.conflict === 'string' ? opts.conflict : cfg.onConflict;
|
|
1289
|
+
const { created, skipped, examples, merged, installed } = await runInit(root, tpl, {
|
|
1290
|
+
// Include only when defined to satisfy exactOptionalPropertyTypes
|
|
1291
|
+
...(install !== undefined ? { install } : {}),
|
|
1292
|
+
...(typeof conflict === 'string' ? { conflict } : {}),
|
|
1293
|
+
yes: opts.yes === true,
|
|
1294
|
+
noInstall: opts.noInstall === true,
|
|
1295
|
+
dryRun: opts.dryRun === true,
|
|
1296
|
+
});
|
|
1168
1297
|
console.log([
|
|
1169
1298
|
created.length
|
|
1170
1299
|
? `Created:\n - ${created.join('\n - ')}`
|
|
@@ -1220,14 +1349,17 @@ const main = () => {
|
|
|
1220
1349
|
.option('-v, --verbose', 'Verbose logging', false)
|
|
1221
1350
|
.action(async (opts) => {
|
|
1222
1351
|
try {
|
|
1352
|
+
const cfg = readSmozConfig(root).cliDefaults?.dev ?? {};
|
|
1353
|
+
// Resolve local mode default from config if not provided
|
|
1354
|
+
const localResolved = typeof opts.local === 'string'
|
|
1355
|
+
? opts.local
|
|
1356
|
+
: opts.local === false
|
|
1357
|
+
? false
|
|
1358
|
+
: (cfg.local ?? 'inline');
|
|
1223
1359
|
await runDev(root, {
|
|
1224
1360
|
register: opts.register !== false,
|
|
1225
1361
|
openapi: opts.openapi !== false,
|
|
1226
|
-
local:
|
|
1227
|
-
? opts.local
|
|
1228
|
-
: opts.local === false
|
|
1229
|
-
? false
|
|
1230
|
-
: 'inline',
|
|
1362
|
+
local: localResolved,
|
|
1231
1363
|
...(typeof opts.stage === 'string' ? { stage: opts.stage } : {}),
|
|
1232
1364
|
port: opts.port ?? 0,
|
|
1233
1365
|
verbose: !!opts.verbose,
|
package/package.json
CHANGED
|
@@ -182,8 +182,8 @@
|
|
|
182
182
|
"test": "vitest run",
|
|
183
183
|
"typecheck": "tsx src/cli/index.ts register && tsc -p tsconfig.json --noEmit",
|
|
184
184
|
"templates:typecheck": "tsx scripts/templates-typecheck.ts",
|
|
185
|
-
"templates:lint": "eslint --fix -c templates
|
|
185
|
+
"templates:lint": "eslint --fix -c templates/default/eslint.config.ts \"templates/default/**/*.{ts,tsx,js,jsx}\" && eslint --fix templates/default/eslint.config.ts"
|
|
186
186
|
},
|
|
187
187
|
"type": "module",
|
|
188
|
-
"version": "0.
|
|
188
|
+
"version": "0.2.0"
|
|
189
189
|
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { fileURLToPath } from 'node:url';
|
|
3
|
-
|
|
4
|
-
import { App, baseEventTypeMapSchema, toPosixPath } from '@karmaniverous/smoz';
|
|
5
|
-
import { z } from 'zod';
|
|
6
|
-
|
|
7
|
-
// Derive the app root as the parent directory of app/config/
|
|
8
|
-
export const APP_ROOT_ABS = toPosixPath(
|
|
9
|
-
fileURLToPath(new URL('..', import.meta.url)),
|
|
10
|
-
);
|
|
11
|
-
|
|
12
|
-
export const app = App.create({
|
|
13
|
-
appRootAbs: APP_ROOT_ABS,
|
|
14
|
-
globalParamsSchema: z.object({
|
|
15
|
-
PROFILE: z.string(),
|
|
16
|
-
REGION: z.string(),
|
|
17
|
-
SERVICE_NAME: z.string(),
|
|
18
|
-
}),
|
|
19
|
-
stageParamsSchema: z.object({
|
|
20
|
-
STAGE: z.string(),
|
|
21
|
-
}),
|
|
22
|
-
eventTypeMapSchema: baseEventTypeMapSchema,
|
|
23
|
-
serverless: {
|
|
24
|
-
httpContextEventMap: {
|
|
25
|
-
my: {}, // place a Cognito authorizer here if needed
|
|
26
|
-
private: { private: true },
|
|
27
|
-
public: {},
|
|
28
|
-
},
|
|
29
|
-
defaultHandlerFileName: 'handler',
|
|
30
|
-
defaultHandlerFileExport: 'handler',
|
|
31
|
-
},
|
|
32
|
-
global: {
|
|
33
|
-
params: {
|
|
34
|
-
PROFILE: 'dev',
|
|
35
|
-
REGION: 'us-east-1',
|
|
36
|
-
SERVICE_NAME: 'my-smoz-app',
|
|
37
|
-
},
|
|
38
|
-
envKeys: ['PROFILE', 'REGION', 'SERVICE_NAME'] as const,
|
|
39
|
-
},
|
|
40
|
-
stage: {
|
|
41
|
-
params: {
|
|
42
|
-
dev: { STAGE: 'dev' },
|
|
43
|
-
},
|
|
44
|
-
envKeys: ['STAGE'] as const,
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
export const ENDPOINTS_ROOT_REST = toPosixPath(
|
|
49
|
-
join(APP_ROOT_ABS, 'functions', 'rest'),
|
|
50
|
-
);
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { App, baseEventTypeMapSchema, toPosixPath } from '@karmaniverous/smoz';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
|
|
7
|
+
// Derive the app root as the parent directory of app/config/
|
|
8
|
+
export const APP_ROOT_ABS = toPosixPath(
|
|
9
|
+
fileURLToPath(new URL('..', import.meta.url)),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export const app = App.create({
|
|
13
|
+
appRootAbs: APP_ROOT_ABS,
|
|
14
|
+
globalParamsSchema: z.object({
|
|
15
|
+
PROFILE: z.string(),
|
|
16
|
+
REGION: z.string(),
|
|
17
|
+
SERVICE_NAME: z.string(),
|
|
18
|
+
}),
|
|
19
|
+
stageParamsSchema: z.object({
|
|
20
|
+
STAGE: z.string(),
|
|
21
|
+
}),
|
|
22
|
+
eventTypeMapSchema: baseEventTypeMapSchema,
|
|
23
|
+
serverless: {
|
|
24
|
+
httpContextEventMap: {
|
|
25
|
+
my: {}, // place a Cognito authorizer here if needed
|
|
26
|
+
private: { private: true },
|
|
27
|
+
public: {},
|
|
28
|
+
},
|
|
29
|
+
defaultHandlerFileName: 'handler',
|
|
30
|
+
defaultHandlerFileExport: 'handler',
|
|
31
|
+
},
|
|
32
|
+
global: {
|
|
33
|
+
params: {
|
|
34
|
+
PROFILE: 'dev',
|
|
35
|
+
REGION: 'us-east-1',
|
|
36
|
+
SERVICE_NAME: 'my-smoz-app',
|
|
37
|
+
},
|
|
38
|
+
envKeys: ['PROFILE', 'REGION', 'SERVICE_NAME'] as const,
|
|
39
|
+
},
|
|
40
|
+
stage: {
|
|
41
|
+
params: {
|
|
42
|
+
dev: { STAGE: 'dev' },
|
|
43
|
+
},
|
|
44
|
+
envKeys: ['STAGE'] as const,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export const ENDPOINTS_ROOT_REST = toPosixPath(
|
|
49
|
+
join(APP_ROOT_ABS, 'functions', 'rest'),
|
|
50
|
+
);
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import * as path from 'node:path';
|
|
2
|
-
|
|
3
|
-
import * as fs from 'fs-extra';
|
|
4
|
-
import { packageDirectorySync } from 'pkg-dir';
|
|
5
|
-
import { createDocument } from 'zod-openapi';
|
|
6
|
-
|
|
7
|
-
import { app } from '@/app/config/app.config';
|
|
8
|
-
/**
|
|
9
|
-
* Template note:
|
|
10
|
-
* - Templates do NOT commit generated register files under app/generated; they
|
|
11
|
-
* are declared via ambient types (templates/minimal/types/registers.d.ts) so
|
|
12
|
-
* TypeScript can typecheck without artifacts.
|
|
13
|
-
* - To ensure side effects still run (endpoint registration) and to satisfy
|
|
14
|
-
* noUncheckedSideEffectImports, import the register module as a namespace and
|
|
15
|
-
* reference it via `void`.
|
|
16
|
-
* - In real apps, `smoz init` seeds placeholders and `smoz register` rewrites
|
|
17
|
-
* app/generated/register.*.ts at author time.
|
|
18
|
-
*/
|
|
19
|
-
import * as __register_openapi from '@/app/generated/register.openapi';
|
|
20
|
-
void __register_openapi;
|
|
21
|
-
console.log('Generating OpenAPI document...');
|
|
22
|
-
|
|
23
|
-
const paths = app.buildAllOpenApiPaths();
|
|
24
|
-
export const doc = createDocument({
|
|
25
|
-
openapi: '3.1.0',
|
|
26
|
-
servers: [{ description: 'Dev', url: 'http://localhost' }],
|
|
27
|
-
info: {
|
|
28
|
-
title: process.env.npm_package_name ?? 'smoz-app',
|
|
29
|
-
version: process.env.npm_package_version ?? '0.0.0',
|
|
30
|
-
},
|
|
31
|
-
paths,
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const pkgDir = packageDirectorySync();
|
|
35
|
-
if (!pkgDir) {
|
|
36
|
-
throw new Error('Could not resolve package root directory');
|
|
37
|
-
}
|
|
38
|
-
const outDir = path.join(pkgDir, 'app', 'generated');
|
|
39
|
-
fs.ensureDirSync(outDir);
|
|
40
|
-
fs.writeFileSync(
|
|
41
|
-
path.join(outDir, 'openapi.json'),
|
|
42
|
-
JSON.stringify(doc, null, 2),
|
|
43
|
-
);
|
|
44
|
-
|
|
45
|
-
console.log('Done!');
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import * as fs from 'fs-extra';
|
|
4
|
+
import { packageDirectorySync } from 'pkg-dir';
|
|
5
|
+
import { createDocument } from 'zod-openapi';
|
|
6
|
+
|
|
7
|
+
import { app } from '@/app/config/app.config';
|
|
8
|
+
/**
|
|
9
|
+
* Template note:
|
|
10
|
+
* - Templates do NOT commit generated register files under app/generated; they
|
|
11
|
+
* are declared via ambient types (templates/minimal/types/registers.d.ts) so
|
|
12
|
+
* TypeScript can typecheck without artifacts.
|
|
13
|
+
* - To ensure side effects still run (endpoint registration) and to satisfy
|
|
14
|
+
* noUncheckedSideEffectImports, import the register module as a namespace and
|
|
15
|
+
* reference it via `void`.
|
|
16
|
+
* - In real apps, `smoz init` seeds placeholders and `smoz register` rewrites
|
|
17
|
+
* app/generated/register.*.ts at author time.
|
|
18
|
+
*/
|
|
19
|
+
import * as __register_openapi from '@/app/generated/register.openapi';
|
|
20
|
+
void __register_openapi;
|
|
21
|
+
console.log('Generating OpenAPI document...');
|
|
22
|
+
|
|
23
|
+
const paths = app.buildAllOpenApiPaths();
|
|
24
|
+
export const doc = createDocument({
|
|
25
|
+
openapi: '3.1.0',
|
|
26
|
+
servers: [{ description: 'Dev', url: 'http://localhost' }],
|
|
27
|
+
info: {
|
|
28
|
+
title: process.env.npm_package_name ?? 'smoz-app',
|
|
29
|
+
version: process.env.npm_package_version ?? '0.0.0',
|
|
30
|
+
},
|
|
31
|
+
paths,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const pkgDir = packageDirectorySync();
|
|
35
|
+
if (!pkgDir) {
|
|
36
|
+
throw new Error('Could not resolve package root directory');
|
|
37
|
+
}
|
|
38
|
+
const outDir = path.join(pkgDir, 'app', 'generated');
|
|
39
|
+
fs.ensureDirSync(outDir);
|
|
40
|
+
fs.writeFileSync(
|
|
41
|
+
path.join(outDir, 'openapi.json'),
|
|
42
|
+
JSON.stringify(doc, null, 2),
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
console.log('Done!');
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import type { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
import type { responseSchema } from './lambda';
|
|
4
|
-
import { fn } from './lambda';
|
|
5
|
-
|
|
6
|
-
type Response = z.infer<typeof responseSchema>;
|
|
7
|
-
|
|
8
|
-
type FnHandlerApi<T> = {
|
|
9
|
-
handler: (impl: () => Promise<T> | T) => (...args: unknown[]) => Promise<T>;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const reg = fn as unknown as FnHandlerApi<Response>;
|
|
13
|
-
|
|
14
|
-
export const handler = reg.handler(async () => {
|
|
15
|
-
const res: Response = { ok: true };
|
|
16
|
-
await Promise.resolve(); // satisfy require-await without adding complexity
|
|
17
|
-
return res;
|
|
18
|
-
});
|
|
1
|
+
import type { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import type { responseSchema } from './lambda';
|
|
4
|
+
import { fn } from './lambda';
|
|
5
|
+
|
|
6
|
+
type Response = z.infer<typeof responseSchema>;
|
|
7
|
+
|
|
8
|
+
type FnHandlerApi<T> = {
|
|
9
|
+
handler: (impl: () => Promise<T> | T) => (...args: unknown[]) => Promise<T>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const reg = fn as unknown as FnHandlerApi<Response>;
|
|
13
|
+
|
|
14
|
+
export const handler = reg.handler(async () => {
|
|
15
|
+
const res: Response = { ok: true };
|
|
16
|
+
await Promise.resolve(); // satisfy require-await without adding complexity
|
|
17
|
+
return res;
|
|
18
|
+
});
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
|
|
3
|
-
import { toPosixPath } from '@karmaniverous/smoz';
|
|
4
|
-
import { z } from 'zod';
|
|
5
|
-
|
|
6
|
-
import { app, APP_ROOT_ABS } from '@/app/config/app.config';
|
|
7
|
-
|
|
8
|
-
export const eventSchema = z.object({}).optional();
|
|
9
|
-
export const responseSchema = z.object({ ok: z.boolean() });
|
|
10
|
-
type FnApi = {
|
|
11
|
-
handler: <T>(
|
|
12
|
-
impl: () => Promise<T> | T,
|
|
13
|
-
) => (...args: unknown[]) => Promise<T>;
|
|
14
|
-
openapi: (op: unknown) => void;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const fn = app.defineFunction({
|
|
18
|
-
functionName: 'hello_get',
|
|
19
|
-
eventType: 'rest',
|
|
20
|
-
httpContexts: ['public'],
|
|
21
|
-
method: 'get',
|
|
22
|
-
basePath: 'hello',
|
|
23
|
-
contentType: 'application/json',
|
|
24
|
-
eventSchema,
|
|
25
|
-
responseSchema,
|
|
26
|
-
callerModuleUrl: import.meta.url,
|
|
27
|
-
endpointsRootAbs: toPosixPath(join(APP_ROOT_ABS, 'functions', 'rest')),
|
|
28
|
-
}) as unknown as FnApi;
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { toPosixPath } from '@karmaniverous/smoz';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
|
|
6
|
+
import { app, APP_ROOT_ABS } from '@/app/config/app.config';
|
|
7
|
+
|
|
8
|
+
export const eventSchema = z.object({}).optional();
|
|
9
|
+
export const responseSchema = z.object({ ok: z.boolean() });
|
|
10
|
+
type FnApi = {
|
|
11
|
+
handler: <T>(
|
|
12
|
+
impl: () => Promise<T> | T,
|
|
13
|
+
) => (...args: unknown[]) => Promise<T>;
|
|
14
|
+
openapi: (op: unknown) => void;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const fn = app.defineFunction({
|
|
18
|
+
functionName: 'hello_get',
|
|
19
|
+
eventType: 'rest',
|
|
20
|
+
httpContexts: ['public'],
|
|
21
|
+
method: 'get',
|
|
22
|
+
basePath: 'hello',
|
|
23
|
+
contentType: 'application/json',
|
|
24
|
+
eventSchema,
|
|
25
|
+
responseSchema,
|
|
26
|
+
callerModuleUrl: import.meta.url,
|
|
27
|
+
endpointsRootAbs: toPosixPath(join(APP_ROOT_ABS, 'functions', 'rest')),
|
|
28
|
+
}) as unknown as FnApi;
|