@pikku/cli 0.12.20 → 0.12.21
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/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +10 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +8 -2
- package/dist/.pikku/function/pikku-function-types.gen.js +4 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +276 -188
- package/dist/.pikku/function/pikku-functions.gen.js +9 -9
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-meta-service.gen.d.ts +1 -1
- package/dist/.pikku/pikku-meta-service.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +11 -7
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +15 -11
- package/dist/.pikku/schemas/schemas/PikkuCommandHTTPOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuCommandQueueOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/WorkflowRunStatus.schema.json +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/meta/allWorkflow.gen.json +9 -9
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/src/cli.wiring.js +12 -2
- package/dist/src/deploy/analyzer/manifest.d.ts +10 -0
- package/dist/src/deploy/build-pipeline.d.ts +2 -0
- package/dist/src/deploy/build-pipeline.js +44 -6
- package/dist/src/deploy/bundler/bundler.d.ts +2 -1
- package/dist/src/deploy/bundler/bundler.js +28 -5
- package/dist/src/deploy/bundler/dep-extractor.d.ts +5 -2
- package/dist/src/deploy/bundler/dep-extractor.js +103 -23
- package/dist/src/deploy/bundler/types.d.ts +5 -1
- package/dist/src/deploy/codegen/per-unit-codegen.d.ts +3 -1
- package/dist/src/deploy/codegen/per-unit-codegen.js +3 -1
- package/dist/src/deploy/plan/planner.js +25 -3
- package/dist/src/deploy/plan/provider.d.ts +2 -0
- package/dist/src/functions/commands/deploy-apply.js +6 -4
- package/dist/src/functions/commands/deploy-plan.js +7 -1
- package/dist/src/functions/commands/pikku-command-summary.js +4 -1
- package/dist/src/functions/commands/versions-update.js +4 -2
- package/dist/src/functions/wirings/channels/pikku-command-channels.d.ts +1 -1
- package/dist/src/functions/wirings/channels/pikku-command-channels.js +1 -1
- package/dist/src/functions/wirings/console/pikku-command-console-functions.js +5 -1
- package/dist/src/functions/wirings/functions/serialize-function-types.js +31 -1
- package/dist/src/functions/wirings/http/pikku-command-http-routes.d.ts +1 -1
- package/dist/src/functions/wirings/http/pikku-command-http-routes.js +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue-map.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue-map.js +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue.d.ts +1 -1
- package/dist/src/functions/wirings/queue/pikku-command-queue.js +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +5 -1
- package/dist/src/functions/wirings/rpc/pikku-command-remote-rpc.js +5 -1
- package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +16 -2
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +42 -0
- package/dist/src/functions/workflows/all.workflow.js +4 -4
- package/dist/src/scaffold/rpc-remote.gen.js +1 -1
- package/dist/src/scaffold/workflow-routes.gen.js +33 -1
- package/dist/src/services/cli-logger.service.d.ts +22 -2
- package/dist/src/services/cli-logger.service.js +97 -21
- package/dist/src/services.js +8 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +0 -10
- package/dist/.pikku/cli/pikku-cli-client.gen.js +0 -44
- /package/dist/.pikku/schemas/schemas/{PikkuChannelsOutput.schema.json → PikkuCommandChannelsOutput.schema.json} +0 -0
|
@@ -10,6 +10,24 @@ import { mkdir, writeFile, copyFile } from 'node:fs/promises';
|
|
|
10
10
|
import { analyzeDeployment } from './analyzer/index.js';
|
|
11
11
|
import { generatePerUnitCodegen } from './codegen/per-unit-codegen.js';
|
|
12
12
|
import { bundleUnits } from './bundler/index.js';
|
|
13
|
+
const MERGED_SERVER_UNIT_NAME = 'pikku-server-container';
|
|
14
|
+
const UNITS_DIR_NAME = 'units';
|
|
15
|
+
const CONTAINER_DIR_NAME = 'container';
|
|
16
|
+
function attachBundleMetadata(manifest, bundled) {
|
|
17
|
+
if (bundled.length === 0)
|
|
18
|
+
return;
|
|
19
|
+
const byUnitName = new Map(bundled.map((b) => [b.unitName, b]));
|
|
20
|
+
for (const unit of manifest.units) {
|
|
21
|
+
const bundle = byUnitName.get(unit.name);
|
|
22
|
+
if (!bundle)
|
|
23
|
+
continue;
|
|
24
|
+
unit.bundleHash = bundle.bundleHash;
|
|
25
|
+
unit.bundleSizeBytes = bundle.bundleSizeBytes;
|
|
26
|
+
unit.exactDependenciesHash = bundle.exactDependenciesHash;
|
|
27
|
+
unit.exactDependencies = bundle.exactDependencies;
|
|
28
|
+
unit.exactOptionalDependencies = bundle.exactOptionalDependencies;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
13
31
|
function findLockfile(projectDir) {
|
|
14
32
|
for (const name of ['yarn.lock', 'package-lock.json', 'pnpm-lock.yaml']) {
|
|
15
33
|
const p = join(projectDir, name);
|
|
@@ -30,6 +48,10 @@ export async function runBuildPipeline(options) {
|
|
|
30
48
|
let bundled = [];
|
|
31
49
|
let bundleErrors = [];
|
|
32
50
|
let codegenErrors = [];
|
|
51
|
+
let infraPath = null;
|
|
52
|
+
const deploymentManifestPath = join(providerDir, 'deployment-manifest.json');
|
|
53
|
+
const unitsDir = join(providerDir, UNITS_DIR_NAME);
|
|
54
|
+
const containerDir = join(providerDir, CONTAINER_DIR_NAME);
|
|
33
55
|
if (provider.singleUnit) {
|
|
34
56
|
// Single-unit mode: bundle everything into one unit, use project's .pikku/ directly
|
|
35
57
|
logger.info('Building standalone bundle...');
|
|
@@ -63,6 +85,7 @@ export async function runBuildPipeline(options) {
|
|
|
63
85
|
});
|
|
64
86
|
bundled = bundleResult.results;
|
|
65
87
|
bundleErrors = bundleResult.errors;
|
|
88
|
+
attachBundleMetadata(manifest, bundled);
|
|
66
89
|
logger.info(`Bundled standalone${bundleErrors.length > 0 ? ` (${bundleErrors.length} errors)` : ''}`);
|
|
67
90
|
}
|
|
68
91
|
else {
|
|
@@ -74,6 +97,8 @@ export async function runBuildPipeline(options) {
|
|
|
74
97
|
return {
|
|
75
98
|
manifest,
|
|
76
99
|
providerDir,
|
|
100
|
+
deploymentManifestPath,
|
|
101
|
+
infraPath,
|
|
77
102
|
projectId,
|
|
78
103
|
bundled: [],
|
|
79
104
|
bundleErrors: [],
|
|
@@ -88,7 +113,7 @@ export async function runBuildPipeline(options) {
|
|
|
88
113
|
projectDir,
|
|
89
114
|
manifest: serverlessManifest,
|
|
90
115
|
inspectorState,
|
|
91
|
-
deployDir:
|
|
116
|
+
deployDir: unitsDir,
|
|
92
117
|
onProgress: (unitName, status, error) => {
|
|
93
118
|
if (status === 'start') {
|
|
94
119
|
logger.info(` Codegen: ${unitName}...`);
|
|
@@ -104,7 +129,7 @@ export async function runBuildPipeline(options) {
|
|
|
104
129
|
codegenErrors = serverlessCodegenErrors;
|
|
105
130
|
// Step 2b: Server units — single codegen pass with all server function IDs
|
|
106
131
|
if (serverUnits.length > 0) {
|
|
107
|
-
const serverUnitName =
|
|
132
|
+
const serverUnitName = MERGED_SERVER_UNIT_NAME;
|
|
108
133
|
// Create a merged server unit with all server function IDs
|
|
109
134
|
const mergedServerUnit = {
|
|
110
135
|
name: serverUnitName,
|
|
@@ -122,7 +147,8 @@ export async function runBuildPipeline(options) {
|
|
|
122
147
|
projectDir,
|
|
123
148
|
manifest: serverManifest,
|
|
124
149
|
inspectorState,
|
|
125
|
-
deployDir:
|
|
150
|
+
deployDir: containerDir,
|
|
151
|
+
resolveUnitDir: () => containerDir,
|
|
126
152
|
onProgress: (unitName, status, error) => {
|
|
127
153
|
if (status === 'start')
|
|
128
154
|
logger.info(` Codegen: ${unitName}...`);
|
|
@@ -147,7 +173,7 @@ export async function runBuildPipeline(options) {
|
|
|
147
173
|
const pikkuDir = unitPikkuDirs.get(unit.name);
|
|
148
174
|
if (!pikkuDir)
|
|
149
175
|
continue;
|
|
150
|
-
const unitDir = join(
|
|
176
|
+
const unitDir = unit.target === 'server' ? containerDir : join(unitsDir, unit.name);
|
|
151
177
|
const entryPath = join(unitDir, 'entry.ts');
|
|
152
178
|
await mkdir(unitDir, { recursive: true });
|
|
153
179
|
const ctx = getEntryContext(unitDir, pikkuDir, unit, inspectorState);
|
|
@@ -161,9 +187,11 @@ export async function runBuildPipeline(options) {
|
|
|
161
187
|
define: provider.getDefine?.(),
|
|
162
188
|
platform: provider.getPlatform?.(),
|
|
163
189
|
format: provider.getFormat?.(),
|
|
190
|
+
resolveOutputDir: (unit) => unit.target === 'server' ? containerDir : join(unitsDir, unit.name),
|
|
164
191
|
});
|
|
165
192
|
bundled = bundleResult.results;
|
|
166
193
|
bundleErrors = bundleResult.errors;
|
|
194
|
+
attachBundleMetadata(manifest, bundled);
|
|
167
195
|
logger.info(`Bundled ${bundled.length} units${bundleErrors.length > 0 ? ` (${bundleErrors.length} failed)` : ''}`);
|
|
168
196
|
}
|
|
169
197
|
if (bundleErrors.length > 0) {
|
|
@@ -172,9 +200,13 @@ export async function runBuildPipeline(options) {
|
|
|
172
200
|
}
|
|
173
201
|
}
|
|
174
202
|
// Step 4: Generate configs + infra manifest
|
|
203
|
+
await mkdir(providerDir, { recursive: true });
|
|
204
|
+
await writeFile(deploymentManifestPath, JSON.stringify(manifest, null, 2), 'utf-8');
|
|
205
|
+
logger.info('Generated deployment manifest');
|
|
175
206
|
const infraContent = provider.generateInfraManifest(manifest);
|
|
176
207
|
if (infraContent) {
|
|
177
|
-
|
|
208
|
+
infraPath = join(providerDir, 'infra.json');
|
|
209
|
+
await writeFile(infraPath, infraContent, 'utf-8');
|
|
178
210
|
logger.info('Generated infrastructure manifest');
|
|
179
211
|
}
|
|
180
212
|
if (provider.generateProviderConfigs) {
|
|
@@ -187,7 +219,11 @@ export async function runBuildPipeline(options) {
|
|
|
187
219
|
}
|
|
188
220
|
const lockfileSrc = findLockfile(projectDir);
|
|
189
221
|
for (const unit of manifest.units) {
|
|
190
|
-
const unitDir =
|
|
222
|
+
const unitDir = provider.singleUnit
|
|
223
|
+
? join(providerDir, unit.name)
|
|
224
|
+
: unit.target === 'server'
|
|
225
|
+
? containerDir
|
|
226
|
+
: join(unitsDir, unit.name);
|
|
191
227
|
await mkdir(unitDir, { recursive: true });
|
|
192
228
|
const configs = provider.generateUnitConfigs(unit, manifest, projectId);
|
|
193
229
|
for (const [filename, content] of configs) {
|
|
@@ -201,6 +237,8 @@ export async function runBuildPipeline(options) {
|
|
|
201
237
|
return {
|
|
202
238
|
manifest,
|
|
203
239
|
providerDir,
|
|
240
|
+
deploymentManifestPath,
|
|
241
|
+
infraPath,
|
|
204
242
|
projectId,
|
|
205
243
|
bundled,
|
|
206
244
|
bundleErrors,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* 5. Generates a minimal package.json with exact versions
|
|
10
10
|
* 6. Writes all artifacts to `<outputDir>/<unit-name>/`
|
|
11
11
|
*/
|
|
12
|
-
import type { DeploymentManifest, BundleOutput } from './types.js';
|
|
12
|
+
import type { DeploymentManifest, DeploymentUnit, BundleOutput } from './types.js';
|
|
13
13
|
/**
|
|
14
14
|
* Bundles all deployment units defined in a DeploymentManifest.
|
|
15
15
|
*
|
|
@@ -27,4 +27,5 @@ export declare function bundleUnits(projectDir: string, manifest: DeploymentMani
|
|
|
27
27
|
define?: Record<string, string>;
|
|
28
28
|
platform?: 'node' | 'neutral' | 'browser';
|
|
29
29
|
format?: 'esm' | 'cjs';
|
|
30
|
+
resolveOutputDir?: (unit: DeploymentUnit, baseOutputDir: string) => string;
|
|
30
31
|
}): Promise<BundleOutput>;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { build } from 'esbuild';
|
|
13
13
|
import { writeFile, mkdir, stat, readFile } from 'node:fs/promises';
|
|
14
14
|
import { join } from 'node:path';
|
|
15
|
+
import { createHash } from 'node:crypto';
|
|
15
16
|
import { extractDependencies, generateMinimalPackageJson, } from './dep-extractor.js';
|
|
16
17
|
/**
|
|
17
18
|
* Mapping of service name -> gen file pattern that should be stubbed
|
|
@@ -70,6 +71,7 @@ function createDeadModuleStubPlugin(patterns) {
|
|
|
70
71
|
const BUNDLE_FILENAME = 'bundle.js';
|
|
71
72
|
const METAFILE_FILENAME = 'metafile.json';
|
|
72
73
|
const PACKAGE_JSON_FILENAME = 'package.json';
|
|
74
|
+
const EXACT_DEPENDENCIES_FILENAME = 'exact-dependencies.json';
|
|
73
75
|
/**
|
|
74
76
|
* Bundles a single deployment unit using esbuild.
|
|
75
77
|
*
|
|
@@ -84,6 +86,7 @@ async function bundleUnit(options) {
|
|
|
84
86
|
const bundlePath = join(unitOutputDir, BUNDLE_FILENAME);
|
|
85
87
|
const metafilePath = join(unitOutputDir, METAFILE_FILENAME);
|
|
86
88
|
const packageJsonPath = join(unitOutputDir, PACKAGE_JSON_FILENAME);
|
|
89
|
+
const exactDependenciesPath = join(unitOutputDir, EXACT_DEPENDENCIES_FILENAME);
|
|
87
90
|
// Determine which gen files to stub based on per-unit service requirements
|
|
88
91
|
const deadPatterns = await getDeadGenFilePatterns(unitOutputDir);
|
|
89
92
|
// Run esbuild — inline everything into a self-contained bundle.
|
|
@@ -106,7 +109,9 @@ async function bundleUnit(options) {
|
|
|
106
109
|
// The banner shims require via createRequire so CJS builtins resolve.
|
|
107
110
|
const resolvedFormat = format ?? 'esm';
|
|
108
111
|
const banner = resolvedFormat === 'esm' && (platform ?? 'node') === 'node'
|
|
109
|
-
? {
|
|
112
|
+
? {
|
|
113
|
+
js: `import { createRequire } from 'module'; const require = createRequire(import.meta.url);`,
|
|
114
|
+
}
|
|
110
115
|
: undefined;
|
|
111
116
|
const result = await build({
|
|
112
117
|
entryPoints: [entryPath],
|
|
@@ -132,18 +137,34 @@ async function bundleUnit(options) {
|
|
|
132
137
|
const metafileJson = JSON.stringify(result.metafile, null, 2);
|
|
133
138
|
await writeFile(metafilePath, metafileJson, 'utf-8');
|
|
134
139
|
// Extract dependencies and generate minimal package.json
|
|
135
|
-
const
|
|
136
|
-
const packageJson = generateMinimalPackageJson(unit.name,
|
|
140
|
+
const { exactDependencies, exactOptionalDependencies } = await extractDependencies(result.metafile, projectDir);
|
|
141
|
+
const packageJson = generateMinimalPackageJson(unit.name, exactDependencies, exactOptionalDependencies);
|
|
137
142
|
await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2), 'utf-8');
|
|
143
|
+
await writeFile(exactDependenciesPath, JSON.stringify({
|
|
144
|
+
dependencies: Object.fromEntries(Object.entries(exactDependencies).sort(([a], [b]) => a.localeCompare(b))),
|
|
145
|
+
optionalDependencies: Object.fromEntries(Object.entries(exactOptionalDependencies).sort(([a], [b]) => a.localeCompare(b))),
|
|
146
|
+
}, null, 2), 'utf-8');
|
|
138
147
|
// Get bundle size
|
|
139
148
|
const bundleStat = await stat(bundlePath);
|
|
149
|
+
const bundleContents = await readFile(bundlePath);
|
|
150
|
+
const bundleHash = createHash('sha256').update(bundleContents).digest('hex');
|
|
151
|
+
const exactDependenciesHash = createHash('sha256')
|
|
152
|
+
.update(JSON.stringify({
|
|
153
|
+
dependencies: Object.entries(exactDependencies).sort(([a], [b]) => a.localeCompare(b)),
|
|
154
|
+
optionalDependencies: Object.entries(exactOptionalDependencies).sort(([a], [b]) => a.localeCompare(b)),
|
|
155
|
+
}))
|
|
156
|
+
.digest('hex');
|
|
140
157
|
return {
|
|
141
158
|
unitName: unit.name,
|
|
142
159
|
bundlePath,
|
|
143
160
|
packageJsonPath,
|
|
161
|
+
exactDependenciesPath,
|
|
144
162
|
metafilePath,
|
|
145
163
|
bundleSizeBytes: bundleStat.size,
|
|
146
|
-
|
|
164
|
+
bundleHash,
|
|
165
|
+
exactDependenciesHash,
|
|
166
|
+
exactDependencies,
|
|
167
|
+
exactOptionalDependencies,
|
|
147
168
|
};
|
|
148
169
|
}
|
|
149
170
|
/**
|
|
@@ -173,7 +194,9 @@ export async function bundleUnits(projectDir, manifest, entryFiles, outputDir, o
|
|
|
173
194
|
});
|
|
174
195
|
continue;
|
|
175
196
|
}
|
|
176
|
-
const unitOutputDir =
|
|
197
|
+
const unitOutputDir = options?.resolveOutputDir
|
|
198
|
+
? options.resolveOutputDir(unit, buildDir)
|
|
199
|
+
: join(buildDir, unit.name);
|
|
177
200
|
try {
|
|
178
201
|
const result = await bundleUnit({
|
|
179
202
|
unit,
|
|
@@ -28,8 +28,11 @@ export declare function parsePackageName(specifier: string): string | null;
|
|
|
28
28
|
* Returns a record of package name to exact version, suitable for
|
|
29
29
|
* writing into a minimal package.json.
|
|
30
30
|
*/
|
|
31
|
-
export declare function extractDependencies(metafile: Metafile, projectDir: string): Promise<
|
|
31
|
+
export declare function extractDependencies(metafile: Metafile, projectDir: string): Promise<{
|
|
32
|
+
exactDependencies: Record<string, string>;
|
|
33
|
+
exactOptionalDependencies: Record<string, string>;
|
|
34
|
+
}>;
|
|
32
35
|
/**
|
|
33
36
|
* Generates a minimal package.json content object for a unit bundle.
|
|
34
37
|
*/
|
|
35
|
-
export declare function generateMinimalPackageJson(unitName: string, dependencies: Record<string, string>): Record<string, unknown>;
|
|
38
|
+
export declare function generateMinimalPackageJson(unitName: string, dependencies: Record<string, string>, optionalDependencies: Record<string, string>): Record<string, unknown>;
|
|
@@ -43,12 +43,57 @@ export function parsePackageName(specifier) {
|
|
|
43
43
|
return null;
|
|
44
44
|
}
|
|
45
45
|
const builtins = new Set([
|
|
46
|
-
'assert',
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'
|
|
46
|
+
'assert',
|
|
47
|
+
'buffer',
|
|
48
|
+
'child_process',
|
|
49
|
+
'cluster',
|
|
50
|
+
'console',
|
|
51
|
+
'constants',
|
|
52
|
+
'crypto',
|
|
53
|
+
'dgram',
|
|
54
|
+
'dns',
|
|
55
|
+
'domain',
|
|
56
|
+
'events',
|
|
57
|
+
'fs',
|
|
58
|
+
'http',
|
|
59
|
+
'http2',
|
|
60
|
+
'https',
|
|
61
|
+
'inspector',
|
|
62
|
+
'module',
|
|
63
|
+
'net',
|
|
64
|
+
'os',
|
|
65
|
+
'path',
|
|
66
|
+
'perf_hooks',
|
|
67
|
+
'process',
|
|
68
|
+
'punycode',
|
|
69
|
+
'querystring',
|
|
70
|
+
'readline',
|
|
71
|
+
'repl',
|
|
72
|
+
'stream',
|
|
73
|
+
'string_decoder',
|
|
74
|
+
'sys',
|
|
75
|
+
'timers',
|
|
76
|
+
'tls',
|
|
77
|
+
'tty',
|
|
78
|
+
'url',
|
|
79
|
+
'util',
|
|
80
|
+
'v8',
|
|
81
|
+
'vm',
|
|
82
|
+
'wasi',
|
|
83
|
+
'worker_threads',
|
|
84
|
+
'zlib',
|
|
85
|
+
'async_hooks',
|
|
86
|
+
'child_process',
|
|
87
|
+
'cluster',
|
|
88
|
+
'diagnostics_channel',
|
|
89
|
+
'dns/promises',
|
|
90
|
+
'fs/promises',
|
|
91
|
+
'readline/promises',
|
|
92
|
+
'stream/consumers',
|
|
93
|
+
'stream/promises',
|
|
94
|
+
'stream/web',
|
|
95
|
+
'timers/promises',
|
|
96
|
+
'util/types',
|
|
52
97
|
]);
|
|
53
98
|
if (builtins.has(specifier.split('/')[0])) {
|
|
54
99
|
return null;
|
|
@@ -68,7 +113,7 @@ export function parsePackageName(specifier) {
|
|
|
68
113
|
*/
|
|
69
114
|
async function readProjectDependencies(projectDir) {
|
|
70
115
|
const dependencies = {};
|
|
71
|
-
const
|
|
116
|
+
const optionalDependencies = {};
|
|
72
117
|
// Walk up the directory tree to find all package.json files
|
|
73
118
|
// (handles monorepo setups where deps are in the root package.json)
|
|
74
119
|
let dir = projectDir;
|
|
@@ -82,9 +127,9 @@ async function readProjectDependencies(projectDir) {
|
|
|
82
127
|
if (!(k in dependencies))
|
|
83
128
|
dependencies[k] = v;
|
|
84
129
|
}
|
|
85
|
-
for (const [k, v] of Object.entries(pkg.
|
|
86
|
-
if (!(k in
|
|
87
|
-
|
|
130
|
+
for (const [k, v] of Object.entries(pkg.optionalDependencies ?? {})) {
|
|
131
|
+
if (!(k in optionalDependencies))
|
|
132
|
+
optionalDependencies[k] = v;
|
|
88
133
|
}
|
|
89
134
|
}
|
|
90
135
|
catch {
|
|
@@ -95,7 +140,7 @@ async function readProjectDependencies(projectDir) {
|
|
|
95
140
|
break;
|
|
96
141
|
dir = parent;
|
|
97
142
|
}
|
|
98
|
-
return { dependencies,
|
|
143
|
+
return { dependencies, optionalDependencies };
|
|
99
144
|
}
|
|
100
145
|
/**
|
|
101
146
|
* Attempts to read a version from yarn.lock for a given package.
|
|
@@ -161,7 +206,27 @@ function parseYarnLockKeyLine(line) {
|
|
|
161
206
|
* Resolves the exact version for a package, trying yarn.lock first,
|
|
162
207
|
* then falling back to the version range from package.json.
|
|
163
208
|
*/
|
|
164
|
-
function
|
|
209
|
+
async function resolveInstalledPackageVersion(packageName, projectDir) {
|
|
210
|
+
let dir = projectDir;
|
|
211
|
+
while (true) {
|
|
212
|
+
const pkgPath = join(dir, 'node_modules', packageName, 'package.json');
|
|
213
|
+
try {
|
|
214
|
+
const content = await readFile(pkgPath, 'utf-8');
|
|
215
|
+
const pkg = JSON.parse(content);
|
|
216
|
+
if (pkg.version)
|
|
217
|
+
return pkg.version;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// Keep walking up to workspace root
|
|
221
|
+
}
|
|
222
|
+
const parent = join(dir, '..');
|
|
223
|
+
if (parent === dir)
|
|
224
|
+
break;
|
|
225
|
+
dir = parent;
|
|
226
|
+
}
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
async function resolveVersion(packageName, projectDeps, yarnLockVersions, projectDir) {
|
|
165
230
|
// Prefer exact version from yarn.lock
|
|
166
231
|
const locked = yarnLockVersions.get(packageName);
|
|
167
232
|
if (locked)
|
|
@@ -170,12 +235,13 @@ function resolveVersion(packageName, projectDeps, yarnLockVersions) {
|
|
|
170
235
|
const fromDeps = projectDeps.dependencies[packageName];
|
|
171
236
|
if (fromDeps)
|
|
172
237
|
return fromDeps;
|
|
173
|
-
const
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
238
|
+
const fromOptionalDeps = projectDeps.optionalDependencies[packageName];
|
|
239
|
+
if (fromOptionalDeps)
|
|
240
|
+
return fromOptionalDeps;
|
|
241
|
+
const installedVersion = await resolveInstalledPackageVersion(packageName, projectDir);
|
|
242
|
+
if (installedVersion)
|
|
243
|
+
return installedVersion;
|
|
244
|
+
return null;
|
|
179
245
|
}
|
|
180
246
|
/**
|
|
181
247
|
* Given an esbuild metafile, extracts all external packages and resolves
|
|
@@ -187,27 +253,41 @@ function resolveVersion(packageName, projectDeps, yarnLockVersions) {
|
|
|
187
253
|
export async function extractDependencies(metafile, projectDir) {
|
|
188
254
|
const externalPackages = extractExternalPackages(metafile);
|
|
189
255
|
if (externalPackages.size === 0) {
|
|
190
|
-
return {};
|
|
256
|
+
return { exactDependencies: {}, exactOptionalDependencies: {} };
|
|
191
257
|
}
|
|
192
258
|
const [projectDeps, yarnLockVersions] = await Promise.all([
|
|
193
259
|
readProjectDependencies(projectDir),
|
|
194
260
|
readYarnLockVersions(projectDir),
|
|
195
261
|
]);
|
|
196
|
-
const
|
|
262
|
+
const exactDependencies = {};
|
|
263
|
+
const exactOptionalDependencies = {};
|
|
197
264
|
for (const pkg of [...externalPackages].sort()) {
|
|
198
|
-
|
|
265
|
+
const version = await resolveVersion(pkg, projectDeps, yarnLockVersions, projectDir);
|
|
266
|
+
if (!version) {
|
|
267
|
+
// Some packages are optional-at-runtime (e.g. ws acceleration addons).
|
|
268
|
+
// Keep deploy planning deterministic without forcing a hard failure.
|
|
269
|
+
exactOptionalDependencies[pkg] = '*';
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
if (pkg in projectDeps.optionalDependencies) {
|
|
273
|
+
exactOptionalDependencies[pkg] = version;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
exactDependencies[pkg] = version;
|
|
277
|
+
}
|
|
199
278
|
}
|
|
200
|
-
return
|
|
279
|
+
return { exactDependencies, exactOptionalDependencies };
|
|
201
280
|
}
|
|
202
281
|
/**
|
|
203
282
|
* Generates a minimal package.json content object for a unit bundle.
|
|
204
283
|
*/
|
|
205
|
-
export function generateMinimalPackageJson(unitName, dependencies) {
|
|
284
|
+
export function generateMinimalPackageJson(unitName, dependencies, optionalDependencies) {
|
|
206
285
|
return {
|
|
207
286
|
name: unitName,
|
|
208
287
|
private: true,
|
|
209
288
|
type: 'module',
|
|
210
289
|
main: 'bundle.js',
|
|
211
290
|
dependencies,
|
|
291
|
+
optionalDependencies,
|
|
212
292
|
};
|
|
213
293
|
}
|
|
@@ -7,9 +7,13 @@ export interface BundleResult {
|
|
|
7
7
|
unitName: string;
|
|
8
8
|
bundlePath: string;
|
|
9
9
|
packageJsonPath: string;
|
|
10
|
+
exactDependenciesPath: string;
|
|
10
11
|
metafilePath: string;
|
|
11
12
|
bundleSizeBytes: number;
|
|
12
|
-
|
|
13
|
+
bundleHash: string;
|
|
14
|
+
exactDependenciesHash: string;
|
|
15
|
+
exactDependencies: Record<string, string>;
|
|
16
|
+
exactOptionalDependencies: Record<string, string>;
|
|
13
17
|
}
|
|
14
18
|
export interface BundleError {
|
|
15
19
|
unitName: string;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Uses --stateInput to avoid re-inspecting the codebase for each unit.
|
|
9
9
|
*/
|
|
10
10
|
import type { InspectorState } from '@pikku/inspector';
|
|
11
|
-
import type { DeploymentManifest } from '../analyzer/manifest.js';
|
|
11
|
+
import type { DeploymentManifest, DeploymentUnit } from '../analyzer/manifest.js';
|
|
12
12
|
export interface PerUnitCodegenOptions {
|
|
13
13
|
/** Root directory of the project (where pikku.config.json lives) */
|
|
14
14
|
projectDir: string;
|
|
@@ -22,6 +22,8 @@ export interface PerUnitCodegenOptions {
|
|
|
22
22
|
pikkuBin?: string;
|
|
23
23
|
/** Called for each unit as it starts/completes */
|
|
24
24
|
onProgress?: (unitName: string, status: 'start' | 'done' | 'error', error?: string) => void;
|
|
25
|
+
/** Resolve unit output directory (defaults to <deployDir>/<unit-name>) */
|
|
26
|
+
resolveUnitDir?: (unit: DeploymentUnit, baseDeployDir: string) => string;
|
|
25
27
|
}
|
|
26
28
|
export interface PerUnitCodegenResult {
|
|
27
29
|
/** Map of unit name -> path to the unit's .pikku directory */
|
|
@@ -171,7 +171,9 @@ export async function generatePerUnitCodegen(options) {
|
|
|
171
171
|
continue;
|
|
172
172
|
}
|
|
173
173
|
onProgress?.(unit.name, 'start');
|
|
174
|
-
const unitDir =
|
|
174
|
+
const unitDir = options.resolveUnitDir
|
|
175
|
+
? options.resolveUnitDir(unit, baseDir)
|
|
176
|
+
: join(baseDir, unit.name);
|
|
175
177
|
const unitPikkuDir = join(unitDir, '.pikku');
|
|
176
178
|
await mkdir(unitDir, { recursive: true });
|
|
177
179
|
const namesArg = filterNames.join(',');
|
|
@@ -23,14 +23,36 @@ function diffUnits(manifest, current, drainInfo) {
|
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
else if (!arraysEqual(existing.functionIds, desired.functionIds) ||
|
|
26
|
-
existing.role !== desired.role
|
|
26
|
+
existing.role !== desired.role ||
|
|
27
|
+
(typeof desired.bundleHash === 'string' &&
|
|
28
|
+
typeof existing.bundleHash === 'string' &&
|
|
29
|
+
desired.bundleHash !== existing.bundleHash) ||
|
|
30
|
+
(typeof desired.exactDependenciesHash === 'string' &&
|
|
31
|
+
typeof existing.exactDependenciesHash === 'string' &&
|
|
32
|
+
desired.exactDependenciesHash !== existing.exactDependenciesHash)) {
|
|
33
|
+
const bundleChanged = typeof desired.bundleHash === 'string' &&
|
|
34
|
+
typeof existing.bundleHash === 'string' &&
|
|
35
|
+
desired.bundleHash !== existing.bundleHash;
|
|
36
|
+
const depsChanged = typeof desired.exactDependenciesHash === 'string' &&
|
|
37
|
+
typeof existing.exactDependenciesHash === 'string' &&
|
|
38
|
+
desired.exactDependenciesHash !== existing.exactDependenciesHash;
|
|
27
39
|
changes.push({
|
|
28
40
|
action: 'update',
|
|
29
41
|
resourceType: 'unit',
|
|
30
42
|
name: desired.name,
|
|
31
43
|
role: desired.role,
|
|
32
|
-
reason:
|
|
33
|
-
|
|
44
|
+
reason: bundleChanged
|
|
45
|
+
? depsChanged
|
|
46
|
+
? 'bundle + dependencies changed'
|
|
47
|
+
: 'bundle changed'
|
|
48
|
+
: depsChanged
|
|
49
|
+
? 'dependencies changed'
|
|
50
|
+
: 'code changed',
|
|
51
|
+
details: {
|
|
52
|
+
functionIds: desired.functionIds,
|
|
53
|
+
bundleHash: desired.bundleHash,
|
|
54
|
+
exactDependenciesHash: desired.exactDependenciesHash,
|
|
55
|
+
},
|
|
34
56
|
});
|
|
35
57
|
}
|
|
36
58
|
}
|
|
@@ -94,7 +94,6 @@ export async function resolveProvider(config, providerName) {
|
|
|
94
94
|
}
|
|
95
95
|
const ANSI = {
|
|
96
96
|
green: '\x1b[32m',
|
|
97
|
-
red: '\x1b[31m',
|
|
98
97
|
bold: '\x1b[1m',
|
|
99
98
|
reset: '\x1b[0m',
|
|
100
99
|
};
|
|
@@ -119,8 +118,12 @@ async function runDeploy(provider, providerDir, logger, resultFile) {
|
|
|
119
118
|
deployResult = await provider.deploy({
|
|
120
119
|
buildDir: providerDir,
|
|
121
120
|
logger,
|
|
122
|
-
onProgress: (
|
|
123
|
-
|
|
121
|
+
onProgress: (step, detail) => {
|
|
122
|
+
logger.info({
|
|
123
|
+
message: `[${step}] ${detail}`,
|
|
124
|
+
type: 'progress',
|
|
125
|
+
data: { progress: { step, detail } },
|
|
126
|
+
});
|
|
124
127
|
},
|
|
125
128
|
});
|
|
126
129
|
}
|
|
@@ -132,7 +135,6 @@ async function runDeploy(provider, providerDir, logger, resultFile) {
|
|
|
132
135
|
};
|
|
133
136
|
}
|
|
134
137
|
await writeResultFile(resultFile, deployResult);
|
|
135
|
-
console.log('');
|
|
136
138
|
if (deployResult.success) {
|
|
137
139
|
logger.info(`${ANSI.green}${ANSI.bold}Deployment complete.${ANSI.reset}`);
|
|
138
140
|
logger.info(` ${deployResult.workersDeployed?.length ?? 0} units deployed, ${deployResult.resourcesCreated?.length ?? 0} resources created`);
|
|
@@ -89,7 +89,13 @@ export const deployPlan = pikkuSessionlessFunc({
|
|
|
89
89
|
unitCount: result.bundled.length,
|
|
90
90
|
totalSizeBytes: totalSize,
|
|
91
91
|
errors: result.bundleErrors,
|
|
92
|
-
|
|
92
|
+
codegenErrors: result.codegenErrors,
|
|
93
|
+
summary: plan.summary,
|
|
94
|
+
changeCount: plan.changes.length,
|
|
95
|
+
artifacts: {
|
|
96
|
+
deploymentManifestPath: result.deploymentManifestPath,
|
|
97
|
+
infraPath: result.infraPath,
|
|
98
|
+
},
|
|
93
99
|
}, null, 2), 'utf-8');
|
|
94
100
|
}
|
|
95
101
|
},
|
|
@@ -49,7 +49,10 @@ export const pikkuSummary = pikkuSessionlessFunc({
|
|
|
49
49
|
summary.set('workflowGraphs', workflowGraphsCount);
|
|
50
50
|
}
|
|
51
51
|
if (!logger.isSilent()) {
|
|
52
|
-
|
|
52
|
+
// Route through the logger so JSON mode emits a single NDJSON
|
|
53
|
+
// record instead of raw chalk-coloured text written directly to
|
|
54
|
+
// stdout (which would break NDJSON consumers).
|
|
55
|
+
logger.info({ message: summary.format(), type: 'summary' });
|
|
53
56
|
}
|
|
54
57
|
},
|
|
55
58
|
});
|
|
@@ -13,9 +13,11 @@ export const pikkuVersionsUpdate = pikkuSessionlessFunc({
|
|
|
13
13
|
const immutabilityErrors = visitState.manifest.errors.filter((e) => e.code === ErrorCode.FUNCTION_VERSION_MODIFIED);
|
|
14
14
|
if (immutabilityErrors.length > 0) {
|
|
15
15
|
for (const e of immutabilityErrors) {
|
|
16
|
-
logger.
|
|
16
|
+
logger.critical(ErrorCode.FUNCTION_VERSION_MODIFIED, e.message);
|
|
17
|
+
}
|
|
18
|
+
if (logger.hasCriticalErrors()) {
|
|
19
|
+
process.exit(1);
|
|
17
20
|
}
|
|
18
|
-
logger.warn(`Contract drift detected — version manifest not updated. Run 'pikku versions check' to inspect, or bump versions via code and re-run.`);
|
|
19
21
|
return;
|
|
20
22
|
}
|
|
21
23
|
await saveManifest(manifestPath, visitState.manifest.current);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const
|
|
1
|
+
export declare const pikkuCommandChannels: import("#pikku").PikkuFunctionConfig<void, boolean | undefined, "session" | "rpc", import("#pikku").PikkuFunctionSessionless<void, boolean | undefined, "session" | "rpc", import("#pikku").Services> | import("#pikku").PikkuFunction<void, boolean | undefined, "session" | "rpc", import("#pikku").Services>, undefined, undefined>;
|
|
@@ -4,7 +4,7 @@ import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { getFileImportRelativePath } from '../../../utils/file-import-path.js';
|
|
6
6
|
import { stripVerboseFields, hasVerboseFields, } from '../../../utils/strip-verbose-meta.js';
|
|
7
|
-
export const
|
|
7
|
+
export const pikkuCommandChannels = pikkuSessionlessFunc({
|
|
8
8
|
func: async ({ logger, config, getInspectorState }) => {
|
|
9
9
|
const visitState = await getInspectorState();
|
|
10
10
|
const { channelsWiringFile, channelsWiringMetaFile, channelsWiringMetaJsonFile, packageMappings, schema, } = config;
|
|
@@ -4,7 +4,11 @@ import { writeFileInDir } from '../../../utils/file-writer.js';
|
|
|
4
4
|
import { logCommandInfoAndTime } from '../../../middleware/log-command-info-and-time.js';
|
|
5
5
|
import { serializeConsoleFunctions } from './serialize-console-functions.js';
|
|
6
6
|
export const pikkuConsoleFunctions = pikkuSessionlessFunc({
|
|
7
|
-
func: async ({ logger, config }) => {
|
|
7
|
+
func: async ({ logger, config, variables }) => {
|
|
8
|
+
const deployCodegenFlag = await variables.get('PIKKU_DEPLOY_CODEGEN');
|
|
9
|
+
if (deployCodegenFlag === '1') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
8
12
|
if (config.scaffold?.console) {
|
|
9
13
|
const pathToPikkuTypes = getFileImportRelativePath(config.consoleFunctionsFile, config.typesDeclarationFile, config.packageMappings);
|
|
10
14
|
const pathToAgentTypes = getFileImportRelativePath(config.consoleFunctionsFile, config.agentTypesFile, config.packageMappings);
|