@zintrust/core 0.4.12 → 0.4.13

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.12",
3
+ "version": "0.4.13",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAgtBvF,eAAO,MAAM,YAAY;cACb,YAAY;EA6BtB,CAAC"}
1
+ {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmvBvF,eAAO,MAAM,YAAY;cACb,YAAY;EAmCtB,CAAC"}
@@ -7,6 +7,7 @@ import * as Common from '../../common/index.js';
7
7
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
8
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
9
9
  import * as path from '../../node-singletons/path.js';
10
+ const isAbsolutePath = (value) => value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
10
11
  const resolveNpmPath = () => {
11
12
  try {
12
13
  return typeof Common.resolveNpmPath === 'function' ? Common.resolveNpmPath() : 'npm';
@@ -168,6 +169,26 @@ const resolveCacheEnabledPreference = (options) => {
168
169
  return options.cache;
169
170
  return undefined;
170
171
  };
172
+ const resolveRootEnvPreference = (options) => {
173
+ const hasRootEnv = hasFlag('--root-env');
174
+ const hasNoRootEnv = hasFlag('--no-root-env');
175
+ if (hasRootEnv && hasNoRootEnv) {
176
+ throw ErrorFactory.createCliError('Error: Cannot use both --root-env and --no-root-env.');
177
+ }
178
+ if (hasRootEnv)
179
+ return true;
180
+ if (hasNoRootEnv)
181
+ return false;
182
+ if (typeof options.rootEnv === 'boolean')
183
+ return options.rootEnv;
184
+ return true;
185
+ };
186
+ const resolveEnvPath = (options, projectRoot) => {
187
+ const raw = typeof options.envPath === 'string' ? options.envPath.trim() : '';
188
+ if (raw === '')
189
+ return undefined;
190
+ return isAbsolutePath(raw) ? raw : path.join(projectRoot, raw);
191
+ };
171
192
  const findNearestPackageJsonDir = (cwd) => {
172
193
  let current = path.resolve(cwd);
173
194
  while (true) {
@@ -210,9 +231,16 @@ const buildStartEnv = (projectRoot) => ({
210
231
  ...process.env,
211
232
  ZINTRUST_PROJECT_ROOT: projectRoot,
212
233
  });
213
- const ensureStartEnvLoaded = (context) => {
214
- const extraCwds = context.cwd === context.projectRoot ? [] : [context.cwd];
215
- EnvFileLoader.ensureLoaded({ cwd: context.projectRoot, extraCwds });
234
+ const ensureStartEnvLoaded = (context, options) => {
235
+ const envPath = resolveEnvPath(options, context.projectRoot);
236
+ const rootEnv = resolveRootEnvPreference(options);
237
+ const extraCwds = envPath === undefined && context.cwd !== context.projectRoot ? [context.cwd] : [];
238
+ EnvFileLoader.ensureLoaded({
239
+ cwd: context.projectRoot,
240
+ includeCwd: rootEnv,
241
+ extraCwds,
242
+ ...(envPath === undefined ? {} : { envPaths: [envPath] }),
243
+ });
216
244
  };
217
245
  const isFrameworkRepo = (packageJson) => packageJson.name === '@zintrust/core';
218
246
  const hasDevScript = (packageJson) => {
@@ -483,7 +511,7 @@ const executeSplitStart = async (cmd, context, _options) => {
483
511
  const executeStart = async (options, cmd) => {
484
512
  const cwd = process.cwd();
485
513
  const context = resolveStartContext(cwd);
486
- ensureStartEnvLoaded(context);
514
+ ensureStartEnvLoaded(context, options);
487
515
  const mode = resolveMode(options);
488
516
  const port = resolvePort(options);
489
517
  const runtime = resolveRuntime(options);
@@ -541,8 +569,11 @@ export const StartCommand = Object.freeze({
541
569
  .option('--no-cache', 'Disable cache functionality')
542
570
  .option('--watch', 'Force watch mode (Node only)')
543
571
  .option('--no-watch', 'Disable watch mode (Node only)')
572
+ .option('--root-env', 'Load root project .env files for standalone service start')
573
+ .option('--no-root-env', 'Skip root project .env files for standalone service start')
544
574
  .option('--mode <development|production|testing>', 'Override app mode')
545
575
  .option('--env <name>', 'Wrangler environment name (Wrangler mode only)')
576
+ .option('--env-path <path>', 'Explicit env directory or .env file path for standalone service start')
546
577
  .option('--wrangler-config <path>', 'Wrangler config path (Wrangler mode only)')
547
578
  .option('--runtime <nodejs|cloudflare|lambda|deno|auto>', 'Set RUNTIME for spawned Node')
548
579
  .option('-p, --port <number>', 'Override server port');
@@ -1,7 +1,9 @@
1
1
  type node_env = 'development' | 'production' | 'testing';
2
2
  type LoadOptions = {
3
3
  cwd?: string;
4
+ includeCwd?: boolean;
4
5
  extraCwds?: string[];
6
+ envPaths?: string[];
5
7
  overrideExisting?: boolean;
6
8
  };
7
9
  type LoadState = {
@@ -1 +1 @@
1
- {"version":3,"file":"EnvFileLoader.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/EnvFileLoader.ts"],"names":[],"mappings":"AAQA,KAAK,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAAC;AAmIzD,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAkHF,eAAO,MAAM,aAAa;qBA7DH,WAAW,KAAQ,SAAS;6BAyBpB,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,KAAQ,SAAS;mCAG/C,YAAY,KAAG,IAAI;oBA+BpC,SAAS;EAO5B,CAAC"}
1
+ {"version":3,"file":"EnvFileLoader.d.ts","sourceRoot":"","sources":["../../../../src/cli/utils/EnvFileLoader.ts"],"names":[],"mappings":"AASA,KAAK,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,SAAS,CAAC;AAmIzD,KAAK,WAAW,GAAG;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAaF,KAAK,YAAY,GAAG;IAClB,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAgMF,eAAO,MAAM,aAAa;qBAjDH,WAAW,KAAQ,SAAS;6BAapB,IAAI,CAAC,WAAW,EAAE,kBAAkB,CAAC,KAAQ,SAAS;mCAG/C,YAAY,KAAG,IAAI;oBA+BpC,SAAS;EAO5B,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { Env } from '../../config/env.js';
2
+ import { isArray, isNonEmptyString } from '../../helper/index.js';
2
3
  import { existsSync, readFileSync } from '../../node-singletons/fs.js';
3
- import { join } from '../../node-singletons/path.js';
4
+ import * as path from '../../node-singletons/path.js';
4
5
  const safeEnvGet = (key, defaultValue = '') => {
5
6
  const envAny = Env;
6
7
  if (typeof envAny.get === 'function')
@@ -102,7 +103,7 @@ const applyToProcessEnv = (values, overrideExisting) => {
102
103
  }
103
104
  };
104
105
  const readEnvFileIfExists = (cwd, filename) => {
105
- const fullPath = join(cwd, filename);
106
+ const fullPath = path.join(cwd, filename);
106
107
  if (!existsSync(fullPath))
107
108
  return undefined;
108
109
  const raw = readFileSync(fullPath, 'utf-8');
@@ -120,20 +121,20 @@ const resolveAppMode = (cwd) => {
120
121
  };
121
122
  const filesLoader = (cwd, mode) => {
122
123
  const files = [];
123
- if (existsSync(join(cwd, '.env')))
124
+ if (existsSync(path.join(cwd, '.env')))
124
125
  files.push('.env');
125
126
  // Per your rule: production uses .env; dev uses .env.dev
126
127
  if (mode !== undefined && mode !== '' && mode !== 'production') {
127
128
  const modeFile = `.env.${mode}`;
128
- if (existsSync(join(cwd, modeFile)))
129
+ if (existsSync(path.join(cwd, modeFile)))
129
130
  files.push(modeFile);
130
131
  }
131
132
  const local = '.env.local';
132
- if (existsSync(join(cwd, local)))
133
+ if (existsSync(path.join(cwd, local)))
133
134
  files.push(local);
134
135
  if (mode !== undefined && mode !== '') {
135
136
  const modeLocal = `.env.${mode}.local`;
136
- if (existsSync(join(cwd, modeLocal)))
137
+ if (existsSync(path.join(cwd, modeLocal)))
137
138
  files.push(modeLocal);
138
139
  }
139
140
  return files;
@@ -161,25 +162,85 @@ const loadFromCwd = (cwd, overrideExisting) => {
161
162
  }
162
163
  return { loadedFiles: files, mode };
163
164
  };
164
- const load = (options = {}) => {
165
- if (cached !== undefined)
166
- return cached;
167
- const cwd = typeof options.cwd === 'string' && options.cwd !== '' ? options.cwd : process.cwd();
168
- const extraCwds = Array.isArray(options.extraCwds)
169
- ? options.extraCwds.filter((value) => typeof value === 'string' && value.trim() !== '')
170
- : [];
165
+ const loadFromFile = (filePath, overrideExisting) => {
166
+ if (!existsSync(filePath))
167
+ return { loadedFiles: [] };
168
+ const raw = readFileSync(filePath, 'utf-8');
169
+ const parsed = parseEnvFile(raw);
170
+ applyToProcessEnv(parsed, overrideExisting);
171
+ const rawMode = parsed['NODE_ENV'];
172
+ const mode = isNonEmptyString(rawMode) ? normalizeAppMode(rawMode) : undefined;
173
+ if (mode !== undefined) {
174
+ safeEnvSet('NODE_ENV', mode);
175
+ }
176
+ return { loadedFiles: [filePath], mode };
177
+ };
178
+ const normalizeCwdList = (value) => {
179
+ if (!isArray(value))
180
+ return [];
181
+ return value
182
+ .filter(isNonEmptyString)
183
+ .map((item) => item.trim())
184
+ .filter((item) => item !== '');
185
+ };
186
+ const normalizeEnvPathList = (value) => normalizeCwdList(value);
187
+ const createLoadPlan = (options) => {
188
+ const cwd = isNonEmptyString(options.cwd) ? options.cwd : process.cwd();
189
+ const includeCwd = options.includeCwd !== false;
190
+ const extraCwds = normalizeCwdList(options.extraCwds);
191
+ const envPaths = normalizeEnvPathList(options.envPaths);
171
192
  const overrideExisting = options.overrideExisting ?? true;
172
- const roots = [cwd, ...extraCwds].filter((value, index, items) => items.indexOf(value) === index);
173
- let mergedMode;
174
- const loadedFiles = [];
175
- for (let index = 0; index < roots.length; index += 1) {
176
- const root = roots[index];
177
- const state = loadFromCwd(root, index === 0 ? overrideExisting : true);
178
- if (mergedMode === undefined && state.mode !== undefined)
179
- mergedMode = state.mode;
180
- loadedFiles.push(...state.loadedFiles);
181
- }
182
- cached = { loadedFiles, mode: mergedMode };
193
+ const sources = [];
194
+ if (includeCwd) {
195
+ sources.push({
196
+ key: `cwd:${cwd}`,
197
+ path: cwd,
198
+ kind: 'cwd',
199
+ overrideExisting,
200
+ });
201
+ }
202
+ for (const extraCwd of extraCwds) {
203
+ sources.push({
204
+ key: `cwd:${extraCwd}`,
205
+ path: extraCwd,
206
+ kind: 'cwd',
207
+ overrideExisting: true,
208
+ });
209
+ }
210
+ for (const envPath of envPaths) {
211
+ const looksLikeFile = path.basename(envPath).startsWith('.env');
212
+ sources.push({
213
+ key: `${looksLikeFile ? 'file' : 'cwd'}:${envPath}`,
214
+ path: envPath,
215
+ kind: looksLikeFile ? 'file' : 'cwd',
216
+ overrideExisting: true,
217
+ });
218
+ }
219
+ return sources.filter((source, index, items) => items.findIndex((item) => item.key === source.key) === index);
220
+ };
221
+ const loadSource = (source) => {
222
+ if (source.kind === 'file')
223
+ return loadFromFile(source.path, source.overrideExisting);
224
+ return loadFromCwd(source.path, source.overrideExisting);
225
+ };
226
+ const mergeCachedState = (state, source, next) => {
227
+ state.loadedSourceKeys.push(source.key);
228
+ if (next.mode !== undefined && state.mode === undefined) {
229
+ state.mode = next.mode;
230
+ }
231
+ if (next.loadedFiles.length > 0) {
232
+ state.loadedFiles.push(...next.loadedFiles);
233
+ }
234
+ return state;
235
+ };
236
+ const load = (options = {}) => {
237
+ const plan = createLoadPlan(options);
238
+ cached ??= { loadedFiles: [], loadedSourceKeys: [] };
239
+ for (const source of plan) {
240
+ if (cached.loadedSourceKeys.includes(source.key))
241
+ continue;
242
+ mergeCachedState(cached, source, loadSource(source));
243
+ }
183
244
  return cached;
184
245
  };
185
246
  const ensureLoaded = (options = {}) => load({ ...options, overrideExisting: false });
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.12
2
+ * @zintrust/core v0.4.13
3
3
  *
4
4
  * ZinTrust Framework - Production-Grade TypeScript Backend
5
5
  * Built for performance, type safety, and exceptional developer experience
6
6
  *
7
7
  * Build Information:
8
- * Built: 2026-03-23T13:57:31.438Z
8
+ * Built: 2026-03-23T14:57:40.299Z
9
9
  * Node: >=20.0.0
10
10
  * License: MIT
11
11
  *
@@ -21,7 +21,7 @@
21
21
  * Available at runtime for debugging and health checks
22
22
  */
23
23
  export const ZINTRUST_VERSION = '0.1.41';
24
- export const ZINTRUST_BUILD_DATE = '2026-03-23T13:57:31.366Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-23T14:57:40.266Z'; // Replaced during build
25
25
  export { Application } from './boot/Application.js';
26
26
  export { AwsSigV4 } from './common/index.js';
27
27
  export { SignedRequest } from './security/SignedRequest.js';
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/start.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AAexC,eAAO,MAAM,UAAU,GAAI,eAAe,MAAM,KAAG,OAYlD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,eAAe,OAAO,KAAG,oBASnE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,eAAe,OAAO,KACrB,OAAO,CAAC,oBAAoB,CAQ9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAO1C,CAAC;AAEF;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/start.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,gCAAgC,CAAC;AAuBxC,eAAO,MAAM,UAAU,GAAI,eAAe,MAAM,KAAG,OAYlD,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,eAAe,OAAO,KAAG,oBASnE,CAAC;AAqFF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,eAAe,OAAO,KACrB,OAAO,CAAC,oBAAoB,CAS9B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,KAAK,QAAa,OAAO,CAAC,IAAI,CAO1C,CAAC;AAEF;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEpE;;GAEG;AACH,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElD;;GAEG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
package/src/start.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { ErrorFactory } from './exceptions/ZintrustError.js';
2
+ import { isArray, isNonEmptyString, isObject } from './helper/index.js';
2
3
  import { ZintrustLang } from './lang/lang.js';
3
4
  import { normalizeActiveServiceRuntime, } from './microservices/ServiceManifest.js';
4
5
  import { ProjectRuntime } from './runtime/ProjectRuntime.js';
5
6
  import { isNodeRuntime } from './runtime/detectRuntime.js';
7
+ const isAbsolutePath = (value) => value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
6
8
  const fileUrlToPathLike = (value) => {
7
9
  if (!value.startsWith(ZintrustLang.FILE_PROTOCOL))
8
10
  return value;
@@ -34,7 +36,68 @@ export const configureStandaloneService = (activeService) => {
34
36
  }
35
37
  return ProjectRuntime.set({ activeService: normalized }).activeService ?? normalized;
36
38
  };
39
+ const normalizeStandaloneEnvPaths = (value) => {
40
+ if (isNonEmptyString(value)) {
41
+ const trimmed = value.trim();
42
+ return trimmed === '' ? [] : [trimmed];
43
+ }
44
+ if (!isArray(value))
45
+ return [];
46
+ return value
47
+ .filter(isNonEmptyString)
48
+ .map((item) => item.trim())
49
+ .filter((item) => item !== '');
50
+ };
51
+ const resolveStandaloneProjectRoot = async () => {
52
+ const configuredRoot = process.env?.['ZINTRUST_PROJECT_ROOT'] ?? '';
53
+ if (isNonEmptyString(configuredRoot))
54
+ return configuredRoot;
55
+ const { existsSync } = await import('./node-singletons/fs.js');
56
+ const path = await import('./node-singletons/path.js');
57
+ let current = process.cwd();
58
+ while (true) {
59
+ if (existsSync(path.join(current, 'package.json')))
60
+ return current;
61
+ const parent = path.dirname(current);
62
+ if (parent === current)
63
+ return process.cwd();
64
+ current = parent;
65
+ }
66
+ };
67
+ const resolveServiceEnvPath = async (importMetaUrl, activeService, projectRoot) => {
68
+ const path = await import('./node-singletons/path.js');
69
+ if (isObject(activeService) && isNonEmptyString(activeService['configRoot'])) {
70
+ return path.dirname(path.join(projectRoot, activeService['configRoot']));
71
+ }
72
+ const entryFile = fileUrlToPathLike(importMetaUrl);
73
+ const entryDir = path.dirname(entryFile);
74
+ return path.basename(entryDir) === 'src' ? path.dirname(entryDir) : entryDir;
75
+ };
76
+ const resolveConfiguredEnvPaths = async (projectRoot, activeService, importMetaUrl) => {
77
+ const path = await import('./node-singletons/path.js');
78
+ const configured = isObject(activeService)
79
+ ? normalizeStandaloneEnvPaths(activeService['envPath'])
80
+ : [];
81
+ if (configured.length > 0) {
82
+ return configured.map((value) => isAbsolutePath(value) ? value : path.join(projectRoot, value));
83
+ }
84
+ return [await resolveServiceEnvPath(importMetaUrl, activeService, projectRoot)];
85
+ };
86
+ const ensureStandaloneServiceEnv = async (importMetaUrl, activeService) => {
87
+ if (!isNodeRuntime())
88
+ return;
89
+ const { EnvFileLoader } = await import('./cli/utils/EnvFileLoader.js');
90
+ const projectRoot = await resolveStandaloneProjectRoot();
91
+ const envPaths = await resolveConfiguredEnvPaths(projectRoot, activeService, importMetaUrl);
92
+ const rootEnv = !isObject(activeService) || activeService['rootEnv'] !== false;
93
+ EnvFileLoader.ensureLoaded({
94
+ cwd: projectRoot,
95
+ includeCwd: rootEnv,
96
+ envPaths,
97
+ });
98
+ };
37
99
  export const bootStandaloneService = async (importMetaUrl, activeService) => {
100
+ await ensureStandaloneServiceEnv(importMetaUrl, activeService);
38
101
  const configuredService = configureStandaloneService(activeService);
39
102
  if (isNodeMain(importMetaUrl)) {
40
103
  await start();