@stacksjs/config 0.70.88 → 0.70.91

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.
@@ -0,0 +1,53 @@
1
+ import { config } from "./config";
2
+ const FEATURE_NAMES = [
3
+ "auth",
4
+ "marketing",
5
+ "cms",
6
+ "commerce",
7
+ "dashboard",
8
+ "monitoring",
9
+ "realtime",
10
+ "queue"
11
+ ], FEATURE_DEFAULTS = {
12
+ dashboard: !0
13
+ }, overrides = new Map;
14
+ function configFor(name) {
15
+ const raw = config[name];
16
+ return raw && typeof raw === "object" ? raw : void 0;
17
+ }
18
+ export function feature(name) {
19
+ if (overrides.has(name))
20
+ return overrides.get(name);
21
+ const cfg = configFor(name);
22
+ if (cfg) {
23
+ const enabledField = cfg.enabled;
24
+ if (enabledField === !1)
25
+ return !1;
26
+ if (Array.isArray(cfg.env) && cfg.env.length > 0) {
27
+ const currentEnv = (config.app?.env ?? "").toString();
28
+ if (!cfg.env.includes(currentEnv))
29
+ return !1;
30
+ }
31
+ if (enabledField !== void 0)
32
+ return !!enabledField;
33
+ return !0;
34
+ }
35
+ return FEATURE_DEFAULTS[name] ?? !1;
36
+ }
37
+ export function enableFeature(name) {
38
+ overrides.set(name, !0);
39
+ }
40
+ export function disableFeature(name) {
41
+ overrides.set(name, !1);
42
+ }
43
+ export function resetFeature(name) {
44
+ overrides.delete(name);
45
+ }
46
+ export function listFeatures() {
47
+ const out = {};
48
+ for (const name of FEATURE_NAMES)
49
+ out[name] = feature(name);
50
+ for (const name of overrides.keys())
51
+ out[name] = feature(name);
52
+ return out;
53
+ }
@@ -0,0 +1,45 @@
1
+ import { config } from '.';
2
+ import type { AppConfig, CacheConfig, CdnConfig, ChatConfig, CliConfig, DatabaseConfig, DependenciesConfig, DnsConfig, EmailConfig, Events, FilesystemsConfig, FeatureFlagsConfig, GitConfig, HashingConfig, LibraryConfig, Model, NotificationConfig, PaymentConfig, QueueConfig, SearchEngineConfig, SecurityConfig, ServicesConfig, StacksConfig, StorageConfig, UiConfig } from '@stacksjs/types';
3
+ export declare function localUrl(options?: {
4
+ domain?: string
5
+ type?: LocalUrlType
6
+ network?: boolean
7
+ localhost?: boolean
8
+ https?: boolean
9
+ }): Promise<string>;
10
+ export declare function defineStacksConfig(config: StacksConfig): StacksConfig;
11
+ export declare function defineApp(config: AppConfig): AppConfig;
12
+ export declare function defineCache(config: CacheConfig): CacheConfig;
13
+ export declare function defineCdn(config: CdnConfig): CdnConfig;
14
+ export declare function defineChat(config: ChatConfig): ChatConfig;
15
+ export declare function defineCli(config: CliConfig): CliConfig;
16
+ export declare function defineDatabase(config: DatabaseConfig): DatabaseConfig;
17
+ export declare function defineDependencies(config: DependenciesConfig): DependenciesConfig;
18
+ export declare function defineDns(config: DnsConfig): DnsConfig;
19
+ export declare function defineEmailConfig(config: EmailConfig): EmailConfig;
20
+ export declare function defineEmail(config: EmailConfig): EmailConfig;
21
+ export declare function defineGit(config: GitConfig): GitConfig;
22
+ export declare function defineHashing(config: HashingConfig): HashingConfig;
23
+ export declare function defineLibrary(config: LibraryConfig): LibraryConfig;
24
+ export declare function defineNotification(config: NotificationConfig): NotificationConfig;
25
+ export declare function definePayment(config: PaymentConfig): PaymentConfig;
26
+ export declare function defineQueue(config: QueueConfig): QueueConfig;
27
+ export declare function defineSearchEngine(config: SearchEngineConfig): SearchEngineConfig;
28
+ export declare function defineSecurity(config: SecurityConfig): SecurityConfig;
29
+ export declare function defineServices(config: ServicesConfig): ServicesConfig;
30
+ export declare function defineSms(config: any): any;
31
+ export declare function defineStorage(config: StorageConfig): StorageConfig;
32
+ export declare function defineFilesystems(config: FilesystemsConfig): FilesystemsConfig;
33
+ export declare function defineFeatureFlags(config: FeatureFlagsConfig): FeatureFlagsConfig;
34
+ export declare function defineUi(config: UiConfig): UiConfig;
35
+ export declare function defineModel(config: Model): Model;
36
+ export declare function defineEvents(config: Events): Events;
37
+ export type LocalUrlType = | 'frontend'
38
+ | 'backend'
39
+ | 'api'
40
+ | 'admin'
41
+ | 'library'
42
+ | 'email'
43
+ | 'docs'
44
+ | 'inspect'
45
+ | 'desktop';
@@ -0,0 +1,153 @@
1
+ import { config } from ".";
2
+ export async function localUrl(options = {}) {
3
+ const domain = options.domain ?? config.app.url ?? "stacks", type = options.type ?? "frontend", localhost = options.localhost ?? !1, https = options.https, network = options.network;
4
+ let url = domain.replace(/\.[^.]+$/, ".localhost");
5
+ async function tunnel(port) {
6
+ const { createLocalTunnel } = await import("@stacksjs/tunnel");
7
+ return createLocalTunnel(port);
8
+ }
9
+ switch (type) {
10
+ case "frontend":
11
+ if (network)
12
+ return await tunnel(config.ports?.frontend || 3000);
13
+ if (localhost)
14
+ return `http://localhost:${config.ports?.frontend}`;
15
+ break;
16
+ case "backend":
17
+ if (network)
18
+ return await tunnel(config.ports?.backend || 3001);
19
+ if (localhost)
20
+ return `http://localhost:${config.ports?.backend}`;
21
+ url = `api.${url}`;
22
+ break;
23
+ case "admin":
24
+ if (network)
25
+ return await tunnel(config.ports?.admin || 3002);
26
+ if (localhost)
27
+ return `http://localhost:${config.ports?.admin}`;
28
+ url = `admin.${url}`;
29
+ break;
30
+ case "library":
31
+ if (network)
32
+ return await tunnel(config.ports?.library || 3003);
33
+ if (localhost)
34
+ return `http://localhost:${config.ports?.library}`;
35
+ url = `libs.${url}`;
36
+ break;
37
+ case "email":
38
+ if (network)
39
+ return await tunnel(config.ports?.email || 3005);
40
+ if (localhost)
41
+ return `http://localhost:${config.ports?.email}`;
42
+ url = `email.${url}`;
43
+ break;
44
+ case "desktop":
45
+ if (network)
46
+ return await tunnel(config.ports?.desktop || 3004);
47
+ if (localhost)
48
+ return `http://localhost:${config.ports?.desktop}`;
49
+ url = `desktop.${url}`;
50
+ break;
51
+ case "docs":
52
+ if (network)
53
+ return await tunnel(config.ports?.docs || 3006);
54
+ if (localhost)
55
+ return `http://localhost:${config.ports?.docs}`;
56
+ url = `docs.${url}`;
57
+ break;
58
+ case "inspect":
59
+ if (network)
60
+ return await tunnel(config.ports?.inspect || 3007);
61
+ if (localhost)
62
+ return `http://localhost:${config.ports?.inspect}`;
63
+ url = `inspect.${url}`;
64
+ break;
65
+ default:
66
+ if (localhost)
67
+ return `http://localhost:${config.ports?.frontend}`;
68
+ }
69
+ if (https)
70
+ return `https://${url}`;
71
+ return `http://${url}`;
72
+ }
73
+ export function defineStacksConfig(config) {
74
+ return config;
75
+ }
76
+ export function defineApp(config) {
77
+ return config;
78
+ }
79
+ export function defineCache(config) {
80
+ return config;
81
+ }
82
+ export function defineCdn(config) {
83
+ return config;
84
+ }
85
+ export function defineChat(config) {
86
+ return config;
87
+ }
88
+ export function defineCli(config) {
89
+ return config;
90
+ }
91
+ export function defineDatabase(config) {
92
+ return config;
93
+ }
94
+ export function defineDependencies(config) {
95
+ return config;
96
+ }
97
+ export function defineDns(config) {
98
+ return config;
99
+ }
100
+ export function defineEmailConfig(config) {
101
+ return config;
102
+ }
103
+ export function defineEmail(config) {
104
+ return config;
105
+ }
106
+ export function defineGit(config) {
107
+ return config;
108
+ }
109
+ export function defineHashing(config) {
110
+ return config;
111
+ }
112
+ export function defineLibrary(config) {
113
+ return config;
114
+ }
115
+ export function defineNotification(config) {
116
+ return config;
117
+ }
118
+ export function definePayment(config) {
119
+ return config;
120
+ }
121
+ export function defineQueue(config) {
122
+ return config;
123
+ }
124
+ export function defineSearchEngine(config) {
125
+ return config;
126
+ }
127
+ export function defineSecurity(config) {
128
+ return config;
129
+ }
130
+ export function defineServices(config) {
131
+ return config;
132
+ }
133
+ export function defineSms(config) {
134
+ return config;
135
+ }
136
+ export function defineStorage(config) {
137
+ return config;
138
+ }
139
+ export function defineFilesystems(config) {
140
+ return config;
141
+ }
142
+ export function defineFeatureFlags(config) {
143
+ return config;
144
+ }
145
+ export function defineUi(config) {
146
+ return config;
147
+ }
148
+ export function defineModel(config) {
149
+ return config;
150
+ }
151
+ export function defineEvents(config) {
152
+ return config;
153
+ }
@@ -0,0 +1,14 @@
1
+ // IMPORTANT: do NOT re-add `export * as config from './config'` here.
2
+ // That line creates a module-namespace object named `config` whose
3
+ // properties are non-configurable per the ESM spec (namespaces are sealed).
4
+ // Because `export *` is also exporting the real `config` proxy from
5
+ // `./config`, the namespace object would shadow the proxy under the
6
+ // `config` name — and consumers that did `import { config }` would get
7
+ // the sealed namespace, not the live proxy. The visible symptom: every
8
+ // `config.X` read returns whatever value the proxy's get trap produced
9
+ // the *first* time (typically defaults), and later mutations from
10
+ // `overridesReady` are silently ignored.
11
+ export * from './config';
12
+ export { FRAMEWORK_DEFAULTS } from './defaults';
13
+ export { validateConfig, reportConfigIssues, type ConfigValidationIssue } from './validators';
14
+ export { feature, enableFeature, disableFeature, resetFeature, listFeatures } from './features';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./config";
2
+ export { FRAMEWORK_DEFAULTS } from "./defaults";
3
+ export { validateConfig, reportConfigIssues } from "./validators";
4
+ export { feature, enableFeature, disableFeature, resetFeature, listFeatures } from "./features";
@@ -0,0 +1,4 @@
1
+ import type { StacksConfig } from '@stacksjs/types';
2
+ export declare const overrides: StacksConfig;
3
+ export declare const overridesReady: Promise<StacksConfig>;
4
+ export default overrides;
@@ -0,0 +1,111 @@
1
+ import { validateConfig } from "./validators";
2
+ const skipConfigLoading = process.env.SKIP_CONFIG_LOADING === "true", skipConfigValidation = process.env.SKIP_CONFIG_VALIDATION === "true", OVERRIDES_KEY = Symbol.for("@stacksjs/config:overrides"), READY_KEY = Symbol.for("@stacksjs/config:overridesReady");
3
+ function defaultsForOverrides() {
4
+ return {
5
+ ai: {},
6
+ analytics: {},
7
+ app: { name: process.env.APP_NAME || "Stacks", env: process.env.APP_ENV || "production" },
8
+ auth: {},
9
+ cache: {},
10
+ cli: {},
11
+ cloud: {},
12
+ cms: {},
13
+ commerce: {},
14
+ dashboard: {},
15
+ database: {},
16
+ dns: {},
17
+ realtime: {},
18
+ email: {},
19
+ errors: {},
20
+ featureFlags: {},
21
+ git: {},
22
+ hashing: {},
23
+ library: {},
24
+ logging: {},
25
+ marketing: {},
26
+ monitoring: {},
27
+ notification: {},
28
+ queue: {},
29
+ payment: {},
30
+ ports: {},
31
+ saas: {},
32
+ searchEngine: {},
33
+ security: {},
34
+ services: {},
35
+ filesystems: {},
36
+ team: {},
37
+ ui: {}
38
+ };
39
+ }
40
+ const globalScope = globalThis, sharedOverrides = globalScope[OVERRIDES_KEY];
41
+ export const overrides = sharedOverrides ?? (() => {
42
+ const created = defaultsForOverrides();
43
+ globalScope[OVERRIDES_KEY] = created;
44
+ return created;
45
+ })();
46
+ const userConfigs = [
47
+ ["ai", "~/config/ai"],
48
+ ["analytics", "~/config/analytics"],
49
+ ["app", "~/config/app"],
50
+ ["auth", "~/config/auth"],
51
+ ["cache", "~/config/cache"],
52
+ ["cli", "~/config/cli"],
53
+ ["cloud", "~/config/cloud"],
54
+ ["cms", "~/config/cms"],
55
+ ["commerce", "~/config/commerce"],
56
+ ["dashboard", "~/config/dashboard"],
57
+ ["database", "~/config/database"],
58
+ ["dns", "~/config/dns"],
59
+ ["email", "~/config/email"],
60
+ ["errors", "~/config/errors"],
61
+ ["featureFlags", "~/config/feature-flags"],
62
+ ["git", "~/config/git"],
63
+ ["hashing", "~/config/hashing"],
64
+ ["library", "~/config/library"],
65
+ ["logging", "~/config/logging"],
66
+ ["marketing", "~/config/marketing"],
67
+ ["monitoring", "~/config/monitoring"],
68
+ ["notification", "~/config/notification"],
69
+ ["payment", "~/config/payment"],
70
+ ["ports", "~/config/ports"],
71
+ ["queue", "~/config/queue"],
72
+ ["realtime", "~/config/realtime"],
73
+ ["saas", "~/config/saas"],
74
+ ["searchEngine", "~/config/search-engine"],
75
+ ["security", "~/config/security"],
76
+ ["services", "~/config/services"],
77
+ ["filesystems", "~/config/filesystems"],
78
+ ["team", "~/config/team"],
79
+ ["ui", "~/config/ui"]
80
+ ], sharedReady = globalScope[READY_KEY];
81
+ export const overridesReady = sharedReady ?? (() => {
82
+ const promise = skipConfigLoading ? Promise.resolve(overrides) : Promise.all(userConfigs.map(async ([key, modulePath]) => {
83
+ try {
84
+ const mod = await import(modulePath);
85
+ if (mod?.default !== void 0)
86
+ overrides[key] = mod.default;
87
+ } catch (err) {
88
+ const code = err?.code, msg = err?.message ?? String(err);
89
+ if (!(code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND" || /Cannot find module/i.test(msg)))
90
+ console.warn(`[config] Failed to load ${String(key)} config from ${modulePath}: ${msg}`);
91
+ }
92
+ })).then(() => {
93
+ if (!skipConfigValidation) {
94
+ const issues = validateConfig(overrides);
95
+ if (issues.length > 0) {
96
+ console.error("[config] Configuration issues detected:");
97
+ for (const issue of issues)
98
+ console.error(` \u2022 ${issue.path}: ${issue.message}`);
99
+ const summary = issues.map((i) => ` \u2022 ${i.path}: ${i.message}`).join(`
100
+ `);
101
+ throw Error(`[config] ${issues.length} configuration issue(s) detected at boot:
102
+ ${summary}
103
+ Set SKIP_CONFIG_VALIDATION=true to bypass (e.g. when running migrations against partial config).`);
104
+ }
105
+ }
106
+ return overrides;
107
+ });
108
+ globalScope[READY_KEY] = promise;
109
+ return promise;
110
+ })();
111
+ export default overrides;
@@ -0,0 +1,19 @@
1
+ import type { StacksConfig } from '@stacksjs/types';
2
+ /**
3
+ * Validate a config snapshot. Returns the list of issues — caller decides
4
+ * whether to throw, log, or just print a summary. Returns an empty array
5
+ * when everything checks out.
6
+ */
7
+ export declare function validateConfig(config: Partial<StacksConfig>): ConfigValidationIssue[];
8
+ /**
9
+ * Convenience: validate and pretty-print to stderr. Returns true when
10
+ * the config is clean. Used by the boot path so users see issues
11
+ * immediately on startup rather than digging into a stack trace later.
12
+ */
13
+ export declare function reportConfigIssues(config: Partial<StacksConfig>): boolean;
14
+ export declare interface ConfigValidationIssue {
15
+ path: string
16
+ message: string
17
+ }
18
+ // eslint-disable-next-line pickier/no-unused-vars
19
+ declare type Check = (value: unknown, path: string) => ConfigValidationIssue[];
@@ -0,0 +1,119 @@
1
+ function isPlainObject(v) {
2
+ return typeof v === "object" && v !== null && !Array.isArray(v);
3
+ }
4
+ function checkInteger(min, max) {
5
+ return (value, path) => {
6
+ if (value == null)
7
+ return [];
8
+ let num = null;
9
+ if (typeof value === "number" && Number.isInteger(value))
10
+ num = value;
11
+ else if (typeof value === "string" && /^-?\d+$/.test(value))
12
+ num = Number.parseInt(value, 10);
13
+ if (num === null)
14
+ return [{ path, message: `expected integer, got ${typeof value} (${JSON.stringify(value)})` }];
15
+ if (num < min || num > max)
16
+ return [{ path, message: `expected integer in [${min}, ${max}], got ${num}` }];
17
+ return [];
18
+ };
19
+ }
20
+ function checkOneOf(values) {
21
+ return (value, path) => {
22
+ if (value == null)
23
+ return [];
24
+ if (typeof value !== "string" || !values.includes(value))
25
+ return [{ path, message: `expected one of [${values.join(", ")}], got ${JSON.stringify(value)}` }];
26
+ return [];
27
+ };
28
+ }
29
+ function checkString() {
30
+ return (value, path) => {
31
+ if (value == null)
32
+ return [];
33
+ return typeof value === "string" ? [] : [{ path, message: `expected string, got ${typeof value}` }];
34
+ };
35
+ }
36
+ function checkBoolean() {
37
+ return (value, path) => {
38
+ if (value == null)
39
+ return [];
40
+ return typeof value === "boolean" ? [] : [{ path, message: `expected boolean, got ${typeof value}` }];
41
+ };
42
+ }
43
+ const PORT_CHECK = checkInteger(1, 65535), SCHEMA = {
44
+ app: {
45
+ rules: {
46
+ name: checkString(),
47
+ env: checkOneOf(["local", "development", "staging", "production", "test"]),
48
+ debug: checkBoolean(),
49
+ url: checkString()
50
+ }
51
+ },
52
+ ports: {
53
+ rules: {
54
+ frontend: PORT_CHECK,
55
+ api: PORT_CHECK,
56
+ admin: PORT_CHECK,
57
+ docs: PORT_CHECK,
58
+ systemTray: PORT_CHECK,
59
+ desktop: PORT_CHECK
60
+ }
61
+ },
62
+ database: {
63
+ rules: {
64
+ default: checkOneOf(["sqlite", "mysql", "singlestore", "postgres", "dynamodb"])
65
+ }
66
+ },
67
+ cache: {
68
+ rules: {
69
+ driver: checkOneOf(["memory", "redis", "singlestore"])
70
+ }
71
+ },
72
+ featureFlags: {
73
+ rules: {
74
+ default: checkOneOf(["memory", "database"]),
75
+ missing: checkOneOf(["false", "throw"])
76
+ }
77
+ },
78
+ queue: {
79
+ rules: {
80
+ default: checkOneOf(["sync", "database", "redis"])
81
+ }
82
+ },
83
+ logging: {
84
+ rules: {
85
+ level: checkOneOf(["trace", "debug", "info", "warn", "error", "fatal"])
86
+ }
87
+ },
88
+ email: {
89
+ rules: {
90
+ default: checkOneOf(["ses", "sendgrid", "mailgun", "mailtrap", "smtp", "log", "capture"])
91
+ }
92
+ }
93
+ };
94
+ export function validateConfig(config) {
95
+ const issues = [];
96
+ for (const [section, schema] of Object.entries(SCHEMA)) {
97
+ const sectionValue = config[section];
98
+ if (sectionValue == null)
99
+ continue;
100
+ if (!isPlainObject(sectionValue)) {
101
+ issues.push({ path: section, message: `expected object, got ${typeof sectionValue}` });
102
+ continue;
103
+ }
104
+ for (const [field, check] of Object.entries(schema.rules)) {
105
+ const v = sectionValue[field];
106
+ issues.push(...check(v, `${section}.${field}`));
107
+ }
108
+ }
109
+ return issues;
110
+ }
111
+ export function reportConfigIssues(config) {
112
+ const issues = validateConfig(config);
113
+ if (issues.length === 0)
114
+ return !0;
115
+ console.warn("[config] Configuration issues detected:");
116
+ for (const issue of issues)
117
+ console.warn(` \u2022 ${issue.path}: ${issue.message}`);
118
+ return !1;
119
+ }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/config",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.88",
5
+ "version": "0.70.91",
6
6
  "description": "The Stacks config helper methods.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -53,9 +53,9 @@
53
53
  "ts-pantry": "^0.10.11"
54
54
  },
55
55
  "devDependencies": {
56
- "@stacksjs/alias": "0.70.88",
56
+ "@stacksjs/alias": "0.70.91",
57
57
  "better-dx": "^0.2.16",
58
- "@stacksjs/types": "0.70.88",
58
+ "@stacksjs/types": "0.70.91",
59
59
  "bunfig": "^0.15.11"
60
60
  }
61
61
  }