@luckystack/devkit 0.7.6 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.8.1] - 2026-07-27
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Hot-reload watchers use application runtime mode rather than deploy-topology identity.
|
|
15
|
+
|
|
16
|
+
## [0.8.0] - 2026-07-27
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Deploy validation now rejects malformed, typed-transport-overlapping, duplicate-normalized and unknown-service custom route ownership before deployment.
|
|
21
|
+
|
|
10
22
|
## [0.7.4] - 2026-07-22
|
|
11
23
|
|
|
12
24
|
### Fixed
|
|
@@ -44,6 +44,42 @@ var validateDeploy = ({
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
const normalizedCustomPrefixes = /* @__PURE__ */ new Set();
|
|
48
|
+
for (const [rawPrefix, owner] of Object.entries(services.customRoutes ?? {})) {
|
|
49
|
+
const prefix = rawPrefix.length > 1 && rawPrefix.endsWith("/") ? rawPrefix.slice(0, -1) : rawPrefix;
|
|
50
|
+
if (!prefix.startsWith("/") || prefix === "/" || prefix.includes("?") || prefix.includes("#")) {
|
|
51
|
+
findings.push({
|
|
52
|
+
severity: "error",
|
|
53
|
+
code: "custom-route-invalid-prefix",
|
|
54
|
+
message: `Custom route ownership prefix "${rawPrefix}" must be an absolute non-root path prefix without query/hash.`,
|
|
55
|
+
location: `services.config.ts > customRoutes.${rawPrefix}`
|
|
56
|
+
});
|
|
57
|
+
} else if (prefix === "/api" || prefix.startsWith("/api/") || prefix === "/sync" || prefix.startsWith("/sync/")) {
|
|
58
|
+
findings.push({
|
|
59
|
+
severity: "error",
|
|
60
|
+
code: "custom-route-overlaps-typed-transport",
|
|
61
|
+
message: `Custom route ownership prefix "${rawPrefix}" overlaps /api or /sync. Typed transport ownership is derived from the service-first route name.`,
|
|
62
|
+
location: `services.config.ts > customRoutes.${rawPrefix}`
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (normalizedCustomPrefixes.has(prefix)) {
|
|
66
|
+
findings.push({
|
|
67
|
+
severity: "error",
|
|
68
|
+
code: "custom-route-duplicate-prefix",
|
|
69
|
+
message: `Custom route ownership prefix "${rawPrefix}" duplicates normalized prefix "${prefix}".`,
|
|
70
|
+
location: `services.config.ts > customRoutes.${rawPrefix}`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
normalizedCustomPrefixes.add(prefix);
|
|
74
|
+
if (!serviceNames.has(owner)) {
|
|
75
|
+
findings.push({
|
|
76
|
+
severity: "error",
|
|
77
|
+
code: "custom-route-unknown-service",
|
|
78
|
+
message: `Custom route ownership prefix "${rawPrefix}" references unknown service "${owner}".`,
|
|
79
|
+
location: `services.config.ts > customRoutes.${rawPrefix}`
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
47
83
|
const environments = deploy.environments ?? {};
|
|
48
84
|
const resourceNames = new Set(Object.keys(deploy.resources));
|
|
49
85
|
for (const [envKey, envDef] of Object.entries(environments)) {
|
|
@@ -167,4 +203,4 @@ var validateDeploy = ({
|
|
|
167
203
|
export {
|
|
168
204
|
validateDeploy
|
|
169
205
|
};
|
|
170
|
-
//# sourceMappingURL=chunk-
|
|
206
|
+
//# sourceMappingURL=chunk-IWGWJU4M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/validateDeploy.ts"],"sourcesContent":["//? Pure validator for `services.config.ts` + `deploy.config.ts` content.\n//? Catches the classes of misconfiguration that otherwise only surface at\n//? runtime, often silently (empty service map served, fallback target\n//? unreachable, env var typo).\n//?\n//? Pure module so it can be unit-tested and reused outside the CLI — the\n//? CLI in `cli/validateDeploy.ts` is a thin wrapper that loads the config\n//? files, calls `validateDeploy(...)`, and prints results.\n\nimport type {\n DeployConfigShape,\n ServicesConfigShape,\n} from '@luckystack/core';\n\nexport type ValidationSeverity = 'error' | 'warning';\n\nexport interface ValidationFinding {\n severity: ValidationSeverity;\n code: string;\n message: string;\n /** Human-readable location pointer (e.g. `services.config.ts > presets.api`). */\n location?: string;\n}\n\nexport interface ValidateDeployInput {\n services: ServicesConfigShape;\n deploy: DeployConfigShape;\n /**\n * Environment values to consult for `synchronizedEnvKeys` / `urlEnvKey`\n * presence checks. Defaults to `process.env`. Pass a fixture for tests.\n */\n env?: Record<string, string | undefined>;\n}\n\nexport interface ValidateDeployResult {\n ok: boolean;\n findings: ValidationFinding[];\n errorCount: number;\n warningCount: number;\n}\n\nexport const validateDeploy = ({\n services,\n deploy,\n env = process.env,\n}: ValidateDeployInput): ValidateDeployResult => {\n const findings: ValidationFinding[] = [];\n const serviceNames = new Set(Object.keys(services.services));\n\n //? Rule 1: every service is assigned to exactly one preset.\n const servicesInPresets = new Map<string, string[]>();\n for (const [presetKey, preset] of Object.entries(services.presets)) {\n for (const service of preset.services) {\n const existing = servicesInPresets.get(service) ?? [];\n existing.push(presetKey);\n servicesInPresets.set(service, existing);\n }\n }\n\n for (const service of serviceNames) {\n const owners = servicesInPresets.get(service) ?? [];\n if (owners.length === 0) {\n findings.push({\n severity: 'error',\n code: 'service-unassigned',\n message: `Service \"${service}\" is declared in services.services but not assigned to any preset. Every service must belong to exactly one preset.`,\n location: `services.config.ts > services.${service}`,\n });\n } else if (owners.length > 1) {\n findings.push({\n severity: 'error',\n code: 'service-in-multiple-presets',\n message: `Service \"${service}\" is in multiple presets: ${owners.join(', ')}. A service may belong to exactly one preset.`,\n location: `services.config.ts > services.${service}`,\n });\n }\n }\n\n //? Rule 2: every preset references services that exist.\n for (const [presetKey, preset] of Object.entries(services.presets)) {\n for (const service of preset.services) {\n if (!serviceNames.has(service)) {\n findings.push({\n severity: 'error',\n code: 'preset-references-unknown-service',\n message: `Preset \"${presetKey}\" references unknown service \"${service}\". Add it to services.services or remove the reference.`,\n location: `services.config.ts > presets.${presetKey}.services`,\n });\n }\n }\n }\n\n //? Rule 3: every custom HTTP route prefix has a valid, known service owner.\n const normalizedCustomPrefixes = new Set<string>();\n for (const [rawPrefix, owner] of Object.entries(services.customRoutes ?? {})) {\n const prefix = rawPrefix.length > 1 && rawPrefix.endsWith('/')\n ? rawPrefix.slice(0, -1)\n : rawPrefix;\n if (!prefix.startsWith('/') || prefix === '/' || prefix.includes('?') || prefix.includes('#')) {\n findings.push({\n severity: 'error',\n code: 'custom-route-invalid-prefix',\n message: `Custom route ownership prefix \"${rawPrefix}\" must be an absolute non-root path prefix without query/hash.`,\n location: `services.config.ts > customRoutes.${rawPrefix}`,\n });\n } else if (prefix === '/api' || prefix.startsWith('/api/') || prefix === '/sync' || prefix.startsWith('/sync/')) {\n findings.push({\n severity: 'error',\n code: 'custom-route-overlaps-typed-transport',\n message: `Custom route ownership prefix \"${rawPrefix}\" overlaps /api or /sync. Typed transport ownership is derived from the service-first route name.`,\n location: `services.config.ts > customRoutes.${rawPrefix}`,\n });\n }\n if (normalizedCustomPrefixes.has(prefix)) {\n findings.push({\n severity: 'error',\n code: 'custom-route-duplicate-prefix',\n message: `Custom route ownership prefix \"${rawPrefix}\" duplicates normalized prefix \"${prefix}\".`,\n location: `services.config.ts > customRoutes.${rawPrefix}`,\n });\n }\n normalizedCustomPrefixes.add(prefix);\n if (!serviceNames.has(owner)) {\n findings.push({\n severity: 'error',\n code: 'custom-route-unknown-service',\n message: `Custom route ownership prefix \"${rawPrefix}\" references unknown service \"${owner}\".`,\n location: `services.config.ts > customRoutes.${rawPrefix}`,\n });\n }\n }\n\n //? Rule 4: every binding's service matches an actual service.\n //? Rule 4: fallback env key must exist.\n //? Rule 5: redis/mongo resource keys must exist.\n //? Rule 6: fallback shared-resource invariant.\n const environments = deploy.environments ?? {};\n const resourceNames = new Set(Object.keys(deploy.resources));\n\n for (const [envKey, envDef] of Object.entries(environments)) {\n for (const [bindingService, bindingUrl] of Object.entries(envDef.bindings)) {\n if (!serviceNames.has(bindingService)) {\n findings.push({\n severity: 'error',\n code: 'binding-references-unknown-service',\n message: `Environment \"${envKey}\" binds unknown service \"${bindingService}\". Either add it to services.services or remove the binding.`,\n location: `deploy.config.ts > environments.${envKey}.bindings`,\n });\n }\n\n //? Every binding MUST declare an explicit port. The URL-spec default\n //? (80/443) is almost never what a multi-instance deploy wants — a\n //? missing port is far more likely a typo than a deliberate \"route to\n //? port 80\". The router enforces this at boot too (resolveTarget.ts).\n let parsed: URL | null = null;\n try {\n parsed = new URL(bindingUrl);\n } catch {\n findings.push({\n severity: 'error',\n code: 'binding-invalid-url',\n message: `Environment \"${envKey}\" service \"${bindingService}\" binding is not a valid URL: \"${bindingUrl}\".`,\n location: `deploy.config.ts > environments.${envKey}.bindings.${bindingService}`,\n });\n }\n if (parsed && !parsed.port) {\n findings.push({\n severity: 'error',\n code: 'binding-missing-port',\n message: `Environment \"${envKey}\" service \"${bindingService}\" binding \"${bindingUrl}\" is missing an explicit port. Set one to avoid the URL-spec default (80/443) silently winning.`,\n location: `deploy.config.ts > environments.${envKey}.bindings.${bindingService}`,\n });\n }\n }\n\n if (!resourceNames.has(envDef.redis)) {\n findings.push({\n severity: 'error',\n code: 'unknown-redis-resource',\n message: `Environment \"${envKey}\" references unknown redis resource \"${envDef.redis}\". Add it to deploy.resources.`,\n location: `deploy.config.ts > environments.${envKey}.redis`,\n });\n }\n if (!resourceNames.has(envDef.mongo)) {\n findings.push({\n severity: 'error',\n code: 'unknown-mongo-resource',\n message: `Environment \"${envKey}\" references unknown mongo resource \"${envDef.mongo}\". Add it to deploy.resources.`,\n location: `deploy.config.ts > environments.${envKey}.mongo`,\n });\n }\n\n if (envDef.fallback) {\n const fallbackEnv = environments[envDef.fallback];\n //? `fallbackEnv` reads from `environments[key]` — TS treats this as\n //? always-defined (Record lookup), but the runtime can return undefined\n //? when the user names a fallback env that doesn't exist. The else\n //? branch reports that error.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Record lookup can return undefined at runtime\n if (fallbackEnv) {\n if (fallbackEnv.redis !== envDef.redis) {\n findings.push({\n severity: 'error',\n code: 'fallback-redis-mismatch',\n message: `Environment \"${envKey}\" and its fallback \"${envDef.fallback}\" reference different redis resources (\"${envDef.redis}\" vs \"${fallbackEnv.redis}\"). Fallback envs must share the same redis resource key to keep the shared-Redis invariant.`,\n location: `deploy.config.ts > environments.${envKey}.fallback`,\n });\n }\n if (fallbackEnv.mongo !== envDef.mongo) {\n findings.push({\n severity: 'error',\n code: 'fallback-mongo-mismatch',\n message: `Environment \"${envKey}\" and its fallback \"${envDef.fallback}\" reference different mongo resources (\"${envDef.mongo}\" vs \"${fallbackEnv.mongo}\"). Fallback envs must share the same mongo resource key.`,\n location: `deploy.config.ts > environments.${envKey}.fallback`,\n });\n }\n } else {\n findings.push({\n severity: 'error',\n code: 'unknown-fallback-env',\n message: `Environment \"${envKey}\" has fallback \"${envDef.fallback}\" which does not exist in deploy.environments.`,\n location: `deploy.config.ts > environments.${envKey}.fallback`,\n });\n }\n }\n }\n\n //? Rule 7+8: env var references must resolve at config time.\n for (const [resourceKey, resource] of Object.entries(deploy.resources)) {\n const urlValue = env[resource.urlEnvKey];\n if (urlValue === undefined || urlValue === '') {\n findings.push({\n severity: 'warning',\n code: 'missing-resource-env-var',\n message: `Resource \"${resourceKey}\" expects env var \"${resource.urlEnvKey}\" but it is unset. The framework will still boot if the env is provided at runtime, but config-time tooling cannot verify the value.`,\n location: `deploy.config.ts > resources.${resourceKey}.urlEnvKey`,\n });\n }\n\n for (const synchronizedKey of resource.synchronizedEnvKeys ?? []) {\n const value = env[synchronizedKey];\n if (value === undefined || value === '') {\n findings.push({\n severity: 'warning',\n code: 'missing-synchronized-env-var',\n message: `Resource \"${resourceKey}\" requires synchronized env var \"${synchronizedKey}\" but it is unset. Boot will compute an empty hash and fall back to warning-only.`,\n location: `deploy.config.ts > resources.${resourceKey}.synchronizedEnvKeys`,\n });\n }\n }\n }\n\n //? Rule 9: services declared in services.config but not bound in any\n //? environment will never receive traffic. Warning, not error — that's a\n //? valid intermediate state during a rollout.\n for (const service of serviceNames) {\n const boundIn = Object.entries(environments).filter(([, envDef]) => service in envDef.bindings);\n if (boundIn.length === 0) {\n findings.push({\n severity: 'warning',\n code: 'service-bound-in-no-environment',\n message: `Service \"${service}\" is in services.services and assigned to a preset but never bound in any environment. Requests for this service will fall through to the missing-service handler.`,\n location: `services.config.ts > services.${service}`,\n });\n }\n }\n\n const errorCount = findings.filter((f) => f.severity === 'error').length;\n const warningCount = findings.filter((f) => f.severity === 'warning').length;\n\n return {\n ok: errorCount === 0,\n findings,\n errorCount,\n warningCount,\n };\n};\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
validateDeploy
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-IWGWJU4M.js";
|
|
4
4
|
|
|
5
5
|
// src/typeMap/discovery.ts
|
|
6
6
|
import fs from "fs";
|
|
@@ -4640,7 +4640,7 @@ var runPrismaGenerate = async () => tryCatch2(
|
|
|
4640
4640
|
);
|
|
4641
4641
|
|
|
4642
4642
|
// src/hotReload.ts
|
|
4643
|
-
import { tryCatch as tryCatch3, getProjectConfig, getLocaleReloader,
|
|
4643
|
+
import { tryCatch as tryCatch3, getProjectConfig, getLocaleReloader, isProductionRuntime } from "@luckystack/core";
|
|
4644
4644
|
var normalizeFsPath = (value) => path13.resolve(value).replaceAll("\\", "/");
|
|
4645
4645
|
var isGeneratedPath = (normalizedPath) => {
|
|
4646
4646
|
return normalizedPath.includes("apiTypes.generated.ts") || normalizedPath.includes("apiDocs.generated.json");
|
|
@@ -4751,7 +4751,7 @@ var mountWatchers = (onAdd, onChange, onDelete, onFunctionChange) => {
|
|
|
4751
4751
|
watch(pathsConfig.sharedDir, { ignoreInitial: true }).on("add", onFunctionChange).on("change", onFunctionChange).on("unlink", onFunctionChange);
|
|
4752
4752
|
};
|
|
4753
4753
|
var setupWatchers = () => {
|
|
4754
|
-
const isDevMode =
|
|
4754
|
+
const isDevMode = !isProductionRuntime();
|
|
4755
4755
|
if (!isDevMode) return;
|
|
4756
4756
|
const segments = buildPathSegments();
|
|
4757
4757
|
const isInServerFunctionsDir = makeIsInServerFunctionsDir(segments);
|