@zintrust/core 0.1.18 → 0.1.19

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.
Files changed (122) hide show
  1. package/README.md +6 -0
  2. package/package.json +10 -1
  3. package/public/index.html +1 -1
  4. package/routes/api.d.ts +7 -0
  5. package/routes/api.d.ts.map +1 -0
  6. package/routes/api.js +91 -0
  7. package/routes/broadcast.d.ts +9 -0
  8. package/routes/broadcast.d.ts.map +1 -0
  9. package/routes/broadcast.js +27 -0
  10. package/routes/health.d.ts +7 -0
  11. package/routes/health.d.ts.map +1 -0
  12. package/routes/health.js +127 -0
  13. package/routes/storage.d.ts +4 -0
  14. package/routes/storage.d.ts.map +1 -0
  15. package/routes/storage.js +35 -0
  16. package/src/boot/Application.js +1 -1
  17. package/src/boot/bootstrap.js +1 -1
  18. package/src/cli/CLI.d.ts.map +1 -1
  19. package/src/cli/CLI.js +2 -0
  20. package/src/cli/PromptHelper.d.ts.map +1 -1
  21. package/src/cli/PromptHelper.js +4 -3
  22. package/src/cli/commands/NewCommand.d.ts +1 -1
  23. package/src/cli/commands/NewCommand.d.ts.map +1 -1
  24. package/src/cli/commands/NewCommand.js +26 -9
  25. package/src/cli/commands/SimulateCommand.js +1 -1
  26. package/src/cli/commands/StartCommand.d.ts.map +1 -1
  27. package/src/cli/commands/StartCommand.js +90 -3
  28. package/src/cli/commands/UpgradeCommand.d.ts +16 -0
  29. package/src/cli/commands/UpgradeCommand.d.ts.map +1 -0
  30. package/src/cli/commands/UpgradeCommand.js +107 -0
  31. package/src/cli/commands/runner/index.d.ts +3 -0
  32. package/src/cli/commands/runner/index.d.ts.map +1 -0
  33. package/src/cli/commands/runner/index.js +139 -0
  34. package/src/cli/env/EnvFileBackfill.d.ts +10 -0
  35. package/src/cli/env/EnvFileBackfill.d.ts.map +1 -0
  36. package/src/cli/env/EnvFileBackfill.js +64 -0
  37. package/src/cli/scaffolding/ProjectScaffolder.d.ts.map +1 -1
  38. package/src/cli/scaffolding/ProjectScaffolder.js +22 -59
  39. package/src/cli/utils/DistPackager.d.ts.map +1 -1
  40. package/src/cli/utils/DistPackager.js +8 -0
  41. package/src/config/broadcast.js +1 -1
  42. package/src/config/database.d.ts +6 -0
  43. package/src/config/database.d.ts.map +1 -1
  44. package/src/config/database.js +7 -1
  45. package/src/config/index.d.ts +7 -1
  46. package/src/config/index.d.ts.map +1 -1
  47. package/src/config/middleware.d.ts +2 -1
  48. package/src/config/middleware.d.ts.map +1 -1
  49. package/src/config/middleware.js +47 -11
  50. package/src/config/notification.js +1 -1
  51. package/src/config/storage.js +1 -1
  52. package/src/config/type.d.ts +7 -1
  53. package/src/config/type.d.ts.map +1 -1
  54. package/src/index.d.ts +1 -0
  55. package/src/index.d.ts.map +1 -1
  56. package/src/middleware/RateLimiter.d.ts.map +1 -1
  57. package/src/middleware/RateLimiter.js +26 -1
  58. package/src/node.d.ts +1 -1
  59. package/src/node.d.ts.map +1 -1
  60. package/src/node.js +4 -1
  61. package/src/orm/DatabaseRuntimeRegistration.d.ts.map +1 -1
  62. package/src/orm/DatabaseRuntimeRegistration.js +4 -0
  63. package/src/orm/QueryBuilder.d.ts.map +1 -1
  64. package/src/orm/QueryBuilder.js +7 -3
  65. package/src/orm/adapters/SQLiteAdapter.d.ts.map +1 -1
  66. package/src/orm/adapters/SQLiteAdapter.js +5 -1
  67. package/src/routes/api.d.ts +2 -0
  68. package/src/routes/api.d.ts.map +1 -0
  69. package/src/routes/api.js +1 -0
  70. package/src/routes/broadcast.d.ts +2 -0
  71. package/src/routes/broadcast.d.ts.map +1 -0
  72. package/src/routes/broadcast.js +1 -0
  73. package/src/routes/health.d.ts +2 -0
  74. package/src/routes/health.d.ts.map +1 -0
  75. package/src/routes/health.js +1 -0
  76. package/src/routes/storage.d.ts +2 -0
  77. package/src/routes/storage.d.ts.map +1 -0
  78. package/src/routes/storage.js +1 -0
  79. package/src/runtime/RuntimeAdapter.d.ts.map +1 -1
  80. package/src/runtime/RuntimeAdapter.js +20 -1
  81. package/src/runtime/adapters/DenoAdapter.js +2 -2
  82. package/src/scripts/TemplateImportsCheck.js +7 -7
  83. package/src/scripts/TemplateSync.js +6 -0
  84. package/src/start.d.ts +21 -0
  85. package/src/start.d.ts.map +1 -0
  86. package/src/start.js +60 -0
  87. package/src/templates/features/Queue.ts.tpl +2 -3
  88. package/src/templates/project/basic/.env.example.tpl +1 -1
  89. package/src/templates/project/basic/app/Controllers/UserController.ts.tpl +2 -4
  90. package/src/templates/project/basic/app/Middleware/ProfilerMiddleware.ts.tpl +1 -3
  91. package/src/templates/project/basic/app/Middleware/index.ts.tpl +3 -8
  92. package/src/templates/project/basic/app/Models/Post.ts.tpl +2 -3
  93. package/src/templates/project/basic/app/Models/User.ts.tpl +1 -1
  94. package/src/templates/project/basic/config/FileLogWriter.ts.tpl +1 -1
  95. package/src/templates/project/basic/config/SecretsManager.ts.tpl +2 -2
  96. package/src/templates/project/basic/config/StartupConfigValidator.ts.tpl +2 -2
  97. package/src/templates/project/basic/config/app.ts.tpl +3 -3
  98. package/src/templates/project/basic/config/broadcast.ts.tpl +4 -5
  99. package/src/templates/project/basic/config/cache.ts.tpl +2 -3
  100. package/src/templates/project/basic/config/cloudflare.ts.tpl +1 -1
  101. package/src/templates/project/basic/config/database.ts.tpl +9 -3
  102. package/src/templates/project/basic/config/env.ts.tpl +1 -1
  103. package/src/templates/project/basic/config/features.ts.tpl +2 -2
  104. package/src/templates/project/basic/config/index.ts.tpl +38 -20
  105. package/src/templates/project/basic/config/logger.ts.tpl +5 -381
  106. package/src/templates/project/basic/config/logging/HttpLogger.ts.tpl +1 -1
  107. package/src/templates/project/basic/config/logging/KvLogger.ts.tpl +2 -2
  108. package/src/templates/project/basic/config/logging/SlackLogger.ts.tpl +1 -1
  109. package/src/templates/project/basic/config/mail.ts.tpl +2 -3
  110. package/src/templates/project/basic/config/microservices.ts.tpl +1 -1
  111. package/src/templates/project/basic/config/middleware.ts.tpl +40 -13
  112. package/src/templates/project/basic/config/notification.ts.tpl +3 -4
  113. package/src/templates/project/basic/config/queue.ts.tpl +2 -2
  114. package/src/templates/project/basic/config/security.ts.tpl +3 -3
  115. package/src/templates/project/basic/config/startup.ts.tpl +1 -1
  116. package/src/templates/project/basic/config/storage.ts.tpl +3 -4
  117. package/src/templates/project/basic/config/type.ts.tpl +12 -2
  118. package/src/templates/project/basic/package.json.tpl +1 -1
  119. package/src/templates/project/basic/routes/api.ts.tpl +4 -4
  120. package/src/templates/project/basic/routes/health.ts.tpl +1 -6
  121. package/src/templates/project/basic/src/index.ts.tpl +7 -80
  122. package/src/templates/project/basic/tsconfig.json.tpl +0 -2
