@pikku/deploy-azure 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Azure Functions provider adapter for the Pikku deploy pipeline.
3
+ *
4
+ * Uses the Azure Functions v4 Node.js programming model where functions
5
+ * are registered in code via app.http(), app.storageQueue(), app.timer()
6
+ * rather than function.json files.
7
+ */
8
+ type DeploymentHandler = {
9
+ type: 'fetch';
10
+ routes: Array<{
11
+ method: string;
12
+ route: string;
13
+ pikkuFuncId: string;
14
+ }>;
15
+ } | {
16
+ type: 'queue';
17
+ queueName: string;
18
+ } | {
19
+ type: 'scheduled';
20
+ schedule: string;
21
+ taskName: string;
22
+ };
23
+ interface DeploymentUnit {
24
+ name: string;
25
+ role: string;
26
+ functionIds: string[];
27
+ services: Array<{
28
+ capability: string;
29
+ sourceServiceName: string;
30
+ }>;
31
+ dependsOn: string[];
32
+ handlers: DeploymentHandler[];
33
+ tags: string[];
34
+ }
35
+ interface DeploymentManifest {
36
+ projectId: string;
37
+ units: DeploymentUnit[];
38
+ queues: Array<{
39
+ name: string;
40
+ consumerUnit: string;
41
+ consumerFunctionId: string;
42
+ }>;
43
+ scheduledTasks: Array<{
44
+ name: string;
45
+ schedule: string;
46
+ unitName: string;
47
+ functionId: string;
48
+ }>;
49
+ channels: Array<{
50
+ name: string;
51
+ route: string;
52
+ unitName: string;
53
+ }>;
54
+ agents: Array<{
55
+ name: string;
56
+ unitName: string;
57
+ model: string;
58
+ }>;
59
+ mcpEndpoints: Array<{
60
+ unitName: string;
61
+ }>;
62
+ workflows: Array<{
63
+ name: string;
64
+ orchestratorUnit: string;
65
+ }>;
66
+ secrets: Array<{
67
+ secretId: string;
68
+ displayName: string;
69
+ description?: string;
70
+ }>;
71
+ variables: Array<{
72
+ variableId: string;
73
+ displayName: string;
74
+ description?: string;
75
+ }>;
76
+ }
77
+ interface EntryGenerationContext {
78
+ unit: DeploymentUnit;
79
+ unitDir: string;
80
+ bootstrapPath: string;
81
+ configImport: string;
82
+ configVar: string;
83
+ servicesImport: string;
84
+ servicesVar: string;
85
+ singletonServicesImport: string;
86
+ servicesType: string;
87
+ }
88
+ export declare class AzureProviderAdapter {
89
+ readonly name = "azure";
90
+ readonly deployDirName = "azure";
91
+ /**
92
+ * Externals for esbuild. Azure Functions runs on Node.js.
93
+ * Same as Lambda — node builtins + Azure SDK packages.
94
+ */
95
+ getExternals(): string[];
96
+ generateEntrySource(ctx: EntryGenerationContext): string;
97
+ /**
98
+ * Generates entry source for function/workflow-step units.
99
+ * Uses Azure Functions v4 app.http(), app.storageQueue(), app.timer().
100
+ */
101
+ private generateCombinedEntry;
102
+ /**
103
+ * Generates entry source for channel units (WebSocket via Web PubSub).
104
+ */
105
+ private generateChannelEntry;
106
+ /**
107
+ * Generates entry source for mcp, agent, and workflow units.
108
+ */
109
+ private generateGatewayEntry;
110
+ private generatePlatformServicesBlock;
111
+ private generateGatewayPlatformServicesBlock;
112
+ /**
113
+ * No per-unit config files — Azure Functions v4 uses code-based registration.
114
+ */
115
+ generateUnitConfigs(_unit: DeploymentUnit, _manifest: DeploymentManifest, _projectId: string): Map<string, string>;
116
+ generateInfraManifest(manifest: DeploymentManifest): string | null;
117
+ /**
118
+ * Generates host.json and local.settings.json for the Function App.
119
+ */
120
+ generateProviderConfigs(manifest: DeploymentManifest): Map<string, string>;
121
+ }
122
+ export {};
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Azure Functions provider adapter for the Pikku deploy pipeline.
3
+ *
4
+ * Uses the Azure Functions v4 Node.js programming model where functions
5
+ * are registered in code via app.http(), app.storageQueue(), app.timer()
6
+ * rather than function.json files.
7
+ */
8
+ import { generateInfraManifest } from './infra-manifest.js';
9
+ import { generateHostJson, generateLocalSettings } from './host-json.js';
10
+ function getHandlerTypes(unit) {
11
+ const types = new Set();
12
+ for (const handler of unit.handlers) {
13
+ types.add(handler.type);
14
+ }
15
+ return [...types];
16
+ }
17
+ export class AzureProviderAdapter {
18
+ name = 'azure';
19
+ deployDirName = 'azure';
20
+ /**
21
+ * Externals for esbuild. Azure Functions runs on Node.js.
22
+ * Same as Lambda — node builtins + Azure SDK packages.
23
+ */
24
+ getExternals() {
25
+ const builtins = [
26
+ 'buffer',
27
+ 'crypto',
28
+ 'events',
29
+ 'fs',
30
+ 'http',
31
+ 'https',
32
+ 'net',
33
+ 'os',
34
+ 'path',
35
+ 'querystring',
36
+ 'stream',
37
+ 'string_decoder',
38
+ 'tls',
39
+ 'url',
40
+ 'util',
41
+ 'zlib',
42
+ 'child_process',
43
+ 'worker_threads',
44
+ 'assert',
45
+ 'dns',
46
+ 'dgram',
47
+ 'readline',
48
+ 'tty',
49
+ 'v8',
50
+ 'vm',
51
+ ];
52
+ return ['node:*', ...builtins, '@azure/*'];
53
+ }
54
+ generateEntrySource(ctx) {
55
+ const { unit } = ctx;
56
+ if (unit.role === 'channel') {
57
+ return this.generateChannelEntry(ctx);
58
+ }
59
+ if (unit.role === 'mcp' || unit.role === 'agent') {
60
+ return this.generateGatewayEntry(ctx);
61
+ }
62
+ if (unit.role === 'workflow') {
63
+ return this.generateGatewayEntry(ctx, true);
64
+ }
65
+ return this.generateCombinedEntry(ctx);
66
+ }
67
+ /**
68
+ * Generates entry source for function/workflow-step units.
69
+ * Uses Azure Functions v4 app.http(), app.storageQueue(), app.timer().
70
+ */
71
+ generateCombinedEntry(ctx) {
72
+ const handlerTypes = getHandlerTypes(ctx.unit);
73
+ const lines = [
74
+ `// Generated Azure Functions entry for "${ctx.unit.name}" (${ctx.unit.role})`,
75
+ `import { app } from '@azure/functions'`,
76
+ `import { createAzureHandler } from '@pikku/azure-functions'`,
77
+ `import { AzureQueueService } from '@pikku/azure-functions'`,
78
+ `import { JsonConsoleLogger } from '@pikku/core/services'`,
79
+ ctx.configImport,
80
+ ctx.servicesImport,
81
+ ctx.singletonServicesImport,
82
+ `import { requiredSingletonServices } from './.pikku/pikku-services.gen.js'`,
83
+ `import '${ctx.bootstrapPath}'`,
84
+ ``,
85
+ ...this.generatePlatformServicesBlock(ctx),
86
+ ``,
87
+ `const handlers = createAzureHandler(`,
88
+ ` { createConfig: ${ctx.configVar}, createSingletonServices: ${ctx.servicesVar}, createPlatformServices },`,
89
+ ` ${JSON.stringify(handlerTypes)}`,
90
+ `)`,
91
+ ``,
92
+ ];
93
+ // Register Azure Functions v4 triggers
94
+ for (const handler of ctx.unit.handlers) {
95
+ if (handler.type === 'fetch') {
96
+ for (const route of handler.routes) {
97
+ const method = route.method.toLowerCase();
98
+ const azureRoute = route.route.replace(/:(\w+)/g, '{$1}');
99
+ lines.push(`app.http('${ctx.unit.name}-${method}-${safeName(route.route)}', {`, ` methods: ['${route.method}'],`, ` route: '${azureRoute}',`, ` authLevel: 'anonymous',`, ` handler: handlers.http,`, `})`, ``);
100
+ }
101
+ }
102
+ if (handler.type === 'queue') {
103
+ lines.push(`app.storageQueue('${ctx.unit.name}-queue-${safeName(handler.queueName)}', {`, ` queueName: '${handler.queueName}',`, ` connection: 'AzureWebJobsStorage',`, ` handler: handlers.queue,`, `})`, ``);
104
+ }
105
+ if (handler.type === 'scheduled') {
106
+ lines.push(`app.timer('${ctx.unit.name}-timer-${safeName(handler.taskName)}', {`, ` schedule: '${toAzureCron(handler.schedule)}',`, ` handler: handlers.timer,`, `})`, ``);
107
+ }
108
+ }
109
+ // If no explicit routes but has fetch handler (RPC access)
110
+ if (handlerTypes.includes('fetch') &&
111
+ !ctx.unit.handlers.some((h) => h.type === 'fetch' && h.routes.length > 0)) {
112
+ lines.push(`app.http('${ctx.unit.name}-rpc', {`, ` methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],`, ` route: '__pikku/${ctx.unit.name}/{*restOfPath}',`, ` authLevel: 'anonymous',`, ` handler: handlers.http,`, `})`, ``);
113
+ }
114
+ return lines.join('\n');
115
+ }
116
+ /**
117
+ * Generates entry source for channel units (WebSocket via Web PubSub).
118
+ */
119
+ generateChannelEntry(ctx) {
120
+ const lines = [
121
+ `// Generated Azure Functions WebSocket entry for "${ctx.unit.name}" (${ctx.unit.role})`,
122
+ `import { app } from '@azure/functions'`,
123
+ `import { createAzureWebSocketHandler } from '@pikku/azure-functions'`,
124
+ `import { JsonConsoleLogger } from '@pikku/core/services'`,
125
+ ctx.configImport,
126
+ ctx.servicesImport,
127
+ ctx.singletonServicesImport,
128
+ `import { requiredSingletonServices } from './.pikku/pikku-services.gen.js'`,
129
+ `import '${ctx.bootstrapPath}'`,
130
+ ``,
131
+ ...this.generatePlatformServicesBlock(ctx),
132
+ ``,
133
+ `const handlers = createAzureWebSocketHandler(`,
134
+ ` { createConfig: ${ctx.configVar}, createSingletonServices: ${ctx.servicesVar}, createPlatformServices }`,
135
+ `)`,
136
+ ``,
137
+ `app.http('${ctx.unit.name}-connect', {`,
138
+ ` methods: ['GET'],`,
139
+ ` route: 'ws/${ctx.unit.name}',`,
140
+ ` authLevel: 'anonymous',`,
141
+ ` handler: handlers.negotiate,`,
142
+ `})`,
143
+ ``,
144
+ ];
145
+ return lines.join('\n');
146
+ }
147
+ /**
148
+ * Generates entry source for mcp, agent, and workflow units.
149
+ */
150
+ generateGatewayEntry(ctx, includeQueueHandler = false) {
151
+ const bindingEntries = ctx.unit.dependsOn.map((dep) => {
152
+ return ` '${fromKebab(dep)}': process.env.AZURE_FUNC_URL_${toScreamingSnake(dep)} || ''`;
153
+ });
154
+ const bindingsMap = `{\n${bindingEntries.join(',\n')}\n }`;
155
+ const handlerTypes = includeQueueHandler
156
+ ? `["fetch", "queue"]`
157
+ : `["fetch"]`;
158
+ const lines = [
159
+ `// Generated Azure Functions gateway entry for "${ctx.unit.name}" (${ctx.unit.role})`,
160
+ `import { app } from '@azure/functions'`,
161
+ `import { createAzureHandler } from '@pikku/azure-functions'`,
162
+ `import { AzureQueueService, AzureDeploymentService } from '@pikku/azure-functions'`,
163
+ `import { JsonConsoleLogger } from '@pikku/core/services'`,
164
+ ctx.configImport,
165
+ ctx.servicesImport,
166
+ ctx.singletonServicesImport,
167
+ `import { requiredSingletonServices } from './.pikku/pikku-services.gen.js'`,
168
+ `import '${ctx.bootstrapPath}'`,
169
+ ``,
170
+ ...this.generateGatewayPlatformServicesBlock(ctx, bindingsMap),
171
+ ``,
172
+ `const handlers = createAzureHandler(`,
173
+ ` { createConfig: ${ctx.configVar}, createSingletonServices: ${ctx.servicesVar}, createPlatformServices },`,
174
+ ` ${handlerTypes}`,
175
+ `)`,
176
+ ``,
177
+ ];
178
+ // Register HTTP routes for the gateway
179
+ for (const handler of ctx.unit.handlers) {
180
+ if (handler.type === 'fetch') {
181
+ for (const route of handler.routes) {
182
+ const method = route.method.toLowerCase();
183
+ const azureRoute = route.route.replace(/:(\w+)/g, '{$1}');
184
+ lines.push(`app.http('${ctx.unit.name}-${method}-${safeName(route.route)}', {`, ` methods: ['${route.method}'],`, ` route: '${azureRoute}',`, ` authLevel: 'anonymous',`, ` handler: handlers.http,`, `})`, ``);
185
+ }
186
+ }
187
+ if (handler.type === 'queue') {
188
+ lines.push(`app.storageQueue('${ctx.unit.name}-queue', {`, ` queueName: '${handler.queueName}',`, ` connection: 'AzureWebJobsStorage',`, ` handler: handlers.queue,`, `})`, ``);
189
+ }
190
+ }
191
+ // Catch-all for RPC if no explicit routes
192
+ if (!ctx.unit.handlers.some((h) => h.type === 'fetch' && h.routes.length > 0)) {
193
+ lines.push(`app.http('${ctx.unit.name}-rpc', {`, ` methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],`, ` route: '__pikku/${ctx.unit.name}/{*restOfPath}',`, ` authLevel: 'anonymous',`, ` handler: handlers.http,`, `})`, ``);
194
+ }
195
+ return lines.join('\n');
196
+ }
197
+ generatePlatformServicesBlock(ctx) {
198
+ return [
199
+ `const createPlatformServices = async (): Promise<${ctx.servicesType}> => {`,
200
+ ` const services: ${ctx.servicesType} = {}`,
201
+ ` const logger = new JsonConsoleLogger()`,
202
+ ` services.logger = logger`,
203
+ ` if (requiredSingletonServices.queueService) {`,
204
+ ` services.queueService = new AzureQueueService()`,
205
+ ` }`,
206
+ ` return services`,
207
+ `}`,
208
+ ];
209
+ }
210
+ generateGatewayPlatformServicesBlock(ctx, bindingsMap) {
211
+ return [
212
+ `const createPlatformServices = async (existingServices?: Record<string, unknown>): Promise<${ctx.servicesType}> => {`,
213
+ ` const services: ${ctx.servicesType} = {}`,
214
+ ` const logger = new JsonConsoleLogger()`,
215
+ ` services.logger = logger`,
216
+ ` if (requiredSingletonServices.queueService) {`,
217
+ ` services.queueService = new AzureQueueService()`,
218
+ ` }`,
219
+ ` services.deploymentService = new AzureDeploymentService(`,
220
+ ` existingServices?.jwt as any,`,
221
+ ` existingServices?.secrets as any,`,
222
+ ` ${bindingsMap}`,
223
+ ` )`,
224
+ ` return services`,
225
+ `}`,
226
+ ];
227
+ }
228
+ /**
229
+ * No per-unit config files — Azure Functions v4 uses code-based registration.
230
+ */
231
+ generateUnitConfigs(_unit, _manifest, _projectId) {
232
+ return new Map();
233
+ }
234
+ generateInfraManifest(manifest) {
235
+ const infraManifest = generateInfraManifest(manifest);
236
+ return JSON.stringify(infraManifest, null, 2);
237
+ }
238
+ /**
239
+ * Generates host.json and local.settings.json for the Function App.
240
+ */
241
+ generateProviderConfigs(manifest) {
242
+ const configs = new Map();
243
+ configs.set('host.json', generateHostJson());
244
+ configs.set('local.settings.json', generateLocalSettings(manifest.secrets, manifest.variables));
245
+ return configs;
246
+ }
247
+ }
248
+ function toScreamingSnake(name) {
249
+ return name
250
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
251
+ .replace(/-/g, '_')
252
+ .toUpperCase();
253
+ }
254
+ function fromKebab(str) {
255
+ return str.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
256
+ }
257
+ function safeName(str) {
258
+ return str.replace(/[^a-zA-Z0-9]/g, '-').replace(/-+/g, '-');
259
+ }
260
+ /**
261
+ * Converts 5-field UNIX cron to 6-field Azure cron (NCrontab).
262
+ * Azure uses: {second} {minute} {hour} {day} {month} {day-of-week}
263
+ */
264
+ function toAzureCron(schedule) {
265
+ const parts = schedule.trim().split(/\s+/);
266
+ if (parts.length >= 6)
267
+ return schedule;
268
+ if (parts.length === 5) {
269
+ return `0 ${schedule}`;
270
+ }
271
+ return schedule;
272
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Generates host.json for Azure Functions.
3
+ *
4
+ * This is the global configuration file for the Function App.
5
+ */
6
+ export declare function generateHostJson(): string;
7
+ /**
8
+ * Generates local.settings.json for Azure Functions local dev.
9
+ *
10
+ * Contains environment variables and connection strings.
11
+ */
12
+ export declare function generateLocalSettings(secrets: Array<{
13
+ secretId: string;
14
+ }>, variables: Array<{
15
+ variableId: string;
16
+ }>): string;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Generates host.json for Azure Functions.
3
+ *
4
+ * This is the global configuration file for the Function App.
5
+ */
6
+ export function generateHostJson() {
7
+ return JSON.stringify({
8
+ version: '2.0',
9
+ logging: {
10
+ applicationInsights: {
11
+ samplingSettings: {
12
+ isEnabled: true,
13
+ excludedTypes: 'Request',
14
+ },
15
+ },
16
+ },
17
+ extensionBundle: {
18
+ id: 'Microsoft.Azure.Functions.ExtensionBundle',
19
+ version: '[4.*, 5.0.0)',
20
+ },
21
+ extensions: {
22
+ http: {
23
+ routePrefix: '',
24
+ },
25
+ },
26
+ }, null, 2);
27
+ }
28
+ /**
29
+ * Generates local.settings.json for Azure Functions local dev.
30
+ *
31
+ * Contains environment variables and connection strings.
32
+ */
33
+ export function generateLocalSettings(secrets, variables) {
34
+ const values = {
35
+ AzureWebJobsStorage: 'UseDevelopmentStorage=true',
36
+ FUNCTIONS_WORKER_RUNTIME: 'node',
37
+ };
38
+ for (const secret of secrets) {
39
+ values[secret.secretId] = '';
40
+ }
41
+ for (const variable of variables) {
42
+ values[variable.variableId] = '';
43
+ }
44
+ return JSON.stringify({
45
+ IsEncrypted: false,
46
+ Values: values,
47
+ }, null, 2);
48
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @pikku/deploy-azure — Azure Functions adapter for Pikku.
3
+ *
4
+ * Generates Azure Functions v4 entry points with code-based trigger
5
+ * registration, host.json, and local.settings.json.
6
+ */
7
+ import { AzureProviderAdapter } from './adapter.js';
8
+ export { AzureProviderAdapter };
9
+ export declare const createAdapter: () => AzureProviderAdapter;
10
+ export { generateInfraManifest } from './infra-manifest.js';
11
+ export { generateHostJson, generateLocalSettings } from './host-json.js';
12
+ export type { AzureInfraManifest, AzureUnitManifest, AzureQueueResource, AzureBlobResource, AzureTimerResource, AzureWebPubSubResource, } from './types.js';
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @pikku/deploy-azure — Azure Functions adapter for Pikku.
3
+ *
4
+ * Generates Azure Functions v4 entry points with code-based trigger
5
+ * registration, host.json, and local.settings.json.
6
+ */
7
+ import { AzureProviderAdapter } from './adapter.js';
8
+ export { AzureProviderAdapter };
9
+ export const createAdapter = () => new AzureProviderAdapter();
10
+ export { generateInfraManifest } from './infra-manifest.js';
11
+ export { generateHostJson, generateLocalSettings } from './host-json.js';
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Generates an Azure-specific infrastructure manifest from a provider-agnostic
3
+ * DeploymentManifest.
4
+ */
5
+ import type { AzureInfraManifest } from './types.js';
6
+ type DeploymentHandler = {
7
+ type: 'fetch';
8
+ routes: Array<{
9
+ method: string;
10
+ route: string;
11
+ pikkuFuncId: string;
12
+ }>;
13
+ } | {
14
+ type: 'queue';
15
+ queueName: string;
16
+ } | {
17
+ type: 'scheduled';
18
+ schedule: string;
19
+ taskName: string;
20
+ };
21
+ interface DeploymentUnit {
22
+ name: string;
23
+ role: string;
24
+ functionIds: string[];
25
+ services: Array<{
26
+ capability: string;
27
+ sourceServiceName: string;
28
+ }>;
29
+ dependsOn: string[];
30
+ handlers: DeploymentHandler[];
31
+ tags: string[];
32
+ }
33
+ interface DeploymentManifest {
34
+ projectId: string;
35
+ units: DeploymentUnit[];
36
+ queues: Array<{
37
+ name: string;
38
+ consumerUnit: string;
39
+ consumerFunctionId: string;
40
+ }>;
41
+ scheduledTasks: Array<{
42
+ name: string;
43
+ schedule: string;
44
+ unitName: string;
45
+ functionId: string;
46
+ }>;
47
+ channels: Array<{
48
+ name: string;
49
+ route: string;
50
+ unitName: string;
51
+ }>;
52
+ agents: Array<{
53
+ name: string;
54
+ unitName: string;
55
+ model: string;
56
+ }>;
57
+ mcpEndpoints: Array<{
58
+ unitName: string;
59
+ }>;
60
+ workflows: Array<{
61
+ name: string;
62
+ orchestratorUnit: string;
63
+ }>;
64
+ secrets: Array<{
65
+ secretId: string;
66
+ displayName: string;
67
+ description?: string;
68
+ }>;
69
+ variables: Array<{
70
+ variableId: string;
71
+ displayName: string;
72
+ description?: string;
73
+ }>;
74
+ }
75
+ export declare function generateInfraManifest(manifest: DeploymentManifest): AzureInfraManifest;
76
+ export {};
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Generates an Azure-specific infrastructure manifest from a provider-agnostic
3
+ * DeploymentManifest.
4
+ */
5
+ export function generateInfraManifest(manifest) {
6
+ const projectId = manifest.projectId;
7
+ // Storage queues
8
+ const storageQueues = [];
9
+ for (const q of manifest.queues) {
10
+ storageQueues.push({
11
+ name: q.name,
12
+ consumerUnit: q.consumerUnit,
13
+ });
14
+ }
15
+ // Blob containers — if any unit needs object-storage
16
+ const needsStorage = manifest.units.some((u) => u.services.some((s) => s.capability === 'object-storage'));
17
+ const blobContainers = needsStorage ? [{ name: `${projectId}-storage`, binding: 'STORAGE' }] : [];
18
+ // Timer triggers
19
+ const timerTriggers = [];
20
+ for (const unit of manifest.units) {
21
+ for (const handler of unit.handlers) {
22
+ if (handler.type === 'scheduled') {
23
+ timerTriggers.push({
24
+ unit: unit.name,
25
+ schedule: handler.schedule,
26
+ name: handler.taskName,
27
+ });
28
+ }
29
+ }
30
+ }
31
+ // Web PubSub — one per channel unit
32
+ const webPubSub = [];
33
+ for (const unit of manifest.units) {
34
+ if (unit.role === 'channel') {
35
+ webPubSub.push({ unit: unit.name });
36
+ }
37
+ }
38
+ // Per-unit manifest
39
+ const units = {};
40
+ for (const unit of manifest.units) {
41
+ const capabilities = unit.services.map((s) => s.capability);
42
+ const routes = [];
43
+ for (const handler of unit.handlers) {
44
+ if (handler.type === 'fetch') {
45
+ for (const r of handler.routes) {
46
+ routes.push({ method: r.method, route: r.route });
47
+ }
48
+ }
49
+ }
50
+ const handlerTypes = [...new Set(unit.handlers.map((h) => h.type))];
51
+ units[unit.name] = {
52
+ role: unit.role,
53
+ handlerTypes,
54
+ routes,
55
+ capabilities,
56
+ dependsOn: unit.dependsOn,
57
+ };
58
+ }
59
+ return {
60
+ projectId,
61
+ resources: {
62
+ storageQueues,
63
+ blobContainers,
64
+ timerTriggers,
65
+ webPubSub,
66
+ },
67
+ secrets: manifest.secrets.map((s) => ({
68
+ id: s.secretId,
69
+ displayName: s.displayName,
70
+ description: s.description,
71
+ })),
72
+ variables: manifest.variables.map((v) => ({
73
+ id: v.variableId,
74
+ displayName: v.displayName,
75
+ description: v.description,
76
+ })),
77
+ units,
78
+ };
79
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Azure Functions specific types for the deploy adapter.
3
+ */
4
+ export interface AzureInfraManifest {
5
+ projectId: string;
6
+ resources: {
7
+ storageQueues: AzureQueueResource[];
8
+ blobContainers: AzureBlobResource[];
9
+ timerTriggers: AzureTimerResource[];
10
+ webPubSub: AzureWebPubSubResource[];
11
+ };
12
+ secrets: Array<{
13
+ id: string;
14
+ displayName: string;
15
+ description?: string;
16
+ }>;
17
+ variables: Array<{
18
+ id: string;
19
+ displayName: string;
20
+ description?: string;
21
+ }>;
22
+ units: Record<string, AzureUnitManifest>;
23
+ }
24
+ export interface AzureQueueResource {
25
+ name: string;
26
+ consumerUnit: string;
27
+ }
28
+ export interface AzureBlobResource {
29
+ name: string;
30
+ binding: string;
31
+ }
32
+ export interface AzureTimerResource {
33
+ unit: string;
34
+ schedule: string;
35
+ name: string;
36
+ }
37
+ export interface AzureWebPubSubResource {
38
+ unit: string;
39
+ }
40
+ export interface AzureUnitManifest {
41
+ role: string;
42
+ handlerTypes: string[];
43
+ routes: Array<{
44
+ method: string;
45
+ route: string;
46
+ }>;
47
+ capabilities: string[];
48
+ dependsOn: string[];
49
+ }
package/dist/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Azure Functions specific types for the deploy adapter.
3
+ */
4
+ export {};
package/package.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@pikku/deploy-azure",
3
+ "version": "0.12.0",
4
+ "type": "module",
5
+ "private": false,
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc -b",
9
+ "tsc": "tsc --noEmit"
10
+ },
11
+ "devDependencies": {
12
+ "typescript": "^5.9"
13
+ }
14
+ }