@pikku/cli 0.12.14 → 0.12.16
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/cli.schema.json +1 -1
- package/console-app/android-chrome-192x192.png +0 -0
- package/console-app/android-chrome-512x512.png +0 -0
- package/console-app/apple-touch-icon.png +0 -0
- package/console-app/assets/index-CzMWJFqj.js +700 -0
- package/console-app/assets/index-DvrDbftC.css +1 -0
- package/console-app/favicon-16x16.png +0 -0
- package/console-app/favicon-32x32.png +0 -0
- package/console-app/favicon.ico +0 -0
- package/console-app/index.html +33 -0
- package/console-app/pikku-console-logo.png +0 -0
- package/console-app/site.webmanifest +19 -0
- package/console-app/wiring-icons/cron.svg +6 -0
- package/console-app/wiring-icons/function.png +0 -0
- package/console-app/wiring-icons/http.svg +2 -0
- package/console-app/wiring-icons/mcp.svg +1 -0
- package/console-app/wiring-icons/queue.svg +18 -0
- package/console-app/wiring-icons/sse.svg +5 -0
- package/console-app/wiring-icons/websocket.svg +8 -0
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-agent-wirings-meta.gen.js +1 -1
- package/dist/.pikku/agent/pikku-agent-wirings.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-agent-wirings.gen.js +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/channel/pikku-channels-meta.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +16 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.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 +69 -6
- 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 +16 -19
- package/dist/.pikku/function/pikku-function-types.gen.js +15 -19
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +277 -194
- package/dist/.pikku/function/pikku-functions.gen.js +6 -2
- 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-meta.gen.json +18 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +2 -2
- package/dist/.pikku/http/pikku-http-wirings.gen.js +3 -3
- 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/mcp/pikku-mcp-wirings-meta.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.d.ts +3 -10
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -18
- package/dist/.pikku/pikku-meta-service.gen.d.ts +7 -0
- package/dist/.pikku/pikku-meta-service.gen.js +9 -0
- package/dist/.pikku/pikku-services.gen.d.ts +2 -1
- package/dist/.pikku/pikku-services.gen.js +1 -0
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/pikku-websocket.gen.d.ts +1 -1
- package/dist/.pikku/pikku-websocket.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/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-remote-rpc-workers.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 +19 -15
- 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/scheduler/pikku-schedulers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +15 -7
- package/dist/.pikku/schemas/schemas/DeployApplyInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/DeployPlanInput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuNewAddonInput.schema.json +1 -1
- package/dist/.pikku/schemas/schemas/PikkuWorkflowRoutesOutput.schema.json +1 -0
- package/dist/.pikku/schemas/schemas/RemoteRPCHandlerInput.schema.json +1 -0
- 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/pikku-workflow-types.gen.d.ts +3 -24
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +1 -14
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/src/cli.wiring.js +63 -4
- package/dist/src/deploy/analyzer/analyzer.d.ts +16 -0
- package/dist/src/deploy/analyzer/analyzer.js +557 -0
- package/dist/src/deploy/analyzer/index.d.ts +3 -0
- package/dist/src/deploy/analyzer/index.js +1 -0
- package/dist/src/deploy/analyzer/manifest.d.ts +112 -0
- package/dist/src/deploy/analyzer/manifest.js +8 -0
- package/dist/src/deploy/build-pipeline.d.ts +39 -0
- package/dist/src/deploy/build-pipeline.js +209 -0
- package/dist/src/deploy/bundler/bundler.d.ts +30 -0
- package/dist/src/deploy/bundler/bundler.js +196 -0
- package/dist/src/deploy/bundler/dep-extractor.d.ts +35 -0
- package/dist/src/deploy/bundler/dep-extractor.js +213 -0
- package/dist/src/deploy/bundler/index.d.ts +3 -0
- package/dist/src/deploy/bundler/index.js +2 -0
- package/dist/src/deploy/bundler/types.d.ts +21 -0
- package/dist/src/deploy/bundler/types.js +5 -0
- package/dist/src/deploy/codegen/index.d.ts +2 -0
- package/dist/src/deploy/codegen/index.js +1 -0
- package/dist/src/deploy/codegen/per-unit-codegen.d.ts +44 -0
- package/dist/src/deploy/codegen/per-unit-codegen.js +216 -0
- package/dist/src/deploy/plan/executor.d.ts +9 -0
- package/dist/src/deploy/plan/executor.js +49 -0
- package/dist/src/deploy/plan/formatter.d.ts +4 -0
- package/dist/src/deploy/plan/formatter.js +114 -0
- package/dist/src/deploy/plan/index.d.ts +5 -0
- package/dist/src/deploy/plan/index.js +3 -0
- package/dist/src/deploy/plan/planner.d.ts +4 -0
- package/dist/src/deploy/plan/planner.js +220 -0
- package/dist/src/deploy/plan/provider.d.ts +30 -0
- package/dist/src/deploy/plan/provider.js +1 -0
- package/dist/src/deploy/plan/types.d.ts +29 -0
- package/dist/src/deploy/plan/types.js +1 -0
- package/dist/src/deploy/provider-adapter.d.ts +111 -0
- package/dist/src/deploy/provider-adapter.js +10 -0
- package/dist/src/functions/commands/all.js +6 -2
- package/dist/src/functions/commands/deploy-apply.d.ts +22 -0
- package/dist/src/functions/commands/deploy-apply.js +206 -0
- package/dist/src/functions/commands/deploy-info.d.ts +1 -0
- package/dist/src/functions/commands/deploy-info.js +122 -0
- package/dist/src/functions/commands/deploy-plan.d.ts +10 -0
- package/dist/src/functions/commands/deploy-plan.js +96 -0
- package/dist/src/functions/commands/enable.js +1 -1
- package/dist/src/functions/commands/new-addon.d.ts +3 -0
- package/dist/src/functions/commands/new-addon.js +70 -5
- package/dist/src/functions/commands/pikku-command-bootstrap.js +30 -20
- package/dist/src/functions/runtimes/nextjs/pikku-command-nextjs.js +7 -3
- package/dist/src/functions/wirings/ai-agent/pikku-command-ai-agent.js +7 -5
- package/dist/src/functions/wirings/channels/pikku-channels.js +3 -0
- package/dist/src/functions/wirings/channels/pikku-command-channels.js +3 -0
- package/dist/src/functions/wirings/cli/pikku-command-cli.js +3 -0
- package/dist/src/functions/wirings/cli/serialize-channel-cli.js +2 -2
- package/dist/src/functions/wirings/console/serialize-console-functions.js +2 -2
- package/dist/src/functions/wirings/functions/pikku-command-services.d.ts +1 -1
- package/dist/src/functions/wirings/functions/pikku-command-services.js +9 -2
- package/dist/src/functions/wirings/functions/serialize-addon-types.js +3 -3
- package/dist/src/functions/wirings/functions/serialize-function-imports.js +5 -3
- package/dist/src/functions/wirings/functions/serialize-function-types.js +17 -19
- package/dist/src/functions/wirings/http/pikku-command-http-routes.js +3 -0
- package/dist/src/functions/wirings/http/pikku-http-routes.js +3 -0
- package/dist/src/functions/wirings/mcp/pikku-command-mcp.js +6 -0
- package/dist/src/functions/wirings/package/pikku-command-package.js +13 -1
- package/dist/src/functions/wirings/package/serialize-package.d.ts +7 -1
- package/dist/src/functions/wirings/package/serialize-package.js +8 -2
- package/dist/src/functions/wirings/queue/pikku-command-queue.js +4 -0
- package/dist/src/functions/wirings/queue/pikku-queue.js +4 -0
- package/dist/src/functions/wirings/queue/serialize-queue-map.js +4 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc.js +10 -3
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +5 -27
- package/dist/src/functions/wirings/rpc/serialize-remote-rpc.js +11 -14
- package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.js +28 -3
- package/dist/src/functions/wirings/rpc/serialize-typed-rpc-map.js +15 -5
- package/dist/src/functions/wirings/scheduler/pikku-command-scheduler.js +4 -0
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.d.ts +1 -0
- package/dist/src/functions/wirings/workflow/pikku-command-workflow-routes.js +21 -0
- package/dist/src/functions/wirings/workflow/pikku-command-workflow.js +10 -9
- package/dist/src/functions/wirings/workflow/serialize-workflow-map.d.ts +6 -1
- package/dist/src/functions/wirings/workflow/serialize-workflow-map.js +42 -5
- package/dist/src/functions/wirings/workflow/serialize-workflow-registration.d.ts +1 -1
- package/dist/src/functions/wirings/workflow/serialize-workflow-registration.js +3 -2
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.d.ts +4 -0
- package/dist/src/functions/wirings/workflow/serialize-workflow-routes.js +139 -0
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +4 -51
- package/dist/src/scaffold/rpc-remote.gen.d.ts +10 -0
- package/dist/src/scaffold/rpc-remote.gen.js +22 -0
- package/dist/src/services.js +12 -7
- package/dist/src/utils/pikku-cli-config.d.ts +1 -1
- package/dist/src/utils/pikku-cli-config.js +30 -28
- package/dist/src/utils/strip-verbose-meta.js +2 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -4
- package/dist/src/functions/wirings/workflow/serialize-workflow-workers.d.ts +0 -4
- package/dist/src/functions/wirings/workflow/serialize-workflow-workers.js +0 -47
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { basename, join, relative } from 'node:path';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
4
|
+
import { runBuildPipeline } from '../../deploy/build-pipeline.js';
|
|
5
|
+
function toRelativeImport(fromDir, toFile) {
|
|
6
|
+
let rel = relative(fromDir, toFile).replace(/\\/g, '/');
|
|
7
|
+
if (!rel.startsWith('.'))
|
|
8
|
+
rel = `./${rel}`;
|
|
9
|
+
return rel.replace(/\.ts$/, '.js');
|
|
10
|
+
}
|
|
11
|
+
export function getEntryContext(unitDir, pikkuDir, unit, inspectorState) {
|
|
12
|
+
const bootstrapRelative = relative(unitDir, join(pikkuDir, 'pikku-bootstrap.gen.js'));
|
|
13
|
+
const bootstrapPath = bootstrapRelative.startsWith('./') || bootstrapRelative.startsWith('../')
|
|
14
|
+
? bootstrapRelative
|
|
15
|
+
: `./${bootstrapRelative}`;
|
|
16
|
+
const { pikkuConfigFactory, singletonServicesFactory, singletonServicesType, } = inspectorState.filesAndMethods;
|
|
17
|
+
if (!pikkuConfigFactory || !singletonServicesFactory) {
|
|
18
|
+
throw new Error('Cannot generate deploy entries: createConfig and createSingletonServices must be defined in your project');
|
|
19
|
+
}
|
|
20
|
+
const configRelative = toRelativeImport(unitDir, pikkuConfigFactory.file);
|
|
21
|
+
const servicesRelative = toRelativeImport(unitDir, singletonServicesFactory.file);
|
|
22
|
+
const singletonServicesImport = singletonServicesType
|
|
23
|
+
? `import type { ${singletonServicesType.type} } from '${toRelativeImport(unitDir, singletonServicesType.typePath)}'`
|
|
24
|
+
: '';
|
|
25
|
+
const servicesType = singletonServicesType
|
|
26
|
+
? `Partial<${singletonServicesType.type}>`
|
|
27
|
+
: 'Record<string, unknown>';
|
|
28
|
+
return {
|
|
29
|
+
unit,
|
|
30
|
+
unitDir,
|
|
31
|
+
bootstrapPath,
|
|
32
|
+
configImport: `import { ${pikkuConfigFactory.variable} } from '${configRelative}'`,
|
|
33
|
+
configVar: pikkuConfigFactory.variable,
|
|
34
|
+
servicesImport: `import { ${singletonServicesFactory.variable} } from '${servicesRelative}'`,
|
|
35
|
+
servicesVar: singletonServicesFactory.variable,
|
|
36
|
+
singletonServicesImport,
|
|
37
|
+
servicesType,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function sanitizeProjectId(raw) {
|
|
41
|
+
return (raw
|
|
42
|
+
.toLowerCase()
|
|
43
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
44
|
+
.replace(/-+/g, '-')
|
|
45
|
+
.replace(/^-|-$/g, '') || 'pikku-project');
|
|
46
|
+
}
|
|
47
|
+
async function resolveProjectId(projectDir) {
|
|
48
|
+
try {
|
|
49
|
+
const pkg = JSON.parse(await readFile(join(projectDir, 'package.json'), 'utf-8'));
|
|
50
|
+
if (pkg.name) {
|
|
51
|
+
const name = pkg.name.replace(/^@[^/]+\//, '');
|
|
52
|
+
return sanitizeProjectId(name);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
const err = e;
|
|
57
|
+
if (err?.code !== 'ENOENT') {
|
|
58
|
+
console.warn(`Warning: failed to read package.json: ${err?.message ?? e}`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return sanitizeProjectId(basename(projectDir));
|
|
62
|
+
}
|
|
63
|
+
export async function resolveProvider(config, providerName) {
|
|
64
|
+
const name = providerName ?? config?.deploy?.defaultProvider ?? 'cloudflare';
|
|
65
|
+
const providers = config?.deploy?.providers ?? {
|
|
66
|
+
cloudflare: '@pikku/deploy-cloudflare',
|
|
67
|
+
serverless: '@pikku/deploy-serverless',
|
|
68
|
+
azure: '@pikku/deploy-azure',
|
|
69
|
+
standalone: '@pikku/deploy-standalone',
|
|
70
|
+
};
|
|
71
|
+
const packageName = providers[name];
|
|
72
|
+
if (!packageName) {
|
|
73
|
+
throw new Error(`Unknown deploy provider: '${name}'. Available: ${Object.keys(providers).join(', ')}`);
|
|
74
|
+
}
|
|
75
|
+
const adapterExportName = name.charAt(0).toUpperCase() + name.slice(1) + 'ProviderAdapter';
|
|
76
|
+
try {
|
|
77
|
+
const mod = await import(packageName);
|
|
78
|
+
if (typeof mod.createAdapter === 'function') {
|
|
79
|
+
return mod.createAdapter();
|
|
80
|
+
}
|
|
81
|
+
if (typeof mod[adapterExportName] === 'function') {
|
|
82
|
+
return new mod[adapterExportName]();
|
|
83
|
+
}
|
|
84
|
+
throw new Error(`Deploy provider '${packageName}' does not export createAdapter() or ${adapterExportName}`);
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
const err = e;
|
|
88
|
+
if (err?.code === 'ERR_MODULE_NOT_FOUND' ||
|
|
89
|
+
err?.code === 'MODULE_NOT_FOUND') {
|
|
90
|
+
throw new Error(`Deploy provider '${packageName}' is not installed. Run: yarn add ${packageName}`);
|
|
91
|
+
}
|
|
92
|
+
throw e;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const ANSI = {
|
|
96
|
+
green: '\x1b[32m',
|
|
97
|
+
red: '\x1b[31m',
|
|
98
|
+
bold: '\x1b[1m',
|
|
99
|
+
reset: '\x1b[0m',
|
|
100
|
+
};
|
|
101
|
+
async function writeResultFile(resultFile, result) {
|
|
102
|
+
if (resultFile) {
|
|
103
|
+
const { writeFile } = await import('node:fs/promises');
|
|
104
|
+
await writeFile(resultFile, JSON.stringify(result, null, 2), 'utf-8');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function runDeploy(provider, providerDir, logger, resultFile) {
|
|
108
|
+
if (typeof provider.deploy !== 'function') {
|
|
109
|
+
logger.error(`Provider '${provider.name}' does not support deploy.`);
|
|
110
|
+
await writeResultFile(resultFile, {
|
|
111
|
+
success: false,
|
|
112
|
+
errors: [{ step: 'provider', error: 'No deploy support' }],
|
|
113
|
+
});
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
logger.info(`Deploying via ${provider.name}...`);
|
|
117
|
+
let deployResult;
|
|
118
|
+
try {
|
|
119
|
+
deployResult = await provider.deploy({
|
|
120
|
+
buildDir: providerDir,
|
|
121
|
+
logger,
|
|
122
|
+
onProgress: (_step, _detail) => {
|
|
123
|
+
process.stdout.write(` ${ANSI.green}done${ANSI.reset}\n`);
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
129
|
+
deployResult = {
|
|
130
|
+
success: false,
|
|
131
|
+
errors: [{ step: 'deploy', error: message }],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
await writeResultFile(resultFile, deployResult);
|
|
135
|
+
console.log('');
|
|
136
|
+
if (deployResult.success) {
|
|
137
|
+
logger.info(`${ANSI.green}${ANSI.bold}Deployment complete.${ANSI.reset}`);
|
|
138
|
+
logger.info(` ${deployResult.workersDeployed?.length ?? 0} units deployed, ${deployResult.resourcesCreated?.length ?? 0} resources created`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
logger.error(`Deployment finished with ${deployResult.errors.length} error(s):`);
|
|
142
|
+
for (const e of deployResult.errors) {
|
|
143
|
+
logger.error(` ${e.step}: ${e.error}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export const deployApply = pikkuSessionlessFunc({
|
|
148
|
+
func: async ({ logger, config, getInspectorState }, data) => {
|
|
149
|
+
const projectDir = config.rootDir;
|
|
150
|
+
const provider = await resolveProvider(config, data?.provider);
|
|
151
|
+
const fromPlan = data?.fromPlan ?? false;
|
|
152
|
+
const resultFile = data?.resultFile;
|
|
153
|
+
if (fromPlan) {
|
|
154
|
+
// Skip build pipeline — deploy from existing plan output
|
|
155
|
+
const { join } = await import('node:path');
|
|
156
|
+
const { existsSync } = await import('node:fs');
|
|
157
|
+
const providerDir = join(projectDir, '.deploy', provider.deployDirName);
|
|
158
|
+
const infraPath = join(providerDir, 'infra.json');
|
|
159
|
+
if (!existsSync(infraPath)) {
|
|
160
|
+
logger.error(`No plan found at ${providerDir}. Run 'pikku deploy plan' first.`);
|
|
161
|
+
await writeResultFile(resultFile, {
|
|
162
|
+
success: false,
|
|
163
|
+
errors: [{ step: 'plan', error: 'No plan found' }],
|
|
164
|
+
});
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
await runDeploy(provider, providerDir, logger, resultFile);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// Full build + deploy pipeline
|
|
171
|
+
const inspectorState = await getInspectorState(true);
|
|
172
|
+
const projectId = await resolveProjectId(projectDir);
|
|
173
|
+
const buildResult = await runBuildPipeline({
|
|
174
|
+
projectDir,
|
|
175
|
+
projectId,
|
|
176
|
+
provider,
|
|
177
|
+
inspectorState,
|
|
178
|
+
serverlessIncompatible: config.deploy?.serverlessIncompatible,
|
|
179
|
+
getEntryContext,
|
|
180
|
+
logger,
|
|
181
|
+
});
|
|
182
|
+
if (buildResult.manifest.units.length === 0) {
|
|
183
|
+
logger.info('No deployment units found. Nothing to deploy.');
|
|
184
|
+
await writeResultFile(resultFile, {
|
|
185
|
+
success: true,
|
|
186
|
+
workersDeployed: [],
|
|
187
|
+
resourcesCreated: [],
|
|
188
|
+
errors: [],
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const { providerDir, bundled } = buildResult;
|
|
193
|
+
if (typeof provider.deploy === 'function') {
|
|
194
|
+
await runDeploy(provider, providerDir, logger, resultFile);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
logger.info(`${ANSI.green}${ANSI.bold}Build complete.${ANSI.reset}`);
|
|
198
|
+
logger.info(` ${bundled.length} functions bundled to ${ANSI.bold}${providerDir}${ANSI.reset}`);
|
|
199
|
+
await writeResultFile(resultFile, {
|
|
200
|
+
success: true,
|
|
201
|
+
buildOnly: true,
|
|
202
|
+
unitCount: bundled.length,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const deployInfo: import("#pikku").PikkuFunctionConfig<void, void, "session" | "rpc", import("#pikku").PikkuFunctionSessionless<void, void, "session" | "rpc", import("#pikku").Services> | import("#pikku").PikkuFunction<void, void, "session" | "rpc", import("#pikku").Services>, undefined, undefined>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { basename, join } from 'node:path';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { pikkuVoidFunc } from '#pikku';
|
|
4
|
+
import { analyzeDeployment } from '../../deploy/analyzer/index.js';
|
|
5
|
+
const ANSI = {
|
|
6
|
+
green: '\x1b[32m',
|
|
7
|
+
blue: '\x1b[34m',
|
|
8
|
+
yellow: '\x1b[33m',
|
|
9
|
+
dim: '\x1b[2m',
|
|
10
|
+
bold: '\x1b[1m',
|
|
11
|
+
reset: '\x1b[0m',
|
|
12
|
+
};
|
|
13
|
+
const ROLE_COLORS = {
|
|
14
|
+
function: ANSI.blue,
|
|
15
|
+
mcp: '\x1b[35m', // magenta
|
|
16
|
+
agent: '\x1b[33m', // yellow
|
|
17
|
+
channel: ANSI.dim,
|
|
18
|
+
'workflow-step': '\x1b[34m',
|
|
19
|
+
workflow: '\x1b[34m',
|
|
20
|
+
};
|
|
21
|
+
function padRight(str, len) {
|
|
22
|
+
return str + ' '.repeat(Math.max(0, len - str.length));
|
|
23
|
+
}
|
|
24
|
+
function sanitizeProjectId(raw) {
|
|
25
|
+
return (raw
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
28
|
+
.replace(/-+/g, '-')
|
|
29
|
+
.replace(/^-|-$/g, '') || 'pikku-project');
|
|
30
|
+
}
|
|
31
|
+
async function resolveProjectId(projectDir) {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(await readFile(join(projectDir, 'package.json'), 'utf-8'));
|
|
34
|
+
if (pkg.name) {
|
|
35
|
+
const name = pkg.name.replace(/^@[^/]+\//, '');
|
|
36
|
+
return sanitizeProjectId(name);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
return sanitizeProjectId(basename(projectDir));
|
|
41
|
+
}
|
|
42
|
+
export const deployInfo = pikkuVoidFunc({
|
|
43
|
+
func: async ({ logger, config, getInspectorState }) => {
|
|
44
|
+
logger.info('Analyzing project...');
|
|
45
|
+
const inspectorState = await getInspectorState(true);
|
|
46
|
+
const projectId = await resolveProjectId(config.rootDir);
|
|
47
|
+
const manifest = analyzeDeployment(inspectorState, { projectId });
|
|
48
|
+
console.log('');
|
|
49
|
+
console.log(`${ANSI.bold}Project:${ANSI.reset} ${manifest.projectId}`);
|
|
50
|
+
console.log('');
|
|
51
|
+
// Units
|
|
52
|
+
console.log(`${ANSI.bold}Units (${manifest.units.length}):${ANSI.reset}`);
|
|
53
|
+
for (const u of manifest.units) {
|
|
54
|
+
const color = ROLE_COLORS[u.role] ?? ANSI.dim;
|
|
55
|
+
const fns = u.functionIds.join(', ');
|
|
56
|
+
console.log(` ${color}${padRight(u.role, 22)}${ANSI.reset} ${ANSI.bold}${padRight(u.name, 30)}${ANSI.reset} ${ANSI.dim}[${fns}]${ANSI.reset}`);
|
|
57
|
+
for (const handler of u.handlers) {
|
|
58
|
+
if (handler.type === 'fetch' && handler.routes.length > 0) {
|
|
59
|
+
for (const route of handler.routes) {
|
|
60
|
+
console.log(` ${ANSI.dim}${route.method} ${route.route}${ANSI.reset}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else if (handler.type === 'queue') {
|
|
64
|
+
console.log(` ${ANSI.dim}queue: ${handler.queueName}${ANSI.reset}`);
|
|
65
|
+
}
|
|
66
|
+
else if (handler.type === 'scheduled') {
|
|
67
|
+
console.log(` ${ANSI.dim}cron: ${handler.schedule}${ANSI.reset}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Queues
|
|
72
|
+
if (manifest.queues.length > 0) {
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log(`${ANSI.bold}Queues (${manifest.queues.length}):${ANSI.reset}`);
|
|
75
|
+
for (const q of manifest.queues) {
|
|
76
|
+
console.log(` ${padRight(q.name, 30)} ${ANSI.dim}-> ${q.consumerUnit}${ANSI.reset}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Scheduled tasks
|
|
80
|
+
if (manifest.scheduledTasks.length > 0) {
|
|
81
|
+
console.log('');
|
|
82
|
+
console.log(`${ANSI.bold}Scheduled Tasks (${manifest.scheduledTasks.length}):${ANSI.reset}`);
|
|
83
|
+
for (const t of manifest.scheduledTasks) {
|
|
84
|
+
console.log(` ${padRight(t.name, 30)} ${ANSI.dim}${t.schedule} -> ${t.unitName}${ANSI.reset}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Channels
|
|
88
|
+
if (manifest.channels.length > 0) {
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(`${ANSI.bold}Channels (${manifest.channels.length}):${ANSI.reset}`);
|
|
91
|
+
for (const c of manifest.channels) {
|
|
92
|
+
console.log(` ${padRight(c.name, 30)} ${ANSI.dim}${c.route} -> ${c.unitName}${ANSI.reset}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Agents
|
|
96
|
+
if (manifest.agents.length > 0) {
|
|
97
|
+
console.log('');
|
|
98
|
+
console.log(`${ANSI.bold}Agents (${manifest.agents.length}):${ANSI.reset}`);
|
|
99
|
+
for (const a of manifest.agents) {
|
|
100
|
+
const tools = a.toolFunctionIds.join(', ');
|
|
101
|
+
console.log(` ${padRight(a.name, 30)} ${ANSI.dim}[${tools}]${ANSI.reset}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Secrets
|
|
105
|
+
if (manifest.secrets.length > 0) {
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(`${ANSI.bold}Required Secrets (${manifest.secrets.length}):${ANSI.reset}`);
|
|
108
|
+
for (const s of manifest.secrets) {
|
|
109
|
+
console.log(` ${ANSI.yellow}${s.secretId}${ANSI.reset}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Variables
|
|
113
|
+
if (manifest.variables.length > 0) {
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(`${ANSI.bold}Variables (${manifest.variables.length}):${ANSI.reset}`);
|
|
116
|
+
for (const v of manifest.variables) {
|
|
117
|
+
console.log(` ${v.variableId}${ANSI.dim} (${v.displayName})${ANSI.reset}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log('');
|
|
121
|
+
},
|
|
122
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const deployPlan: import("#pikku").PikkuFunctionConfig<{
|
|
2
|
+
resultFile?: string;
|
|
3
|
+
provider?: string;
|
|
4
|
+
}, void, "session" | "rpc", import("#pikku").PikkuFunctionSessionless<{
|
|
5
|
+
resultFile?: string;
|
|
6
|
+
provider?: string;
|
|
7
|
+
}, void, "session" | "rpc", import("#pikku").Services> | import("#pikku").PikkuFunction<{
|
|
8
|
+
resultFile?: string;
|
|
9
|
+
provider?: string;
|
|
10
|
+
}, void, "session" | "rpc", import("#pikku").Services>, undefined, undefined>;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { basename, join } from 'node:path';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { pikkuSessionlessFunc } from '#pikku';
|
|
4
|
+
import { generatePlan } from '../../deploy/plan/index.js';
|
|
5
|
+
import { formatPlan } from '../../deploy/plan/formatter.js';
|
|
6
|
+
import { runBuildPipeline } from '../../deploy/build-pipeline.js';
|
|
7
|
+
import { resolveProvider, getEntryContext } from './deploy-apply.js';
|
|
8
|
+
function sanitizeProjectId(raw) {
|
|
9
|
+
return (raw
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
12
|
+
.replace(/-+/g, '-')
|
|
13
|
+
.replace(/^-|-$/g, '') || 'pikku-project');
|
|
14
|
+
}
|
|
15
|
+
async function resolveProjectId(projectDir) {
|
|
16
|
+
try {
|
|
17
|
+
const pkg = JSON.parse(await readFile(join(projectDir, 'package.json'), 'utf-8'));
|
|
18
|
+
if (pkg.name) {
|
|
19
|
+
const name = pkg.name.replace(/^@[^/]+\//, '');
|
|
20
|
+
return sanitizeProjectId(name);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
return sanitizeProjectId(basename(projectDir));
|
|
25
|
+
}
|
|
26
|
+
function createEmptyProvider() {
|
|
27
|
+
return {
|
|
28
|
+
async getCurrentState() {
|
|
29
|
+
return {
|
|
30
|
+
units: [],
|
|
31
|
+
queues: [],
|
|
32
|
+
scheduledTasks: [],
|
|
33
|
+
secrets: [],
|
|
34
|
+
variables: {},
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
async applyChange() {
|
|
38
|
+
throw new Error('Plan mode — apply not available');
|
|
39
|
+
},
|
|
40
|
+
async hasActiveWork() {
|
|
41
|
+
return { active: false, pendingCount: 0 };
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export const deployPlan = pikkuSessionlessFunc({
|
|
46
|
+
func: async ({ logger, config, getInspectorState }, data) => {
|
|
47
|
+
const projectDir = config.rootDir;
|
|
48
|
+
const inspectorState = await getInspectorState(true);
|
|
49
|
+
const projectId = await resolveProjectId(projectDir);
|
|
50
|
+
const provider = await resolveProvider(config, data?.provider);
|
|
51
|
+
const result = await runBuildPipeline({
|
|
52
|
+
projectDir,
|
|
53
|
+
projectId,
|
|
54
|
+
provider,
|
|
55
|
+
inspectorState,
|
|
56
|
+
serverlessIncompatible: config.deploy?.serverlessIncompatible,
|
|
57
|
+
getEntryContext,
|
|
58
|
+
logger,
|
|
59
|
+
});
|
|
60
|
+
if (result.manifest.units.length === 0) {
|
|
61
|
+
logger.info('No deployment units found.');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// Show plan
|
|
65
|
+
const deployProvider = createEmptyProvider();
|
|
66
|
+
const currentState = await deployProvider.getCurrentState(result.manifest.projectId);
|
|
67
|
+
const plan = await generatePlan(result.manifest, currentState, deployProvider);
|
|
68
|
+
console.log('');
|
|
69
|
+
console.log(formatPlan(plan));
|
|
70
|
+
console.log('');
|
|
71
|
+
// Summary
|
|
72
|
+
let totalSize = 0;
|
|
73
|
+
for (const b of result.bundled)
|
|
74
|
+
totalSize += b.bundleSizeBytes;
|
|
75
|
+
const formatBytes = (bytes) => bytes < 1024
|
|
76
|
+
? `${bytes}B`
|
|
77
|
+
: bytes < 1024 * 1024
|
|
78
|
+
? `${(bytes / 1024).toFixed(1)}KB`
|
|
79
|
+
: `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
80
|
+
logger.info(`${result.bundled.length} workers bundled (${formatBytes(totalSize)} total)`);
|
|
81
|
+
logger.info(`Output: ${result.providerDir}`);
|
|
82
|
+
// Write result file if --result-file is set (used by Fabric build pipeline)
|
|
83
|
+
const resultFile = data?.resultFile;
|
|
84
|
+
if (resultFile) {
|
|
85
|
+
const { writeFile } = await import('node:fs/promises');
|
|
86
|
+
await writeFile(resultFile, JSON.stringify({
|
|
87
|
+
success: result.bundleErrors.length === 0,
|
|
88
|
+
providerDir: result.providerDir,
|
|
89
|
+
unitCount: result.bundled.length,
|
|
90
|
+
totalSizeBytes: totalSize,
|
|
91
|
+
errors: result.bundleErrors,
|
|
92
|
+
manifest: result.manifest,
|
|
93
|
+
}, null, 2), 'utf-8');
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
@@ -8,7 +8,7 @@ const FEATURE_DEFAULTS = {
|
|
|
8
8
|
workflow: 'auth',
|
|
9
9
|
};
|
|
10
10
|
async function enableFeature(feature, logger, config, data) {
|
|
11
|
-
const noAuth = data?.
|
|
11
|
+
const noAuth = data?.noAuth ?? false;
|
|
12
12
|
const configPath = join(config.configDir, 'pikku.config.json');
|
|
13
13
|
const raw = await readFile(configPath, 'utf-8');
|
|
14
14
|
const json = JSON.parse(raw);
|
|
@@ -11,6 +11,7 @@ export declare const pikkuNewAddon: import("#pikku").PikkuFunctionConfig<{
|
|
|
11
11
|
test?: boolean;
|
|
12
12
|
openapi?: string;
|
|
13
13
|
mcp?: boolean;
|
|
14
|
+
camelCase?: boolean;
|
|
14
15
|
}, void, "session" | "rpc", import("#pikku").PikkuFunctionSessionless<{
|
|
15
16
|
name: string;
|
|
16
17
|
displayName?: string;
|
|
@@ -24,6 +25,7 @@ export declare const pikkuNewAddon: import("#pikku").PikkuFunctionConfig<{
|
|
|
24
25
|
test?: boolean;
|
|
25
26
|
openapi?: string;
|
|
26
27
|
mcp?: boolean;
|
|
28
|
+
camelCase?: boolean;
|
|
27
29
|
}, void, "session" | "rpc", import("#pikku").Services> | import("#pikku").PikkuFunction<{
|
|
28
30
|
name: string;
|
|
29
31
|
displayName?: string;
|
|
@@ -37,4 +39,5 @@ export declare const pikkuNewAddon: import("#pikku").PikkuFunctionConfig<{
|
|
|
37
39
|
test?: boolean;
|
|
38
40
|
openapi?: string;
|
|
39
41
|
mcp?: boolean;
|
|
42
|
+
camelCase?: boolean;
|
|
40
43
|
}, void, "session" | "rpc", import("#pikku").Services>, undefined, undefined>;
|
|
@@ -5,7 +5,7 @@ import { createEmptyManifest, saveManifest, } from '../../utils/contract-version
|
|
|
5
5
|
import { pikkuSessionlessFunc } from '#pikku';
|
|
6
6
|
import { parseOpenAPISpec, computeContractHash, generateAddonFromOpenAPI, } from '@pikku/openapi-parser';
|
|
7
7
|
function toCamelCase(str) {
|
|
8
|
-
return str.replace(/-([a-
|
|
8
|
+
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
9
9
|
}
|
|
10
10
|
function toPascalCase(str) {
|
|
11
11
|
const camel = toCamelCase(str);
|
|
@@ -14,6 +14,69 @@ function toPascalCase(str) {
|
|
|
14
14
|
function toScreamingSnake(str) {
|
|
15
15
|
return str.replace(/-/g, '_').toUpperCase();
|
|
16
16
|
}
|
|
17
|
+
const TLD_SEGMENTS = new Set([
|
|
18
|
+
'com',
|
|
19
|
+
'io',
|
|
20
|
+
'org',
|
|
21
|
+
'net',
|
|
22
|
+
'co',
|
|
23
|
+
'dev',
|
|
24
|
+
'app',
|
|
25
|
+
'us',
|
|
26
|
+
'uk',
|
|
27
|
+
'eu',
|
|
28
|
+
'de',
|
|
29
|
+
'fr',
|
|
30
|
+
'nl',
|
|
31
|
+
'ch',
|
|
32
|
+
'ca',
|
|
33
|
+
'au',
|
|
34
|
+
'gov',
|
|
35
|
+
'edu',
|
|
36
|
+
'local',
|
|
37
|
+
'cloud',
|
|
38
|
+
'ai',
|
|
39
|
+
'fm',
|
|
40
|
+
'tv',
|
|
41
|
+
'me',
|
|
42
|
+
'cc',
|
|
43
|
+
'info',
|
|
44
|
+
'biz',
|
|
45
|
+
'xyz',
|
|
46
|
+
'tech',
|
|
47
|
+
'space',
|
|
48
|
+
'online',
|
|
49
|
+
'site',
|
|
50
|
+
'store',
|
|
51
|
+
'ac',
|
|
52
|
+
'int',
|
|
53
|
+
'mil',
|
|
54
|
+
'ninja',
|
|
55
|
+
'guru',
|
|
56
|
+
]);
|
|
57
|
+
function sanitizeAddonName(raw) {
|
|
58
|
+
const dotParts = raw.toLowerCase().split('.');
|
|
59
|
+
const kept = [];
|
|
60
|
+
for (const part of dotParts) {
|
|
61
|
+
const clean = part.replace(/^-|-$/g, '');
|
|
62
|
+
if (TLD_SEGMENTS.has(clean))
|
|
63
|
+
continue;
|
|
64
|
+
const hyphenIdx = part.indexOf('-');
|
|
65
|
+
if (hyphenIdx > 0 && TLD_SEGMENTS.has(part.slice(0, hyphenIdx))) {
|
|
66
|
+
kept.push(part.slice(hyphenIdx + 1));
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
kept.push(part);
|
|
70
|
+
}
|
|
71
|
+
let name = kept
|
|
72
|
+
.join('-')
|
|
73
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
74
|
+
.replace(/-+/g, '-')
|
|
75
|
+
.replace(/^-|-$/g, '');
|
|
76
|
+
if (name && /^[0-9]/.test(name))
|
|
77
|
+
name = `x${name}`;
|
|
78
|
+
return name || raw;
|
|
79
|
+
}
|
|
17
80
|
function getAddonFiles(vars, flags) {
|
|
18
81
|
const { name, camelName, pascalName, screamingName, displayName, description, category, } = vars;
|
|
19
82
|
const files = {};
|
|
@@ -112,7 +175,7 @@ ${description}
|
|
|
112
175
|
`;
|
|
113
176
|
// src/services.ts
|
|
114
177
|
if (flags.credential && flags.credential !== 'oauth2') {
|
|
115
|
-
// Per-user credential: use createWireServices with wire.
|
|
178
|
+
// Per-user credential: use createWireServices with wire.getCredential()
|
|
116
179
|
const credField = flags.credential === 'bearer' ? 'token' : 'apiKey';
|
|
117
180
|
files['src/services.ts'] =
|
|
118
181
|
`import { ${pascalName}Service } from './${name}-api.service.js'
|
|
@@ -120,8 +183,7 @@ import { pikkuAddonWireServices } from '#pikku'
|
|
|
120
183
|
|
|
121
184
|
export const createWireServices = pikkuAddonWireServices(
|
|
122
185
|
async ({ variables }, wire) => {
|
|
123
|
-
const
|
|
124
|
-
const cred = credentials['${camelName}'] as { ${credField}: string } | undefined
|
|
186
|
+
const cred = await wire.getCredential<{ ${credField}: string }>('${camelName}')
|
|
125
187
|
if (!cred?.${credField}) {
|
|
126
188
|
throw new Error('Missing ${camelName} credential')
|
|
127
189
|
}
|
|
@@ -701,7 +763,8 @@ async function writeFiles(baseDir, files) {
|
|
|
701
763
|
return written;
|
|
702
764
|
}
|
|
703
765
|
export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
704
|
-
func: async ({ logger, config }, { name, displayName, description, category = 'General', dir, secret = false, variable = false, oauth = false, credential, test = true, openapi, mcp = false, }) => {
|
|
766
|
+
func: async ({ logger, config }, { name, displayName, description, category = 'General', dir, secret = false, variable = false, oauth = false, credential, test = true, openapi, mcp = false, camelCase = false, }) => {
|
|
767
|
+
name = sanitizeAddonName(name);
|
|
705
768
|
if (!/^[a-z][a-z0-9_-]*$/.test(name)) {
|
|
706
769
|
logger.error(`Invalid addon name "${name}": must start with a lowercase letter and contain only lowercase alphanumerics, hyphens, and underscores`);
|
|
707
770
|
process.exit(1);
|
|
@@ -748,6 +811,7 @@ export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
|
748
811
|
secret: (secret || effectiveOAuth) && !credentialType,
|
|
749
812
|
credential: credentialType,
|
|
750
813
|
mcp,
|
|
814
|
+
camelCase,
|
|
751
815
|
});
|
|
752
816
|
Object.assign(addonFiles, openapiFiles);
|
|
753
817
|
// Inject openapi metadata into pikku.config.json
|
|
@@ -758,6 +822,7 @@ export const pikkuNewAddon = pikkuSessionlessFunc({
|
|
|
758
822
|
config.addon.openapi = {
|
|
759
823
|
version: spec.info.version,
|
|
760
824
|
hash: computeContractHash(spec),
|
|
825
|
+
...(camelCase ? { camelCase: true } : {}),
|
|
761
826
|
};
|
|
762
827
|
addonFiles['pikku.config.json'] = JSON.stringify(config, null, 2);
|
|
763
828
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { join, dirname } from 'node:path';
|
|
1
2
|
import { pikkuSessionlessFunc } from '#pikku';
|
|
2
3
|
import { getFileImportRelativePath } from '../../utils/file-import-path.js';
|
|
3
4
|
import { writeFileInDir } from '../../utils/file-writer.js';
|
|
@@ -13,26 +14,35 @@ export const pikkuBootstrap = pikkuSessionlessFunc({
|
|
|
13
14
|
const wireAddonFileImports = Array.from(stateBeforeBootstrap.rpc?.wireAddonFiles ?? []).map((to) => `import '${getFileImportRelativePath(config.bootstrapFile, to, config.packageMappings)}'`);
|
|
14
15
|
const localImports = allImports.map((to) => `import '${getFileImportRelativePath(config.bootstrapFile, to, config.packageMappings)}'`);
|
|
15
16
|
const addonImports = addonBootstraps.map((packagePath) => `import '${packagePath}'`);
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
17
|
+
const outDir = dirname(config.bootstrapFile);
|
|
18
|
+
const metaServiceFile = join(outDir, 'pikku-meta-service.gen.ts');
|
|
19
|
+
const escapedOutDir = outDir.replace(/\\/g, '/').replace(/'/g, "\\'");
|
|
20
|
+
const metaServiceContent = [
|
|
21
|
+
`import { LocalMetaService } from '@pikku/core/services/local-meta'`,
|
|
22
|
+
``,
|
|
23
|
+
`export class PikkuMetaService extends LocalMetaService {`,
|
|
24
|
+
` constructor() {`,
|
|
25
|
+
` super('${escapedOutDir}')`,
|
|
26
|
+
` }`,
|
|
27
|
+
`}`,
|
|
28
|
+
``,
|
|
29
|
+
].join('\n');
|
|
30
|
+
await writeFileInDir(logger, metaServiceFile, metaServiceContent);
|
|
31
|
+
const allBootstrapImports = [
|
|
32
|
+
...localImports,
|
|
33
|
+
...wireAddonFileImports,
|
|
34
|
+
...addonImports,
|
|
35
|
+
]
|
|
36
|
+
.sort((a, b) => {
|
|
37
|
+
const aMeta = a.includes('meta');
|
|
38
|
+
const bMeta = b.includes('meta');
|
|
39
|
+
if (aMeta && !bMeta)
|
|
40
|
+
return -1;
|
|
41
|
+
if (!aMeta && bMeta)
|
|
42
|
+
return 1;
|
|
43
|
+
return 0;
|
|
44
|
+
})
|
|
45
|
+
.join('\n');
|
|
36
46
|
await writeFileInDir(logger, config.bootstrapFile, allBootstrapImports);
|
|
37
47
|
},
|
|
38
48
|
});
|
|
@@ -35,10 +35,14 @@ export const pikkuNext = pikkuSessionlessFunc({
|
|
|
35
35
|
if (!pikkuConfigFactory || !singletonServicesFactory) {
|
|
36
36
|
throw new Error('Required types not found');
|
|
37
37
|
}
|
|
38
|
-
const
|
|
39
|
-
const
|
|
38
|
+
const configAlias = pikkuConfigFactory.variable === 'createConfig' ? '' : ' as createConfig';
|
|
39
|
+
const servicesAlias = singletonServicesFactory.variable === 'createSingletonServices'
|
|
40
|
+
? ''
|
|
41
|
+
: ' as createSingletonServices';
|
|
42
|
+
const pikkuConfigImport = `import { ${pikkuConfigFactory.variable}${configAlias} } from '${getFileImportRelativePath(nextBackendFile, pikkuConfigFactory.file, packageMappings)}'`;
|
|
43
|
+
const singletonServicesImport = `import { ${singletonServicesFactory.variable}${servicesAlias} } from '${getFileImportRelativePath(nextBackendFile, singletonServicesFactory.file, packageMappings)}'`;
|
|
40
44
|
const wireServicesImport = wireServicesFactory
|
|
41
|
-
? `import { ${wireServicesFactory.variable} as createWireServices } from '${getFileImportRelativePath(nextBackendFile, wireServicesFactory.file, packageMappings)}'`
|
|
45
|
+
? `import { ${wireServicesFactory.variable}${wireServicesFactory.variable === 'createWireServices' ? '' : ' as createWireServices'} } from '${getFileImportRelativePath(nextBackendFile, wireServicesFactory.file, packageMappings)}'`
|
|
42
46
|
: '';
|
|
43
47
|
const bootstrapPath = getFileImportRelativePath(nextBackendFile, config.bootstrapFile, packageMappings);
|
|
44
48
|
const routesMapDeclarationPath = getFileImportRelativePath(nextBackendFile, httpMapDeclarationFile, packageMappings);
|