@@ -1,9 +1,10 @@
1
1
  import { BaseCommand } from '../BaseCommand.js';
2
+ import { DENO_RUNNER_SOURCE, LAMBDA_RUNNER_SOURCE } from '../commands/runner/index.js';
2
3
  import { EnvFileLoader } from '../utils/EnvFileLoader.js';
3
4
  import { SpawnUtil } from '../utils/spawn.js';
4
5
  import { resolveNpmPath } from '../../common/index.js';
5
6
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
6
- import { existsSync, readFileSync } from '../../node-singletons/fs.js';
7
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
7
8
  import * as path from '../../node-singletons/path.js';
8
9
  const isValidModeInput = (value) => value === 'development' ||
9
10
  value === 'dev' ||
@@ -59,6 +60,22 @@ const resolveRuntime = (options) => {
59
60
  const raw = typeof options.runtime === 'string' ? options.runtime.trim() : '';
60
61
  return raw === '' ? undefined : raw;
61
62
  };
63
+ const resolveStartVariant = (options) => {
64
+ const wantWrangler = options.wrangler === true || options.wg === true;
65
+ const wantDeno = options.deno === true;
66
+ const wantLambda = options.lambda === true;
67
+ const enabled = [wantWrangler, wantDeno, wantLambda].filter(Boolean).length;
68
+ if (enabled > 1) {
69
+ throw ErrorFactory.createCliError('Error: Choose only one of --wrangler/--wg, --deno, or --lambda.');
70
+ }
71
+ if (wantWrangler)
72
+ return 'wrangler';
73
+ if (wantDeno)
74
+ return 'deno';
75
+ if (wantLambda)
76
+ return 'lambda';
77
+ return 'node';
78
+ };
62
79
  const hasFlag = (flag) => process.argv.includes(flag);
