@craft-ts/deploy 0.7.0-beta.15

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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/package.json +42 -0
  4. package/src/index.d.ts +18 -0
  5. package/src/index.d.ts.map +1 -0
  6. package/src/index.js +10 -0
  7. package/src/index.js.map +1 -0
  8. package/src/lib/artifact.d.ts +18 -0
  9. package/src/lib/artifact.d.ts.map +1 -0
  10. package/src/lib/artifact.js +121 -0
  11. package/src/lib/artifact.js.map +1 -0
  12. package/src/lib/check.d.ts +31 -0
  13. package/src/lib/check.d.ts.map +1 -0
  14. package/src/lib/check.js +278 -0
  15. package/src/lib/check.js.map +1 -0
  16. package/src/lib/diagnostics.d.ts +38 -0
  17. package/src/lib/diagnostics.d.ts.map +1 -0
  18. package/src/lib/diagnostics.js +297 -0
  19. package/src/lib/diagnostics.js.map +1 -0
  20. package/src/lib/format.d.ts +17 -0
  21. package/src/lib/format.d.ts.map +1 -0
  22. package/src/lib/format.js +42 -0
  23. package/src/lib/format.js.map +1 -0
  24. package/src/lib/manifest.d.ts +236 -0
  25. package/src/lib/manifest.d.ts.map +1 -0
  26. package/src/lib/manifest.js +47 -0
  27. package/src/lib/manifest.js.map +1 -0
  28. package/src/lib/protocol.d.ts +22 -0
  29. package/src/lib/protocol.d.ts.map +1 -0
  30. package/src/lib/protocol.js +182 -0
  31. package/src/lib/protocol.js.map +1 -0
  32. package/src/lib/providers.d.ts +111 -0
  33. package/src/lib/providers.d.ts.map +1 -0
  34. package/src/lib/providers.js +156 -0
  35. package/src/lib/providers.js.map +1 -0
  36. package/src/lib/sources.d.ts +31 -0
  37. package/src/lib/sources.d.ts.map +1 -0
  38. package/src/lib/sources.js +155 -0
  39. package/src/lib/sources.js.map +1 -0
  40. package/src/lib/validate.d.ts +16 -0
  41. package/src/lib/validate.d.ts.map +1 -0
  42. package/src/lib/validate.js +361 -0
  43. package/src/lib/validate.js.map +1 -0