63
80
  const resolveWatchPreference = (options, mode) => {
64
81
  const hasWatch = hasFlag('--watch');
@@ -104,6 +121,10 @@ const findWranglerConfig = (cwd) => {
104
121
  return undefined;
105
122
  };
106
123
  const resolveWranglerEntry = (cwd) => {
124
+ const indexEntry = path.join(cwd, 'src/index.ts');
125
+ if (existsSync(indexEntry))
126
+ return 'src/index.ts';
127
+ // Legacy fallback
107
128
  const entry = path.join(cwd, 'src/functions/cloudflare.ts');
108
129
  return existsSync(entry) ? 'src/functions/cloudflare.ts' : undefined;
109
130
  };
@@ -167,6 +188,55 @@ const executeWranglerStart = async (cmd, cwd, port, runtime) => {
167
188
  const exitCode = await SpawnUtil.spawnAndWait({ command: 'wrangler', args: wranglerArgs });
168
189
  process.exit(exitCode);
169
190
  };
191
+ const ensureTmpRunnerFile = (cwd, filename, content) => {
192
+ const tmpDir = path.join(cwd, 'tmp');
193
+ try {
194
+ mkdirSync(tmpDir, { recursive: true });
195
+ }
196
+ catch (error) {
197
+ throw ErrorFactory.createTryCatchError('Failed to create tmp directory', error);
198
+ }
199
+ const fullPath = path.join(tmpDir, filename);
200
+ try {
201
+ writeFileSync(fullPath, content, 'utf-8');
202
+ }
203
+ catch (error) {
204
+ throw ErrorFactory.createTryCatchError('Failed to write start runner', error);
205
+ }
206
+ return fullPath;
207
+ };
208
+ const executeDenoStart = async (cmd, cwd, mode, watchEnabled, _port, runtime) => {
209
+ if (runtime !== undefined) {
210
+ throw ErrorFactory.createCliError('Error: --runtime cannot be used with --deno.');
211
+ }
212
+ if (mode === 'testing') {
213
+ throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use development or production.');
214
+ }
215
+ const denoRunner = ensureTmpRunnerFile(cwd, 'zin-start-deno.ts', DENO_RUNNER_SOURCE);
216
+ const args = [];
217
+ if (mode === 'development' && watchEnabled)
218
+ args.push('watch');
219
+ args.push(denoRunner);
220
+ cmd.info('Starting in Deno adapter mode...');
221
+ const exitCode = await SpawnUtil.spawnAndWait({ command: 'tsx', args });
222
+ process.exit(exitCode);
223
+ };
224
+ const executeLambdaStart = async (cmd, cwd, mode, watchEnabled, _port, runtime) => {
225
+ if (runtime !== undefined) {
226
+ throw ErrorFactory.createCliError('Error: --runtime cannot be used with --lambda.');
227
+ }
228
+ if (mode === 'testing') {
229
+ throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use development or production.');
230
+ }
231
+ const lambdaRunner = ensureTmpRunnerFile(cwd, 'zin-start-lambda.ts', LAMBDA_RUNNER_SOURCE);
232
+ const args = [];
233
+ if (mode === 'development' && watchEnabled)
234
+ args.push('watch');
235
+ args.push(lambdaRunner);
236
+ cmd.info('Starting in Lambda adapter mode...');
237
+ const exitCode = await SpawnUtil.spawnAndWait({ command: 'tsx', args });
238
+ process.exit(exitCode);
239
+ };
170
240
  const executeNodeStart = async (cmd, cwd, mode, watchEnabled) => {
171
241
  if (mode === 'testing') {
172
242
  throw ErrorFactory.createCliError('Error: Cannot start server in testing mode. Use --force to override (not supported).');
@@ -196,12 +266,26 @@ const executeStart = async (options, cmd) => {
196
266
  const mode = resolveMode(options);
197
267
  const port = resolvePort(options);
198
268
  const runtime = resolveRuntime(options);
199
- EnvFileLoader.applyCliOverrides({ nodeEnv: mode, port, runtime });
200
- if (options.wrangler === true) {
269
+ const variant = resolveStartVariant(options);
270
+ let effectiveRuntime = runtime;
271
+ if (variant === 'deno')
272
+ effectiveRuntime = 'deno';
273
+ if (variant === 'lambda')
274
+ effectiveRuntime = 'lambda';
275
+ EnvFileLoader.applyCliOverrides({ nodeEnv: mode, port, runtime: effectiveRuntime });
276
+ if (variant === 'wrangler') {
201
277
  await executeWranglerStart(cmd, cwd, port, runtime);
202
278
  return;
203
279
  }
204
280
  const watchEnabled = resolveWatchPreference(options, mode);
281
+ if (variant === 'deno') {
282
+ await executeDenoStart(cmd, cwd, mode, watchEnabled, port, runtime);
283
+ return;
284
+ }
285
+ if (variant === 'lambda') {
286
+ await executeLambdaStart(cmd, cwd, mode, watchEnabled, port, runtime);
287
+ return;
288
+ }
205
289
  await executeNodeStart(cmd, cwd, mode, watchEnabled);
206
290
  };
207
291
  export const StartCommand = Object.freeze({
@@ -210,6 +294,9 @@ export const StartCommand = Object.freeze({
210
294
  command.alias('s');
211
295
  command
212
296
  .option('-w, --wrangler', 'Start with Wrangler dev mode (Cloudflare Workers)')
297
+ .option('--wg', 'Alias for --wrangler')
298
+ .option('--deno', 'Start a local server using the Deno runtime adapter')
299
+ .option('--lambda', 'Start a local server using the AWS Lambda runtime adapter')
213
300
  .option('--watch', 'Force watch mode (Node only)')
214
301
  .option('--no-watch', 'Disable watch mode (Node only)')
215
302
  .option('--mode <development|production|testing>', 'Override app mode')
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Upgrade Command
3
+ *
4
+ * Goal: help existing Zintrust projects adopt new safe defaults without forcing
5
+ * a full re-scaffold.
6
+ *
7
+ * Current scope (minimal/safe):
8
+ * - Backfill required .env defaults (HOST, PORT, LOG_LEVEL) when missing or blank.
9
+ * - Never overwrite a non-empty user value.
10
+ */
11
+ import { type IBaseCommand } from '../BaseCommand';
12
+ export declare const UpgradeCommand: Readonly<{
13
+ create(): IBaseCommand;
14
+ }>;
15
+ export default UpgradeCommand;
16
+ //# sourceMappingURL=UpgradeCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpgradeCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/UpgradeCommand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAoFvF,eAAO,MAAM,cAAc;cACf,YAAY;EA6BtB,CAAC;AAEH,eAAe,cAAc,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Upgrade Command
3
+ *
4
+ * Goal: help existing Zintrust projects adopt new safe defaults without forcing
5
+ * a full re-scaffold.
6
+ *
7
+ * Current scope (minimal/safe):
8
+ * - Backfill required .env defaults (HOST, PORT, LOG_LEVEL) when missing or blank.
9
+ * - Never overwrite a non-empty user value.
10
+ */
11
+ import { BaseCommand } from '../BaseCommand.js';
12
+ import { ErrorHandler } from '../ErrorHandler.js';
13
+ import { EnvFileBackfill } from '../env/EnvFileBackfill.js';
14
+ import { mkdirSync, readFileSync, unlinkSync, writeFileSync } from '../../node-singletons/fs.js';
15
+ import * as path from '../../node-singletons/path.js';
16
+ const ensureEnvFileExists = (envPath) => {
17
+ const dir = path.dirname(envPath);
18
+ mkdirSync(dir, { recursive: true });
19
+ try {
20
+ readFileSync(envPath, 'utf8');
21
+ }
22
+ catch {
23
+ writeFileSync(envPath, '\n', 'utf8');
24
+ }
25
+ };
26
+ const resolveCwd = (cwd) => {
27
+ if (typeof cwd === 'string' && cwd.trim() !== '')
28
+ return cwd;
29
+ return process.cwd();
30
+ };
31
+ const getEnvDefaults = () => Object.freeze({
32
+ HOST: 'localhost',
33
+ PORT: '7777',
34
+ LOG_LEVEL: 'debug',
35
+ });
36
+ const envFileExists = (envPath) => {
37
+ try {
38
+ readFileSync(envPath, 'utf8');
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ };
45
+ const formatBackfillSummary = (result) => {
46
+ const filled = result.filledKeys.join(', ') || '(none)';
47
+ const appended = result.appendedKeys.join(', ') || '(none)';
48
+ return `filled=${filled}; appended=${appended}`;
49
+ };
50
+ const runDryRun = (envPath, defaults) => {
51
+ const exists = envFileExists(envPath);
52
+ if (exists === false) {
53
+ ErrorHandler.info(`[dry-run] Would create ${envPath}`);
54
+ return;
55
+ }
56
+ const raw = readFileSync(envPath, 'utf8');
57
+ const tmpPath = `${envPath}.zin-upgrade.tmp`;
58
+ writeFileSync(tmpPath, raw, 'utf8');
59
+ try {
60
+ const result = EnvFileBackfill.backfillEnvDefaults(tmpPath, defaults);
61
+ if (result.changed === false) {
62
+ ErrorHandler.success('No changes needed.');
63
+ return;
64
+ }
65
+ ErrorHandler.info(`[dry-run] Would backfill .env: ${formatBackfillSummary(result)}`);
66
+ }
67
+ finally {
68
+ try {
69
+ unlinkSync(tmpPath);
70
+ }
71
+ catch {
72
+ // ignore
73
+ }
74
+ }
75
+ };
76
+ const runUpgrade = (envPath, defaults) => {
77
+ ensureEnvFileExists(envPath);
78
+ return EnvFileBackfill.backfillEnvDefaults(envPath, defaults);
79
+ };
80
+ export const UpgradeCommand = Object.freeze({
81
+ create() {
82
+ return BaseCommand.create({
83
+ name: 'upgrade',
84
+ description: 'Upgrade an existing Zintrust project in-place (safe, non-destructive)',
85
+ addOptions: (command) => {
86
+ command.option('--cwd <path>', 'Project directory (default: current working directory)');
87
+ command.option('--dry-run', 'Print planned changes without writing files');
88
+ },
89
+ execute: (options) => {
90
+ const cwd = resolveCwd(options.cwd);
91
+ const envPath = path.resolve(cwd, '.env');
92
+ const defaults = getEnvDefaults();
93
+ if (options.dryRun === true) {
94
+ runDryRun(envPath, defaults);
95
+ return;
96
+ }
97
+ const result = runUpgrade(envPath, defaults);
98
+ if (result.changed === false) {
99
+ ErrorHandler.success('No changes needed.');
100
+ return;
101
+ }
102
+ ErrorHandler.success(`Upgraded .env: ${formatBackfillSummary(result)}`);
103
+ },
104
+ });
105
+ },
106
+ });
107
+ export default UpgradeCommand;
@@ -0,0 +1,3 @@
1
+ export declare const DENO_RUNNER_SOURCE: string;
2
+ export declare const LAMBDA_RUNNER_SOURCE: string;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/runner/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,QAwDnB,CAAC;AAEb,eAAO,MAAM,oBAAoB,QAiFrB,CAAC"}
@@ -0,0 +1,139 @@
1
+ export const DENO_RUNNER_SOURCE = [
2
+ "import http from 'node:http';",
3
+ "import { deno } from '@zintrust/core/start';",
4
+ '',
5
+ "const port = Number.parseInt(process.env.PORT ?? '3000', 10) || 3000;",
6
+ "const host = process.env.HOST ?? 'localhost';",
7
+ '',
8
+ 'const readBody = async (req: http.IncomingMessage): Promise<Buffer | undefined> => {',
9
+ " if (req.method === 'GET' || req.method === 'HEAD') return undefined;",
10
+ ' const chunks: Buffer[] = [];',
11
+ ' for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));',
12
+ ' return chunks.length > 0 ? Buffer.concat(chunks) : undefined;',
13
+ '};',
14
+ '',
15
+ 'const server = http.createServer(async (req, res) => {',
16
+ ' try {',
17
+ " const proto = (req.headers['x-forwarded-proto'] ?? 'http').toString();",
18
+ ' const authority = (req.headers.host ?? host).toString();',
19
+ " const url = new URL(req.url ?? '/', proto + '://' + authority);",
20
+ '',
21
+ ' const headers = new Headers();',
22
+ ' for (const [key, value] of Object.entries(req.headers)) {',
23
+ ' if (value === undefined) continue;',
24
+ ' if (Array.isArray(value)) {',
25
+ ' for (const v of value) headers.append(key, v);',
26
+ ' } else {',
27
+ ' headers.set(key, String(value));',
28
+ ' }',
29
+ ' }',
30
+ '',
31
+ ' const body = await readBody(req);',
32
+ ' const request = new Request(url.toString(), {',
33
+ " method: req.method ?? 'GET',",
34
+ ' headers,',
35
+ ' body,',
36
+ ' });',
37
+ '',
38
+ ' const response = await deno(request);',
39
+ '',
40
+ ' res.statusCode = response.status;',
41
+ ' response.headers.forEach((v, k) => res.setHeader(k, v));',
42
+ '',
43
+ ' const ab = await response.arrayBuffer();',
44
+ ' res.end(Buffer.from(ab));',
45
+ ' } catch (err) {',
46
+ ' res.statusCode = 500;',
47
+ " res.setHeader('content-type', 'text/plain');",
48
+ " res.end('Internal Server Error' + (err as Error).message);",
49
+ ' }',
50
+ '});',
51
+ '',
52
+ 'server.listen(port, host, () => {',
53
+ ' // eslint-disable-next-line no-console',
54
+ " console.log('Deno adapter server listening at http://' + host + ':' + port);",
55
+ '});',
56
+ '',
57
+ ].join('\n');
58
+ export const LAMBDA_RUNNER_SOURCE = [
59
+ "import http from 'node:http';",
60
+ "import { handler as lambdaHandler } from '@zintrust/core/start';",
61
+ '',
62
+ "const port = Number.parseInt(process.env.PORT ?? '3000', 10) || 3000;",
63
+ "const host = process.env.HOST ?? 'localhost';",
64
+ '',
65
+ 'const readBody = async (req: http.IncomingMessage): Promise<string | undefined> => {',
66
+ " if (req.method === 'GET' || req.method === 'HEAD') return undefined;",
67
+ ' const chunks: Buffer[] = [];',
68
+ ' for await (const chunk of req) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));',
69
+ " return chunks.length > 0 ? Buffer.concat(chunks).toString('utf-8') : undefined;",
70
+ '};',
71
+ '',
72
+ 'const server = http.createServer(async (req, res) => {',
73
+ ' try {',
74
+ " const proto = (req.headers['x-forwarded-proto'] ?? 'http').toString();",
75
+ ' const authority = (req.headers.host ?? host).toString();',
76
+ " const url = new URL(req.url ?? '/', proto + '://' + authority);",
77
+ '',
78
+ ' const body = await readBody(req);',
79
+ ' const headers: Record<string, string> = {};',
80
+ ' for (const [k, v] of Object.entries(req.headers)) {',
81
+ ' if (v === undefined) continue;',
82
+ " headers[k] = Array.isArray(v) ? v.join(',') : String(v);",
83
+ ' }',
84
+ '',
85
+ ' const queryStringParameters: Record<string, string> = {};',
86
+ ' url.searchParams.forEach((value, key) => {',
87
+ ' if (queryStringParameters[key] === undefined) queryStringParameters[key] = value;',
88
+ ' });',
89
+ '',
90
+ ' const event = {',
91
+ " version: '2.0',",
92
+ " routeKey: '$default',",
93
+ ' rawPath: url.pathname,',
94
+ ' rawQueryString: url.searchParams.toString(),',
95
+ ' headers,',
96
+ ' queryStringParameters,',
97
+ ' requestContext: {',
98
+ ' http: {',
99
+ " method: req.method ?? 'GET',",
100
+ ' path: url.pathname,',
101
+ " sourceIp: (req.socket.remoteAddress ?? '').toString(),",
102
+ ' },',
103
+ ' },',
104
+ ' body: body ?? null,',
105
+ ' isBase64Encoded: false,',
106
+ ' };',
107
+ '',
108
+ ' const result = (await lambdaHandler(event, {})) as {',
109
+ ' statusCode?: number;',
110
+ ' headers?: Record<string, string | string[]>;',
111
+ ' body?: string;',
112
+ ' isBase64Encoded?: boolean;',
113
+ ' };',
114
+ '',
115
+ ' res.statusCode = typeof result.statusCode === "number" ? result.statusCode : 200;',
116
+ ' if (result.headers) {',
117
+ ' for (const [k, v] of Object.entries(result.headers)) {',
118
+ ' res.setHeader(k, v as any);',
119
+ ' }',
120
+ ' }',
121
+ '',
122
+ ' if (result.isBase64Encoded === true && typeof result.body === "string") {',
123
+ " res.end(Buffer.from(result.body, 'base64'));",
124
+ ' } else {',
125
+ " res.end(result.body ?? '');",
126
+ ' }',
127
+ ' } catch {',
128
+ ' res.statusCode = 500;',
129
+ " res.setHeader('content-type', 'application/json');",
130
+ " res.end(JSON.stringify({ message: 'Internal Server Error' }));",
131
+ ' }',
132
+ '});',
133
+ '',
134
+ 'server.listen(port, host, () => {',
135
+ ' // eslint-disable-next-line no-console',
136
+ " console.log('Lambda adapter server listening at http://' + host + ':' + port);",
137
+ '});',
138
+ '',
139
+ ].join('\n');
@@ -0,0 +1,10 @@
1
+ export type EnvBackfillResult = {
2
+ changed: boolean;
3
+ filledKeys: string[];
4
+ appendedKeys: string[];
5
+ };
6
+ export declare const EnvFileBackfill: Readonly<{
7
+ stripEnvInlineComment: (value: string) => string;
8
+ backfillEnvDefaults: (envPath: string, defaults: Record<string, string>) => EnvBackfillResult;
9
+ }>;
10
+ //# sourceMappingURL=EnvFileBackfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EnvFileBackfill.d.ts","sourceRoot":"","sources":["../../../../src/cli/env/EnvFileBackfill.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAsEF,eAAO,MAAM,eAAe;mCApEU,MAAM,KAAG,MAAM;mCAqB1C,MAAM,YACL,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC/B,iBAAiB;EAgDlB,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { readFileSync, writeFileSync } from '../../node-singletons/fs.js';
2
+ const stripEnvInlineComment = (value) => {
3
+ let inSingle = false;
4
+ let inDouble = false;
5
+ for (let i = 0; i < value.length; i += 1) {
6
+ const ch = value[i];
7
+ if (ch === "'" && !inDouble)
8
+ inSingle = !inSingle;
9
+ if (ch === '"' && !inSingle)
10
+ inDouble = !inDouble;
11
+ if (!inSingle && !inDouble && ch === '#') {
12
+ const prev = value[i - 1];
13
+ if (prev === undefined || prev === ' ' || prev === '\t') {
14
+ return value.slice(0, i).trimEnd();
15
+ }
16
+ }
17
+ }
18
+ return value;
19
+ };
20
+ const backfillEnvDefaults = (envPath, defaults) => {
21
+ const raw = readFileSync(envPath, 'utf8');
22
+ const lines = raw.split(/\r?\n/);
23
+ const seen = new Set();
24
+ const filledKeys = [];
25
+ const appendedKeys = [];
26
+ const out = lines.map((line) => {
27
+ const trimmed = line.trim();
28
+ if (trimmed === '' || trimmed.startsWith('#'))
29
+ return line;
30
+ const withoutExport = trimmed.startsWith('export ') ? trimmed.slice('export '.length) : trimmed;
31
+ const eq = withoutExport.indexOf('=');
32
+ if (eq <= 0)
33
+ return line;
34
+ const key = withoutExport.slice(0, eq).trim();
35
+ if (key === '')
36
+ return line;
37
+ if (!Object.hasOwn(defaults, key))
38
+ return line;
39
+ if (seen.has(key))
40
+ return line;
41
+ seen.add(key);
42
+ const rhs = withoutExport.slice(eq + 1);
43
+ const withoutComment = stripEnvInlineComment(rhs);
44
+ const value = withoutComment.trim();
45
+ if (value !== '')
46
+ return line;
47
+ filledKeys.push(key);
48
+ return `${key}=${defaults[key]}`;
49
+ });
50
+ const missingKeys = Object.keys(defaults).filter((k) => !seen.has(k));
51
+ if (missingKeys.length > 0) {
52
+ appendedKeys.push(...missingKeys);
53
+ out.push(...missingKeys.map((k) => `${k}=${defaults[k]}`));
54
+ }
55
+ const changed = filledKeys.length > 0 || appendedKeys.length > 0;
56
+ if (!changed)
57
+ return { changed: false, filledKeys, appendedKeys };
58
+ writeFileSync(envPath, out.join('\n') + (out.at(-1) === '' ? '' : '\n'));
59
+ return { changed: true, filledKeys, appendedKeys };
60
+ };
61
+ export const EnvFileBackfill = Object.freeze({
62
+ stripEnvInlineComment,
63
+ backfillEnvDefaults,
64
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IACpE,cAAc,IAAI,MAAM,CAAC;IACzB,sBAAsB,IAAI,OAAO,CAAC;IAClC,iBAAiB,IAAI,MAAM,CAAC;IAC5B,WAAW,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACtD,gBAAgB,IAAI,OAAO,CAAC;IAC5B,aAAa,IAAI,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3E;AAsfD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsBrE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAsBA;AA0ID;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,GAAE,MAAsB,GAAG,kBAAkB,CAsB/F;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAEhC;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC"}
1
+ {"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IACpE,cAAc,IAAI,MAAM,CAAC;IACzB,sBAAsB,IAAI,OAAO,CAAC;IAClC,iBAAiB,IAAI,MAAM,CAAC;IAC5B,WAAW,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACtD,gBAAgB,IAAI,OAAO,CAAC;IAC5B,aAAa,IAAI,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3E;AA0cD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsBrE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAsBA;AA0ID;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,GAAE,MAAsB,GAAG,kBAAkB,CAsB/F;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAEhC;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC"}
@@ -2,6 +2,7 @@
2
2
  * Project Scaffolder - New project generation
3
3
  * Handles directory structure and boilerplate file creation
4
4
  */
5
+ import { EnvFileBackfill } from '../env/EnvFileBackfill.js';
5
6
  import { Logger } from '../../config/logger.js';
6
7
  import { randomBytes } from '../../node-singletons/crypto.js';
7
8
  import fs from '../../node-singletons/fs.js';
@@ -97,62 +98,6 @@ const createProjectConfigFile = (projectPath, variables) => {
97
98
  return false;
98
99
  }
99
100
  };
100
- const stripEnvInlineComment = (value) => {
101
- let inSingle = false;
102
- let inDouble = false;
103
- for (let i = 0; i < value.length; i += 1) {
104
- const ch = value[i];
105
- if (ch === "'" && !inDouble)
106
- inSingle = !inSingle;
107
- if (ch === '"' && !inSingle)
108
- inDouble = !inDouble;
109
- if (!inSingle && !inDouble && ch === '#') {
110
- const prev = value[i - 1];
111
- if (prev === undefined || prev === ' ' || prev === '\t') {
112
- return value.slice(0, i).trimEnd();
113
- }
114
- }
115
- }
116
- return value;
117
- };
118
- const backfillEnvDefaults = (envPath, defaults) => {
119
- const raw = fs.readFileSync(envPath, 'utf8');
120
- const lines = raw.split(/\r?\n/);
121
- const seen = new Set();
122
- const filled = new Set();
123
- const out = lines.map((line) => {
124
- const trimmed = line.trim();
125
- if (trimmed === '' || trimmed.startsWith('#'))
126
- return line;
127
- const withoutExport = trimmed.startsWith('export ') ? trimmed.slice('export '.length) : trimmed;
128
- const eq = withoutExport.indexOf('=');
129
- if (eq <= 0)
130
- return line;
131
- const key = withoutExport.slice(0, eq).trim();
132
- if (key === '')
133
- return line;
134
- if (!Object.hasOwn(defaults, key))
135
- return line;
136
- if (seen.has(key))
137
- return line;
138
- seen.add(key);
139
- const rhs = withoutExport.slice(eq + 1);
140
- const withoutComment = stripEnvInlineComment(rhs);
141
- const value = withoutComment.trim();
142
- if (value !== '')
143
- return line;
144
- filled.add(key);
145
- return `${key}=${defaults[key]}`;
146
- });
147
- const missingKeys = Object.keys(defaults).filter((k) => !seen.has(k));
148
- if (missingKeys.length > 0) {
149
- out.push(...missingKeys.map((k) => `${k}=${defaults[k]}`));
150
- }
151
- // Avoid rewriting if nothing changed.
152
- if (filled.size === 0 && missingKeys.length === 0)
153
- return;
154
- fs.writeFileSync(envPath, out.join('\n') + (out.at(-1) === '' ? '' : '\n'));
155
- };
156
101
  const buildDatabaseEnvLines = (database) => {
157
102
  if (database === 'postgresql' || database === 'postgres') {
158
103
  return [
@@ -163,10 +108,29 @@ const buildDatabaseEnvLines = (database) => {
163
108
  'DB_PASSWORD=',
164
109
  ];
165
110
  }
111
+ if (database === 'mysql') {
112
+ return [
113
+ 'DB_HOST=localhost',
114
+ 'DB_PORT=3306',
115
+ 'DB_DATABASE=zintrust',
116
+ 'DB_USERNAME=root',
117
+ 'DB_PASSWORD=',
118
+ ];
119
+ }
166
120
  if (database === 'sqlite') {
167
121
  // Provide both DB_DATABASE (used by the framework) and DB_PATH (common alias)
168
122
  return ['DB_DATABASE=./database.sqlite', 'DB_PATH=./database.sqlite'];
169
123
  }
124
+ if (database === 'd1-remote' || database === 'd1-proxy') {
125
+ return [
126
+ '# Cloudflare D1 Remote Proxy (HTTPS)',
127
+ 'D1_REMOTE_URL=',
128
+ 'D1_REMOTE_KEY_ID=',
129
+ 'D1_REMOTE_SECRET=',
130
+ 'D1_REMOTE_MODE=registry',
131
+ 'ZT_PROXY_TIMEOUT_MS=15000',
132
+ ];
133
+ }
170
134
  return [];
171
135
  };
172
136
  const createEnvFile = (projectPath, variables) => {
@@ -178,7 +142,7 @@ const createEnvFile = (projectPath, variables) => {
178
142
  // If an .env already exists (e.g., from a template), do not overwrite user values.
179
143
  // But we *do* backfill safe defaults for common bootstrap keys when missing/blank.
180
144
  if (fs.existsSync(fullPath)) {
181
- backfillEnvDefaults(fullPath, {
145
+ EnvFileBackfill.backfillEnvDefaults(fullPath, {
182
146
  HOST: 'localhost',
183
147
  PORT: String(Number(variables['port'] ?? 7777)),
184
148
  LOG_LEVEL: 'debug',
@@ -197,7 +161,6 @@ const createEnvFile = (projectPath, variables) => {
197
161
  `APP_NAME=${name}`,
198
162
  'HOST=localhost',
199
163
  `PORT=${port}`,
200
- `APP_PORT=${port}`,
201
164
  'APP_DEBUG=true',
202
165
  // Auto-generated secure key for storage signing and encryption
203
166
  `APP_KEY=${appKey}`,
@@ -225,7 +188,7 @@ const createEnvFile = (projectPath, variables) => {
225
188
  '',
226
189
  '# Microservices',
227
190
  'SERVICE_DISCOVERY_ENABLED=false',
228
- 'SERVICE_DISCOVERY_DRIVER=local',
191
+ 'SERVICE_DISCOVERY_TYPE=filesystem',
229
192
  'SERVICE_NAME=',
230
193
  'SERVICE_VERSION=1.0.0',
231
194
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"DistPackager.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/DistPackager.ts"],"names":[],"mappings":"AAgHA,eAAO,MAAM,YAAY;IACvB;;;OAGG;sBACe,MAAM,aAAY,MAAM,GAAmB,IAAI;EAgBjE,CAAC"}
1
+ {"version":3,"file":"DistPackager.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/DistPackager.ts"],"names":[],"mappings":"AAoIA,eAAO,MAAM,YAAY;IACvB;;;OAGG;sBACe,MAAM,aAAY,MAAM,GAAmB,IAAI;EAgBjE,CAAC"}
@@ -45,6 +45,10 @@ const buildDistPackageJson = (rootPkg) => {
45
45
  types: './index.d.ts',
46
46
  default: './index.js',
47
47
  },
48
+ './start': {
49
+ types: './start.d.ts',
50
+ default: './start.js',
51
+ },
48
52
  },
49
53
  dependencies: coerceDependencies(rootPkg.dependencies),
50
54
  };
@@ -52,8 +56,12 @@ const buildDistPackageJson = (rootPkg) => {
52
56
  const writeDistEntrypoints = (distPath) => {
53
57
  const distIndexJsPath = path.join(distPath, 'index.js');
54
58
  const distIndexDtsPath = path.join(distPath, 'index.d.ts');
59
+ const distStartJsPath = path.join(distPath, 'start.js');
60
+ const distStartDtsPath = path.join(distPath, 'start.d.ts');
55
61
  fs.writeFileSync(distIndexJsPath, "export * from './src/index.js';\n");
56
62
  fs.writeFileSync(distIndexDtsPath, "export * from './src/index';\n");
63
+ fs.writeFileSync(distStartJsPath, "export * from './src/start.js'; export { default } from './src/start.js';\n");
64
+ fs.writeFileSync(distStartDtsPath, "export * from './src/start'; export { default } from './src/start';\n");
57
65
  };
58
66
  const writeDistPackageJson = (distPath, pkg) => {
59
67
  const distPackageJsonPath = path.join(distPath, 'package.json');