@@ -0,0 +1,111 @@
1
+ import type { CraftDeploymentDiagnostic } from './diagnostics.js';
2
+ import type { CraftDeploymentManifest, CraftDeploymentPlatform, CraftDeploymentRuntime, CraftStaticMode } from './manifest.js';
3
+ export declare const CRAFT_DEPLOYMENT_CAPABILITIES: readonly ["static-spa", "static-ssg", "node-ssr", "worker", "lambda", "infrastructure", "local-preview"];
4
+ export type CraftDeploymentCapability = (typeof CRAFT_DEPLOYMENT_CAPABILITIES)[number];
5
+ export type CraftDeploymentResult = Readonly<{
6
+ provider: string;
7
+ stage: string;
8
+ /** Public URL of the deployed application, when the provider exposes one. */
9
+ url?: string;
10
+ /** Provider outputs worth keeping, such as resource identifiers. */
11
+ outputs: Readonly<Record<string, string>>;
12
+ }>;
13
+ /**
14
+ * Everything a provider is given. The manifest alone is not enough: the same
15
+ * artefact is deployed to several stages, and the paths it declares are only
16
+ * meaningful against the root they were checked from.
17
+ */
18
+ export type CraftDeploymentRequest = Readonly<{
19
+ manifest: CraftDeploymentManifest;
20
+ /** Directory the manifest paths are relative to. */
21
+ rootDir: string;
22
+ /** Target stage, e.g. `production` or `preview-42`. */
23
+ stage: string;
24
+ }>;
25
+ export declare const CRAFT_DEPLOYMENT_RESOURCE_ACTIONS: readonly ["create", "update", "delete", "unchanged"];
26
+ export type CraftDeploymentResourceAction = (typeof CRAFT_DEPLOYMENT_RESOURCE_ACTIONS)[number];
27
+ /** One resource a deployment would touch, as reported by a preview. */
28
+ export type CraftDeploymentPlannedResource = Readonly<{
29
+ /** Provider-specific resource type, e.g. `cloudflare:Worker`. */
30
+ type: string;
31
+ name: string;
32
+ action: CraftDeploymentResourceAction;
33
+ /** Facts an operator needs to approve the change, never secrets. */
34
+ details: Readonly<Record<string, string>>;
35
+ }>;
36
+ /**
37
+ * What a deployment would do, without doing it.
38
+ *
39
+ * A preview is the approval surface: it has to name every resource and every
40
+ * action before anything is created, which is why `preview` returns a plan
41
+ * instead of printing one.
42
+ */
43
+ export type CraftDeploymentPlan = Readonly<{
44
+ provider: string;
45
+ stage: string;
46
+ resources: readonly CraftDeploymentPlannedResource[];
47
+ /** Things an operator should know that are not failures. */
48
+ notes: readonly string[];
49
+ }>;
50
+ /**
51
+ * A deployment integration. The CLI owns the manifest and delegates every
52
+ * mutation to an implementation of this contract, which is why no provider is
53
+ * a dependency of the CraftTS runtime.
54
+ *
55
+ * `check` reports instead of throwing so its diagnostics join the ones of
56
+ * `craft-ts check`; `preview` must never mutate anything.
57
+ */
58
+ export type CraftDeploymentProvider = Readonly<{
59
+ name: string;
60
+ capabilities: readonly CraftDeploymentCapability[];
61
+ check?(request: CraftDeploymentRequest): Promise<readonly CraftDeploymentDiagnostic[]>;
62
+ preview(request: CraftDeploymentRequest): Promise<CraftDeploymentPlan>;
63
+ deploy(request: CraftDeploymentRequest): Promise<CraftDeploymentResult>;
64
+ }>;
65
+ /**
66
+ * What a provider package must export.
67
+ *
68
+ * The CLI resolves `@craft-ts/deploy-<name>` at run time and reads this single
69
+ * factory, so adding a provider never means changing the CLI.
70
+ */
71
+ export type CraftDeploymentProviderModule = Readonly<{
72
+ createCraftDeploymentProvider(options?: Readonly<Record<string, unknown>>): CraftDeploymentProvider;
73
+ }>;
74
+ /** Narrows an unknown dynamic import to the provider module contract. */
75
+ export declare function isCraftDeploymentProviderModule(value: unknown): value is CraftDeploymentProviderModule;
76
+ /**
77
+ * What a provider entry has to document. A capability list alone is not
78
+ * actionable: an operator needs the artefact shape, the local preview command,
79
+ * the credential mechanism and the known limits before choosing.
80
+ */
81
+ export type CraftDeploymentProviderDescriptor = Readonly<{
82
+ name: string;
83
+ capabilities: readonly CraftDeploymentCapability[];
84
+ platforms: readonly CraftDeploymentPlatform[];
85
+ /** Artefact the provider consumes. */
86
+ artifact: string;
87
+ /** Local preview command, or `null` when the provider offers none. */
88
+ previewCommand: string | null;
89
+ /** How credentials reach the provider. */
90
+ credentials: string;
91
+ limits: readonly string[];
92
+ }>;
93
+ /**
94
+ * Initial capability matrix. Adding a provider here documents it; it does not
95
+ * make it an implementation of `CraftDeploymentProvider` in this package.
96
+ */
97
+ export declare const CRAFT_DEPLOYMENT_PROVIDERS: readonly CraftDeploymentProviderDescriptor[];
98
+ export declare function findCraftDeploymentProvider(name: string): CraftDeploymentProviderDescriptor | undefined;
99
+ /**
100
+ * Capability a manifest requires from a provider. The static runtime splits
101
+ * into two capabilities because pre-rendering and SPA fallback are different
102
+ * publication contracts.
103
+ */
104
+ export declare function requiredCapability(runtime: CraftDeploymentRuntime, staticMode?: CraftStaticMode): CraftDeploymentCapability;
105
+ /**
106
+ * Platforms able to execute a given runtime. A pair absent from this table is
107
+ * not a missing integration: nothing on that platform runs that shape.
108
+ */
109
+ export declare const CRAFT_RUNTIME_PLATFORMS: Readonly<Record<CraftDeploymentRuntime, readonly CraftDeploymentPlatform[]>>;
110
+ export declare function isRuntimeSupportedByPlatform(runtime: CraftDeploymentRuntime, platform: CraftDeploymentPlatform): boolean;
111
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../../../../libs/deploy/src/lib/providers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ export const CRAFT_DEPLOYMENT_CAPABILITIES = [
2
+ 'static-spa',
3
+ 'static-ssg',
4
+ 'node-ssr',
5
+ 'worker',
6
+ 'lambda',
7
+ 'infrastructure',
8
+ 'local-preview',
9
+ ];
10
+ export const CRAFT_DEPLOYMENT_RESOURCE_ACTIONS = [
11
+ 'create',
12
+ 'update',
13
+ 'delete',
14
+ 'unchanged',
15
+ ];
16
+ /** Narrows an unknown dynamic import to the provider module contract. */
17
+ export function isCraftDeploymentProviderModule(value) {
18
+ return (typeof value === 'object' &&
19
+ value !== null &&
20
+ typeof value
21
+ .createCraftDeploymentProvider === 'function');
22
+ }
23
+ /**
24
+ * Initial capability matrix. Adding a provider here documents it; it does not
25
+ * make it an implementation of `CraftDeploymentProvider` in this package.
26
+ */
27
+ export const CRAFT_DEPLOYMENT_PROVIDERS = Object.freeze([
28
+ Object.freeze({
29
+ name: 'alchemy',
30
+ capabilities: [
31
+ 'static-spa',
32
+ 'static-ssg',
33
+ 'node-ssr',
34
+ 'worker',
35
+ 'lambda',
36
+ 'infrastructure',
37
+ 'local-preview',
38
+ ],
39
+ platforms: ['cloudflare', 'aws'],
40
+ artifact: 'Public directory plus the runtime entry declared by the manifest.',
41
+ previewCommand: 'craft-ts deploy preview --provider alchemy',
42
+ credentials: 'Cloudflare or AWS credentials read from the environment by the Alchemy CLI.',
43
+ limits: [
44
+ 'Requires the Alchemy CLI and a reachable state backend.',
45
+ 'Shipped as the separate package `@craft-ts/deploy-alchemy`.',
46
+ ],
47
+ }),
48
+ Object.freeze({
49
+ name: 'docker',
50
+ capabilities: ['node-ssr', 'local-preview'],
51
+ platforms: ['docker', 'node'],
52
+ artifact: 'Image built from the SSR entry and the client output.',
53
+ previewCommand: 'docker compose -f docker-compose.production.yml up',
54
+ credentials: 'Registry credentials handled by the Docker CLI.',
55
+ limits: [
56
+ 'No static-only publication path: a plain bucket is cheaper.',
57
+ 'Provisioning of the host is out of scope.',
58
+ ],
59
+ }),
60
+ Object.freeze({
61
+ name: 'cloudflare-pages',
62
+ capabilities: ['static-spa', 'static-ssg'],
63
+ platforms: ['cloudflare'],
64
+ artifact: 'Public directory uploaded as-is.',
65
+ previewCommand: 'wrangler pages dev <publicDir>',
66
+ credentials: '`CLOUDFLARE_API_TOKEN` read by Wrangler.',
67
+ limits: ['SSR and Worker runtimes need a Worker deployment, not Pages.'],
68
+ }),
69
+ Object.freeze({
70
+ name: 'vercel',
71
+ capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'local-preview'],
72
+ platforms: ['vercel'],
73
+ artifact: 'Public directory plus an optional Node server entry.',
74
+ previewCommand: 'vercel dev',
75
+ credentials: '`VERCEL_TOKEN` read by the Vercel CLI.',
76
+ limits: [
77
+ 'Worker and Lambda runtimes map to platform-specific functions and are not covered by this matrix.',
78
+ 'Infrastructure provisioning is partial and platform-owned.',
79
+ ],
80
+ }),
81
+ Object.freeze({
82
+ name: 'netlify',
83
+ capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'lambda'],
84
+ platforms: ['netlify'],
85
+ artifact: 'Public directory plus a functions directory.',
86
+ previewCommand: 'netlify dev',
87
+ credentials: '`NETLIFY_AUTH_TOKEN` read by the Netlify CLI.',
88
+ limits: [
89
+ 'The Lambda capability is served by Netlify Functions, not by AWS Function URLs.',
90
+ 'No infrastructure provisioning.',
91
+ ],
92
+ }),
93
+ Object.freeze({
94
+ name: 'firebase',
95
+ capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'lambda'],
96
+ platforms: ['firebase'],
97
+ artifact: 'Hosting public directory plus Cloud Functions.',
98
+ previewCommand: 'firebase emulators:start',
99
+ credentials: 'Firebase CLI login or a service account key.',
100
+ limits: [
101
+ 'No Worker runtime.',
102
+ 'Infrastructure provisioning is partial and project-scoped.',
103
+ ],
104
+ }),
105
+ Object.freeze({
106
+ name: 'github-pages',
107
+ capabilities: ['static-spa', 'static-ssg'],
108
+ platforms: ['github-pages'],
109
+ artifact: 'Public directory published as a Pages artefact.',
110
+ previewCommand: null,
111
+ credentials: 'The `GITHUB_TOKEN` of the publishing workflow.',
112
+ limits: [
113
+ 'No server runtime at all.',
114
+ 'SPA fallback requires a `404.html` copy of the fallback document.',
115
+ ],
116
+ }),
117
+ ]);
118
+ export function findCraftDeploymentProvider(name) {
119
+ return CRAFT_DEPLOYMENT_PROVIDERS.find((provider) => provider.name === name);
120
+ }
121
+ /**
122
+ * Capability a manifest requires from a provider. The static runtime splits
123
+ * into two capabilities because pre-rendering and SPA fallback are different
124
+ * publication contracts.
125
+ */
126
+ export function requiredCapability(runtime, staticMode) {
127
+ if (runtime === 'static') {
128
+ return staticMode === 'ssg' ? 'static-ssg' : 'static-spa';
129
+ }
130
+ if (runtime === 'node')
131
+ return 'node-ssr';
132
+ return runtime;
133
+ }
134
+ /**
135
+ * Platforms able to execute a given runtime. A pair absent from this table is
136
+ * not a missing integration: nothing on that platform runs that shape.
137
+ */
138
+ export const CRAFT_RUNTIME_PLATFORMS = Object.freeze({
139
+ static: [
140
+ 'node',
141
+ 'docker',
142
+ 'cloudflare',
143
+ 'aws',
144
+ 'vercel',
145
+ 'netlify',
146
+ 'firebase',
147
+ 'github-pages',
148
+ ],
149
+ node: ['node', 'docker', 'aws', 'vercel', 'netlify', 'firebase'],
150
+ worker: ['cloudflare'],
151
+ lambda: ['aws', 'netlify', 'firebase'],
152
+ });
153
+ export function isRuntimeSupportedByPlatform(runtime, platform) {
154
+ return CRAFT_RUNTIME_PLATFORMS[runtime].includes(platform);
155
+ }
156
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../../../../../libs/deploy/src/lib/providers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CraftDeploymentDiagnostic } from './diagnostics.js';\nimport type {\n CraftDeploymentManifest,\n CraftDeploymentPlatform,\n CraftDeploymentRuntime,\n CraftStaticMode,\n} from './manifest.js';\n\nexport const CRAFT_DEPLOYMENT_CAPABILITIES = [\n 'static-spa',\n 'static-ssg',\n 'node-ssr',\n 'worker',\n 'lambda',\n 'infrastructure',\n 'local-preview',\n] as const;\n\nexport type CraftDeploymentCapability =\n (typeof CRAFT_DEPLOYMENT_CAPABILITIES)[number];\n\nexport type CraftDeploymentResult = Readonly<{\n provider: string;\n stage: string;\n /** Public URL of the deployed application, when the provider exposes one. */\n url?: string;\n /** Provider outputs worth keeping, such as resource identifiers. */\n outputs: Readonly<Record<string, string>>;\n}>;\n\n/**\n * Everything a provider is given. The manifest alone is not enough: the same\n * artefact is deployed to several stages, and the paths it declares are only\n * meaningful against the root they were checked from.\n */\nexport type CraftDeploymentRequest = Readonly<{\n manifest: CraftDeploymentManifest;\n /** Directory the manifest paths are relative to. */\n rootDir: string;\n /** Target stage, e.g. `production` or `preview-42`. */\n stage: string;\n}>;\n\nexport const CRAFT_DEPLOYMENT_RESOURCE_ACTIONS = [\n 'create',\n 'update',\n 'delete',\n 'unchanged',\n] as const;\n\nexport type CraftDeploymentResourceAction =\n (typeof CRAFT_DEPLOYMENT_RESOURCE_ACTIONS)[number];\n\n/** One resource a deployment would touch, as reported by a preview. */\nexport type CraftDeploymentPlannedResource = Readonly<{\n /** Provider-specific resource type, e.g. `cloudflare:Worker`. */\n type: string;\n name: string;\n action: CraftDeploymentResourceAction;\n /** Facts an operator needs to approve the change, never secrets. */\n details: Readonly<Record<string, string>>;\n}>;\n\n/**\n * What a deployment would do, without doing it.\n *\n * A preview is the approval surface: it has to name every resource and every\n * action before anything is created, which is why `preview` returns a plan\n * instead of printing one.\n */\nexport type CraftDeploymentPlan = Readonly<{\n provider: string;\n stage: string;\n resources: readonly CraftDeploymentPlannedResource[];\n /** Things an operator should know that are not failures. */\n notes: readonly string[];\n}>;\n\n/**\n * A deployment integration. The CLI owns the manifest and delegates every\n * mutation to an implementation of this contract, which is why no provider is\n * a dependency of the CraftTS runtime.\n *\n * `check` reports instead of throwing so its diagnostics join the ones of\n * `craft-ts check`; `preview` must never mutate anything.\n */\nexport type CraftDeploymentProvider = Readonly<{\n name: string;\n capabilities: readonly CraftDeploymentCapability[];\n check?(\n request: CraftDeploymentRequest,\n ): Promise<readonly CraftDeploymentDiagnostic[]>;\n preview(request: CraftDeploymentRequest): Promise<CraftDeploymentPlan>;\n deploy(request: CraftDeploymentRequest): Promise<CraftDeploymentResult>;\n}>;\n\n/**\n * What a provider package must export.\n *\n * The CLI resolves `@craft-ts/deploy-<name>` at run time and reads this single\n * factory, so adding a provider never means changing the CLI.\n */\nexport type CraftDeploymentProviderModule = Readonly<{\n createCraftDeploymentProvider(\n options?: Readonly<Record<string, unknown>>,\n ): CraftDeploymentProvider;\n}>;\n\n/** Narrows an unknown dynamic import to the provider module contract. */\nexport function isCraftDeploymentProviderModule(\n value: unknown,\n): value is CraftDeploymentProviderModule {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as CraftDeploymentProviderModule)\n .createCraftDeploymentProvider === 'function'\n );\n}\n\n/**\n * What a provider entry has to document. A capability list alone is not\n * actionable: an operator needs the artefact shape, the local preview command,\n * the credential mechanism and the known limits before choosing.\n */\nexport type CraftDeploymentProviderDescriptor = Readonly<{\n name: string;\n capabilities: readonly CraftDeploymentCapability[];\n platforms: readonly CraftDeploymentPlatform[];\n /** Artefact the provider consumes. */\n artifact: string;\n /** Local preview command, or `null` when the provider offers none. */\n previewCommand: string | null;\n /** How credentials reach the provider. */\n credentials: string;\n limits: readonly string[];\n}>;\n\n/**\n * Initial capability matrix. Adding a provider here documents it; it does not\n * make it an implementation of `CraftDeploymentProvider` in this package.\n */\nexport const CRAFT_DEPLOYMENT_PROVIDERS: readonly CraftDeploymentProviderDescriptor[] =\n Object.freeze([\n Object.freeze({\n name: 'alchemy',\n capabilities: [\n 'static-spa',\n 'static-ssg',\n 'node-ssr',\n 'worker',\n 'lambda',\n 'infrastructure',\n 'local-preview',\n ],\n platforms: ['cloudflare', 'aws'],\n artifact:\n 'Public directory plus the runtime entry declared by the manifest.',\n previewCommand: 'craft-ts deploy preview --provider alchemy',\n credentials:\n 'Cloudflare or AWS credentials read from the environment by the Alchemy CLI.',\n limits: [\n 'Requires the Alchemy CLI and a reachable state backend.',\n 'Shipped as the separate package `@craft-ts/deploy-alchemy`.',\n ],\n } as const),\n Object.freeze({\n name: 'docker',\n capabilities: ['node-ssr', 'local-preview'],\n platforms: ['docker', 'node'],\n artifact: 'Image built from the SSR entry and the client output.',\n previewCommand: 'docker compose -f docker-compose.production.yml up',\n credentials: 'Registry credentials handled by the Docker CLI.',\n limits: [\n 'No static-only publication path: a plain bucket is cheaper.',\n 'Provisioning of the host is out of scope.',\n ],\n } as const),\n Object.freeze({\n name: 'cloudflare-pages',\n capabilities: ['static-spa', 'static-ssg'],\n platforms: ['cloudflare'],\n artifact: 'Public directory uploaded as-is.',\n previewCommand: 'wrangler pages dev <publicDir>',\n credentials: '`CLOUDFLARE_API_TOKEN` read by Wrangler.',\n limits: ['SSR and Worker runtimes need a Worker deployment, not Pages.'],\n } as const),\n Object.freeze({\n name: 'vercel',\n capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'local-preview'],\n platforms: ['vercel'],\n artifact: 'Public directory plus an optional Node server entry.',\n previewCommand: 'vercel dev',\n credentials: '`VERCEL_TOKEN` read by the Vercel CLI.',\n limits: [\n 'Worker and Lambda runtimes map to platform-specific functions and are not covered by this matrix.',\n 'Infrastructure provisioning is partial and platform-owned.',\n ],\n } as const),\n Object.freeze({\n name: 'netlify',\n capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'lambda'],\n platforms: ['netlify'],\n artifact: 'Public directory plus a functions directory.',\n previewCommand: 'netlify dev',\n credentials: '`NETLIFY_AUTH_TOKEN` read by the Netlify CLI.',\n limits: [\n 'The Lambda capability is served by Netlify Functions, not by AWS Function URLs.',\n 'No infrastructure provisioning.',\n ],\n } as const),\n Object.freeze({\n name: 'firebase',\n capabilities: ['static-spa', 'static-ssg', 'node-ssr', 'lambda'],\n platforms: ['firebase'],\n artifact: 'Hosting public directory plus Cloud Functions.',\n previewCommand: 'firebase emulators:start',\n credentials: 'Firebase CLI login or a service account key.',\n limits: [\n 'No Worker runtime.',\n 'Infrastructure provisioning is partial and project-scoped.',\n ],\n } as const),\n Object.freeze({\n name: 'github-pages',\n capabilities: ['static-spa', 'static-ssg'],\n platforms: ['github-pages'],\n artifact: 'Public directory published as a Pages artefact.',\n previewCommand: null,\n credentials: 'The `GITHUB_TOKEN` of the publishing workflow.',\n limits: [\n 'No server runtime at all.',\n 'SPA fallback requires a `404.html` copy of the fallback document.',\n ],\n } as const),\n ]);\n\nexport function findCraftDeploymentProvider(\n name: string,\n): CraftDeploymentProviderDescriptor | undefined {\n return CRAFT_DEPLOYMENT_PROVIDERS.find((provider) => provider.name === name);\n}\n\n/**\n * Capability a manifest requires from a provider. The static runtime splits\n * into two capabilities because pre-rendering and SPA fallback are different\n * publication contracts.\n */\nexport function requiredCapability(\n runtime: CraftDeploymentRuntime,\n staticMode?: CraftStaticMode,\n): CraftDeploymentCapability {\n if (runtime === 'static') {\n return staticMode === 'ssg' ? 'static-ssg' : 'static-spa';\n }\n if (runtime === 'node') return 'node-ssr';\n return runtime;\n}\n\n/**\n * Platforms able to execute a given runtime. A pair absent from this table is\n * not a missing integration: nothing on that platform runs that shape.\n */\nexport const CRAFT_RUNTIME_PLATFORMS: Readonly<\n Record<CraftDeploymentRuntime, readonly CraftDeploymentPlatform[]>\n> = Object.freeze({\n static: [\n 'node',\n 'docker',\n 'cloudflare',\n 'aws',\n 'vercel',\n 'netlify',\n 'firebase',\n 'github-pages',\n ],\n node: ['node', 'docker', 'aws', 'vercel', 'netlify', 'firebase'],\n worker: ['cloudflare'],\n lambda: ['aws', 'netlify', 'firebase'],\n});\n\nexport function isRuntimeSupportedByPlatform(\n runtime: CraftDeploymentRuntime,\n platform: CraftDeploymentPlatform,\n): boolean {\n return CRAFT_RUNTIME_PLATFORMS[runtime].includes(platform);\n}\n"]}
@@ -0,0 +1,31 @@
1
+ export type CraftModuleImport = Readonly<{
2
+ /** Absolute path of the importing file. */
3
+ file: string;
4
+ line: number;
5
+ specifier: string;
6
+ }>;
7
+ export type CraftModuleGraph = Readonly<{
8
+ /** Entry that could not be read, when the walk found nothing. */
9
+ missingEntry: string | null;
10
+ /** Absolute path of every file reachable from the entry. */
11
+ files: readonly string[];
12
+ /** Concatenated source of those files, comments removed. */
13
+ source: string;
14
+ imports: readonly CraftModuleImport[];
15
+ }>;
16
+ /**
17
+ * Walks the module graph reachable from an entry through relative imports.
18
+ *
19
+ * Only relative specifiers are followed: a package boundary is where the
20
+ * application stops being responsible for the platform APIs used, and reading
21
+ * `node_modules` would make the check unbounded and slow.
22
+ */
23
+ export declare function readCraftModuleGraph(entry: string): CraftModuleGraph;
24
+ export declare function isNodeBuiltin(specifier: string): boolean;
25
+ /**
26
+ * Environment variables the sources read. Both shapes are collected: the Node
27
+ * `process.env.NAME` and the Worker/Lambda `env.NAME` handed to the fetch
28
+ * handler.
29
+ */
30
+ export declare function collectEnvironmentReads(source: string): readonly string[];
31
+ //# sourceMappingURL=sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.d.ts","sourceRoot":"","sources":["../../../../../libs/deploy/src/lib/sources.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,CAAC;IACtC,iEAAiE;IACjE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,4DAA4D;IAC5D,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACvC,CAAC,CAAC;AAyBH;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,CAwCpE;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAczE"}
@@ -0,0 +1,155 @@
1
+ import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import { builtinModules } from 'node:module';
3
+ import { dirname, resolve } from 'node:path';
4
+ const EXTENSIONS = ['.ts', '.mts', '.tsx', '.js', '.mjs', '.jsx', '.cjs'];
5
+ const NODE_BUILTINS = new Set(builtinModules);
6
+ /**
7
+ * Names every platform and every bundler already provides. Reporting them as
8
+ * undeclared would make the diagnostic noisy enough to be ignored.
9
+ */
10
+ const AMBIENT_ENVIRONMENT = new Set([
11
+ 'NODE_ENV',
12
+ 'PROD',
13
+ 'DEV',
14
+ 'MODE',
15
+ 'SSR',
16
+ 'BASE_URL',
17
+ ]);
18
+ const IMPORT_PATTERNS = [
19
+ /\bfrom\s*['"]([^'"]+)['"]/g,
20
+ /\bimport\s*['"]([^'"]+)['"]/g,
21
+ /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
22
+ /\brequire\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
23
+ ];
24
+ /**
25
+ * Walks the module graph reachable from an entry through relative imports.
26
+ *
27
+ * Only relative specifiers are followed: a package boundary is where the
28
+ * application stops being responsible for the platform APIs used, and reading
29
+ * `node_modules` would make the check unbounded and slow.
30
+ */
31
+ export function readCraftModuleGraph(entry) {
32
+ const resolvedEntry = resolveModule(entry);
33
+ if (!resolvedEntry) {
34
+ return { missingEntry: entry, files: [], source: '', imports: [] };
35
+ }
36
+ const files = [];
37
+ const imports = [];
38
+ const sources = [];
39
+ const queue = [resolvedEntry];
40
+ const seen = new Set([resolvedEntry]);
41
+ while (queue.length > 0) {
42
+ const file = queue.shift();
43
+ let raw;
44
+ try {
45
+ raw = readFileSync(file, 'utf8');
46
+ }
47
+ catch {
48
+ continue;
49
+ }
50
+ const text = stripComments(raw);
51
+ files.push(file);
52
+ sources.push(text);
53
+ for (const specifier of collectImports(text, file, imports)) {
54
+ if (!specifier.startsWith('.'))
55
+ continue;
56
+ const target = resolveModule(resolve(dirname(file), specifier));
57
+ if (target && !seen.has(target)) {
58
+ seen.add(target);
59
+ queue.push(target);
60
+ }
61
+ }
62
+ }
63
+ return {
64
+ missingEntry: null,
65
+ files,
66
+ source: sources.join('\n'),
67
+ imports,
68
+ };
69
+ }
70
+ export function isNodeBuiltin(specifier) {
71
+ if (specifier.startsWith('node:'))
72
+ return true;
73
+ return NODE_BUILTINS.has(specifier);
74
+ }
75
+ /**
76
+ * Environment variables the sources read. Both shapes are collected: the Node
77
+ * `process.env.NAME` and the Worker/Lambda `env.NAME` handed to the fetch
78
+ * handler.
79
+ */
80
+ export function collectEnvironmentReads(source) {
81
+ const names = new Set();
82
+ const patterns = [
83
+ /\bprocess\.env\.([A-Z][A-Z0-9_]*)\b/g,
84
+ /\bprocess\.env\[\s*['"]([A-Z][A-Z0-9_]*)['"]\s*\]/g,
85
+ /\benv\.([A-Z][A-Z0-9_]*)\b/g,
86
+ ];
87
+ for (const pattern of patterns) {
88
+ for (const match of source.matchAll(pattern)) {
89
+ const name = match[1];
90
+ if (name && !AMBIENT_ENVIRONMENT.has(name))
91
+ names.add(name);
92
+ }
93
+ }
94
+ return [...names].sort();
95
+ }
96
+ function collectImports(text, file, imports) {
97
+ const specifiers = [];
98
+ for (const pattern of IMPORT_PATTERNS) {
99
+ for (const match of text.matchAll(pattern)) {
100
+ const specifier = match[1];
101
+ if (!specifier)
102
+ continue;
103
+ pushSpecifier(specifiers, specifier);
104
+ imports.push({
105
+ file,
106
+ line: lineAt(text, match.index ?? 0),
107
+ specifier,
108
+ });
109
+ }
110
+ }
111
+ return specifiers;
112
+ }
113
+ function pushSpecifier(specifiers, specifier) {
114
+ if (!specifiers.includes(specifier))
115
+ specifiers.push(specifier);
116
+ }
117
+ function resolveModule(path) {
118
+ if (existsSync(path) && statSync(path).isFile())
119
+ return path;
120
+ // TypeScript ESM imports name the emitted `.js`, so the source sitting next
121
+ // to it has to be tried before giving up.
122
+ const withoutJs = path.replace(/\.(m?)js$/, '');
123
+ const candidates = withoutJs === path
124
+ ? []
125
+ : [`${withoutJs}.ts`, `${withoutJs}.mts`, `${withoutJs}.tsx`];
126
+ for (const extension of EXTENSIONS) {
127
+ candidates.push(`${path}${extension}`);
128
+ }
129
+ for (const extension of EXTENSIONS) {
130
+ candidates.push(resolve(path, `index${extension}`));
131
+ }
132
+ for (const candidate of candidates) {
133
+ if (existsSync(candidate) && statSync(candidate).isFile())
134
+ return candidate;
135
+ }
136
+ return null;
137
+ }
138
+ function lineAt(text, index) {
139
+ let line = 1;
140
+ for (let position = 0; position < index; position += 1) {
141
+ if (text[position] === '\n')
142
+ line += 1;
143
+ }
144
+ return line;
145
+ }
146
+ /**
147
+ * Removes comments while preserving line breaks, so reported line numbers stay
148
+ * the ones of the original file.
149
+ */
150
+ function stripComments(source) {
151
+ return source
152
+ .replace(/\/\*[\s\S]*?\*\//g, (block) => block.replace(/[^\n]/g, ' '))
153
+ .replace(/(^|[^:'"\\])\/\/[^\n]*/g, (_all, prefix) => prefix);
154
+ }
155
+ //# sourceMappingURL=sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sources.js","sourceRoot":"","sources":["../../../../../libs/deploy/src/lib/sources.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync, statSync } from 'node:fs';\nimport { builtinModules } from 'node:module';\nimport { dirname, resolve } from 'node:path';\n\nexport type CraftModuleImport = Readonly<{\n /** Absolute path of the importing file. */\n file: string;\n line: number;\n specifier: string;\n}>;\n\nexport type CraftModuleGraph = Readonly<{\n /** Entry that could not be read, when the walk found nothing. */\n missingEntry: string | null;\n /** Absolute path of every file reachable from the entry. */\n files: readonly string[];\n /** Concatenated source of those files, comments removed. */\n source: string;\n imports: readonly CraftModuleImport[];\n}>;\n\nconst EXTENSIONS = ['.ts', '.mts', '.tsx', '.js', '.mjs', '.jsx', '.cjs'];\nconst NODE_BUILTINS = new Set(builtinModules);\n\n/**\n * Names every platform and every bundler already provides. Reporting them as\n * undeclared would make the diagnostic noisy enough to be ignored.\n */\nconst AMBIENT_ENVIRONMENT = new Set([\n 'NODE_ENV',\n 'PROD',\n 'DEV',\n 'MODE',\n 'SSR',\n 'BASE_URL',\n]);\n\nconst IMPORT_PATTERNS = [\n /\\bfrom\\s*['\"]([^'\"]+)['\"]/g,\n /\\bimport\\s*['\"]([^'\"]+)['\"]/g,\n /\\bimport\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g,\n /\\brequire\\s*\\(\\s*['\"]([^'\"]+)['\"]\\s*\\)/g,\n];\n\n/**\n * Walks the module graph reachable from an entry through relative imports.\n *\n * Only relative specifiers are followed: a package boundary is where the\n * application stops being responsible for the platform APIs used, and reading\n * `node_modules` would make the check unbounded and slow.\n */\nexport function readCraftModuleGraph(entry: string): CraftModuleGraph {\n const resolvedEntry = resolveModule(entry);\n if (!resolvedEntry) {\n return { missingEntry: entry, files: [], source: '', imports: [] };\n }\n\n const files: string[] = [];\n const imports: CraftModuleImport[] = [];\n const sources: string[] = [];\n const queue = [resolvedEntry];\n const seen = new Set<string>([resolvedEntry]);\n\n while (queue.length > 0) {\n const file = queue.shift() as string;\n let raw: string;\n try {\n raw = readFileSync(file, 'utf8');\n } catch {\n continue;\n }\n const text = stripComments(raw);\n files.push(file);\n sources.push(text);\n\n for (const specifier of collectImports(text, file, imports)) {\n if (!specifier.startsWith('.')) continue;\n const target = resolveModule(resolve(dirname(file), specifier));\n if (target && !seen.has(target)) {\n seen.add(target);\n queue.push(target);\n }\n }\n }\n\n return {\n missingEntry: null,\n files,\n source: sources.join('\\n'),\n imports,\n };\n}\n\nexport function isNodeBuiltin(specifier: string): boolean {\n if (specifier.startsWith('node:')) return true;\n return NODE_BUILTINS.has(specifier);\n}\n\n/**\n * Environment variables the sources read. Both shapes are collected: the Node\n * `process.env.NAME` and the Worker/Lambda `env.NAME` handed to the fetch\n * handler.\n */\nexport function collectEnvironmentReads(source: string): readonly string[] {\n const names = new Set<string>();\n const patterns = [\n /\\bprocess\\.env\\.([A-Z][A-Z0-9_]*)\\b/g,\n /\\bprocess\\.env\\[\\s*['\"]([A-Z][A-Z0-9_]*)['\"]\\s*\\]/g,\n /\\benv\\.([A-Z][A-Z0-9_]*)\\b/g,\n ];\n for (const pattern of patterns) {\n for (const match of source.matchAll(pattern)) {\n const name = match[1];\n if (name && !AMBIENT_ENVIRONMENT.has(name)) names.add(name);\n }\n }\n return [...names].sort();\n}\n\nfunction collectImports(\n text: string,\n file: string,\n imports: CraftModuleImport[],\n): readonly string[] {\n const specifiers: string[] = [];\n for (const pattern of IMPORT_PATTERNS) {\n for (const match of text.matchAll(pattern)) {\n const specifier = match[1];\n if (!specifier) continue;\n pushSpecifier(specifiers, specifier);\n imports.push({\n file,\n line: lineAt(text, match.index ?? 0),\n specifier,\n });\n }\n }\n return specifiers;\n}\n\nfunction pushSpecifier(specifiers: string[], specifier: string): void {\n if (!specifiers.includes(specifier)) specifiers.push(specifier);\n}\n\nfunction resolveModule(path: string): string | null {\n if (existsSync(path) && statSync(path).isFile()) return path;\n\n // TypeScript ESM imports name the emitted `.js`, so the source sitting next\n // to it has to be tried before giving up.\n const withoutJs = path.replace(/\\.(m?)js$/, '');\n const candidates =\n withoutJs === path\n ? []\n : [`${withoutJs}.ts`, `${withoutJs}.mts`, `${withoutJs}.tsx`];\n\n for (const extension of EXTENSIONS) {\n candidates.push(`${path}${extension}`);\n }\n for (const extension of EXTENSIONS) {\n candidates.push(resolve(path, `index${extension}`));\n }\n\n for (const candidate of candidates) {\n if (existsSync(candidate) && statSync(candidate).isFile()) return candidate;\n }\n return null;\n}\n\nfunction lineAt(text: string, index: number): number {\n let line = 1;\n for (let position = 0; position < index; position += 1) {\n if (text[position] === '\\n') line += 1;\n }\n return line;\n}\n\n/**\n * Removes comments while preserving line breaks, so reported line numbers stay\n * the ones of the original file.\n */\nfunction stripComments(source: string): string {\n return source\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, (block) => block.replace(/[^\\n]/g, ' '))\n .replace(/(^|[^:'\"\\\\])\\/\\/[^\\n]*/g, (_all, prefix: string) => prefix);\n}\n"]}
@@ -0,0 +1,16 @@
1
+ import type { CraftDeploymentDiagnostic } from './diagnostics.js';
2
+ import { type CraftDeploymentDefinition } from './manifest.js';
3
+ export type CraftDeploymentValidation = Readonly<{
4
+ /** `null` when a structural error makes the manifest unusable. */
5
+ definition: CraftDeploymentDefinition | null;
6
+ diagnostics: readonly CraftDeploymentDiagnostic[];
7
+ }>;
8
+ /**
9
+ * Validates the structure and the pure semantics of a deployment manifest.
10
+ *
11
+ * Everything checked here is decidable without touching the filesystem, so the
12
+ * same function guards a hand-written `craft.deploy.ts`, a manifest parsed
13
+ * from JSON and a manifest received by a provider.
14
+ */
15
+ export declare function validateCraftDeploymentDefinition(value: unknown): CraftDeploymentValidation;
16
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../../libs/deploy/src/lib/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,yBAAyB,EAE1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAKL,KAAK,yBAAyB,EAG/B,MAAM,eAAe,CAAC;AAGvB,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,kEAAkE;IAClE,UAAU,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC7C,WAAW,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACnD,CAAC,CAAC;AAyBH;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,OAAO,GACb,yBAAyB,CA6X3B"}