opennextjs-azure 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +194 -0
  3. package/dist/adapters/converters/azure-http.d.mts +22 -0
  4. package/dist/adapters/converters/azure-http.d.ts +22 -0
  5. package/dist/adapters/converters/azure-http.js +97 -0
  6. package/dist/adapters/wrappers/azure-functions.d.mts +10 -0
  7. package/dist/adapters/wrappers/azure-functions.d.ts +10 -0
  8. package/dist/adapters/wrappers/azure-functions.js +102 -0
  9. package/dist/cli/index.d.mts +2 -0
  10. package/dist/cli/index.d.ts +2 -0
  11. package/dist/cli/index.js +67 -0
  12. package/dist/config/index.d.mts +3 -0
  13. package/dist/config/index.d.ts +3 -0
  14. package/dist/config/index.js +68 -0
  15. package/dist/deploy.js +835 -0
  16. package/dist/index.d.mts +35 -0
  17. package/dist/index.d.ts +35 -0
  18. package/dist/index.js +20 -0
  19. package/dist/infrastructure/main.bicep +241 -0
  20. package/dist/overrides/incrementalCache/azure-blob.d.mts +23 -0
  21. package/dist/overrides/incrementalCache/azure-blob.d.ts +23 -0
  22. package/dist/overrides/incrementalCache/azure-blob.js +89 -0
  23. package/dist/overrides/queue/azure-queue.d.mts +19 -0
  24. package/dist/overrides/queue/azure-queue.d.ts +19 -0
  25. package/dist/overrides/queue/azure-queue.js +39 -0
  26. package/dist/overrides/tagCache/azure-table.d.mts +26 -0
  27. package/dist/overrides/tagCache/azure-table.d.ts +26 -0
  28. package/dist/overrides/tagCache/azure-table.js +104 -0
  29. package/dist/shared/opennextjs-azure.d619537c.d.mts +61 -0
  30. package/dist/shared/opennextjs-azure.d619537c.d.ts +61 -0
  31. package/infrastructure/main.bicep +241 -0
  32. package/package.json +99 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Zach Grimaldi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # OpenNext.js Azure
2
+
3
+ **True serverless Next.js on Azure Functions**
4
+
5
+ [![NPM Version](https://img.shields.io/npm/v/opennextjs-azure)](https://www.npmjs.com/package/opennextjs-azure)
6
+ [![NPM Downloads](https://img.shields.io/npm/dt/opennextjs-azure)](https://www.npmjs.com/package/opennextjs-azure)
7
+ [![License: MIT](https://img.shields.io/npm/l/opennextjs-azure)](https://opensource.org/licenses/MIT)
8
+
9
+ Built on the [OpenNext](https://opennext.js.org) framework, this adapter brings native Next.js support to Azure Functions.
10
+
11
+ > **🚀 New to Azure deployment?** Jump to [Quick Start](#quick-start) and run `npx opennextjs-azure@latest init --scaffold` to create a fully configured Next.js app on Azure in seconds!
12
+
13
+ ## The Gap This Project Fills
14
+
15
+ Tutorials exist for static Next.js on Static Web Apps, standalone builds on App Service, and Docker-based deployments, but nothing for **true serverless Next.js on Azure Functions** with ISR, streaming SSR, and on-demand revalidation—until now.
16
+
17
+ Azure Functions is Microsoft's serverless compute platform—comparable to AWS Lambda and Cloudflare Workers, both of which already have OpenNext adapters. This project bridges that gap, bringing the same Vercel-grade developer experience to Azure: one command deploys your Next.js app with all the infrastructure you need.
18
+
19
+ ## Next.js Features → Azure Services
20
+
21
+ | Next.js Feature | Azure Implementation |
22
+ | -------------------------------------- | --------------------------------------------------------------- |
23
+ | Incremental Static Regeneration | Azure Blob Storage |
24
+ | Streaming SSR | Azure Functions with Node.js streams |
25
+ | `revalidateTag()` / `revalidatePath()` | Azure Table Storage + Queue Storage |
26
+ | Fetch caching | Azure Blob Storage with build ID namespacing |
27
+ | Monitoring & Logging | Azure Application Insights (optional, enabled by default) |
28
+ | Infrastructure | Azure Bicep templates upsert infrastructure in a resource group |
29
+
30
+ ## Quick Start
31
+
32
+ ### ⚡ Recommended: Create New Project with Scaffold
33
+
34
+ **The fastest way to get started** is using our scaffold command, which wraps `create-next-app` and sets up everything for Azure deployment:
35
+
36
+ ```bash
37
+ npx opennextjs-azure@latest init --scaffold
38
+ ```
39
+
40
+ This single command:
41
+
42
+ 1. Creates a new Next.js 15 app (TypeScript, App Router, Tailwind, ESLint)
43
+ 2. Adds Azure deployment dependencies (`opennextjs-azure`, `esbuild`)
44
+ 3. Configures `next.config.ts` with `output: "standalone"`
45
+ 4. Creates `open-next.config.ts` with Azure adapters
46
+ 5. Generates `infrastructure/main.bicep` for Azure resources
47
+ 6. Sets up `azure.config.json` for deployment configuration
48
+
49
+ Then build and deploy:
50
+
51
+ ```bash
52
+ npx opennextjs-azure build
53
+ npx opennextjs-azure deploy
54
+ ```
55
+
56
+ **đź’ˇ See it in action:** The [`examples/basic-app`](./examples/basic-app) directory (live at https://opennext-basic-app-func-dev.azurewebsites.net) was created using `init --scaffold` with zero manual configuration!
57
+
58
+ ### 📦 Adding to Existing Project
59
+
60
+ If you have an existing Next.js project:
61
+
62
+ ```bash
63
+ # Initialize Azure infrastructure
64
+ npx opennextjs-azure init
65
+
66
+ # Build for Azure
67
+ npx opennextjs-azure build
68
+
69
+ # Deploy (provisions infrastructure + deploys app)
70
+ npx opennextjs-azure deploy
71
+
72
+ # View live logs in Azure Portal
73
+ npx opennextjs-azure tail
74
+ ```
75
+
76
+ ## Intelligent Preflight Checks
77
+
78
+ Before deployment, the CLI validates your Azure environment to prevent failed deployments:
79
+
80
+ âś“ **Azure CLI** installation and login status
81
+ âś“ **Subscription** permissions and state
82
+ âś“ **Region** availability
83
+ âś“ **Resource providers** (auto-registers Microsoft.Quota, Microsoft.Storage, Microsoft.AlertsManagement, etc.)
84
+ âś“ **Quota availability** for your target SKU
85
+ âś“ **Build output** structure
86
+
87
+ **Smart quota handling:** If you request `--environment prod` but don't have EP1 Premium quota, the CLI suggests `--environment dev` instead. Zero failed deployments from quota issues.
88
+
89
+ ## Architecture
90
+
91
+ ```
92
+ Next.js Request
93
+ ↓
94
+ Azure Functions HTTP Trigger
95
+ ↓
96
+ Azure HTTP Converter (request → InternalEvent)
97
+ ↓
98
+ Azure Functions Wrapper (handles streaming)
99
+ ↓
100
+ Next.js Server (OpenNext)
101
+ ↓
102
+ ISR Cache Check → Azure Blob Storage
103
+ Tag Check → Azure Table Storage
104
+ Revalidation → Azure Queue Storage
105
+ ↓
106
+ Response Stream → Azure Functions Response
107
+ ```
108
+
109
+ ## One Command, Full Infrastructure
110
+
111
+ `opennextjs-azure deploy` provisions everything via Bicep:
112
+
113
+ - Function App (with streaming support)
114
+ - Storage Account (blob containers, tables, queues)
115
+ - App Service Plan (Y1 Consumption or EP1 Premium)
116
+ - Application Insights (optional, for monitoring and logging)
117
+ - CORS configuration
118
+ - Environment variables
119
+ - Connection strings
120
+
121
+ Choose your environment:
122
+
123
+ - `--environment dev` → Y1 Consumption (pay-per-execution)
124
+ - `--environment staging` → EP1 Premium (always-ready instances)
125
+ - `--environment prod` → EP1 Premium (production-grade)
126
+
127
+ ## How It Works
128
+
129
+ **Protocol Adapters:**
130
+ Converts between Azure Functions HTTP triggers and Next.js InternalEvent/InternalResult format with full streaming support.
131
+
132
+ **ISR Implementation:**
133
+
134
+ - **Incremental Cache:** Azure Blob Storage stores rendered pages with `[buildId]/[key].cache` structure
135
+ - **Tag Cache:** Azure Table Storage maps tags → paths for `revalidateTag()`
136
+ - **Revalidation Queue:** Azure Queue Storage triggers on-demand regeneration
137
+
138
+ **Build Process:**
139
+ Uses OpenNext's AWS build with Azure-specific overrides, then adds Azure Functions metadata (`host.json`, `function.json`) for v3 programming model.
140
+
141
+ ## CLI Commands
142
+
143
+ ### `init --scaffold` (Recommended for new projects)
144
+
145
+ Creates a complete Next.js + Azure setup in one command:
146
+
147
+ ```bash
148
+ npx opennextjs-azure@latest init --scaffold
149
+ ```
150
+
151
+ This wraps `create-next-app` with Azure-specific setup. Supports all create-next-app options:
152
+
153
+ ```bash
154
+ # Customize the scaffold
155
+ opennextjs-azure init --scaffold \
156
+ [--no-typescript] \
157
+ [--no-tailwind] \
158
+ [--no-eslint] \
159
+ [--no-src-dir] \
160
+ [--no-app-router] \
161
+ [--import-alias <alias>] \
162
+ [--package-manager npm|yarn|pnpm|bun]
163
+ ```
164
+
165
+ ### Other Commands
166
+
167
+ ```bash
168
+ # Initialize Azure infrastructure in existing project
169
+ opennextjs-azure init
170
+
171
+ # Build Next.js app for Azure
172
+ opennextjs-azure build [-c <config-path>]
173
+
174
+ # Deploy to Azure
175
+ opennextjs-azure deploy \
176
+ [--app-name <name>] \
177
+ [--resource-group <name>] \
178
+ [--location <region>] \
179
+ [--environment dev|staging|prod] \
180
+ [--skip-infrastructure]
181
+
182
+ # View live logs in Azure Portal
183
+ opennextjs-azure tail \
184
+ [--app-name <name>] \
185
+ [--resource-group <name>]
186
+ ```
187
+
188
+ ## License
189
+
190
+ [MIT](./LICENSE)
191
+
192
+ ## Contributing
193
+
194
+ Contributions are welcome! Whether it's bug fixes, feature additions, or documentation improvements, we appreciate your help in making this project better. For major changes or new features, please open an issue first to discuss what you would like to change.
@@ -0,0 +1,22 @@
1
+ import { HttpRequest } from '@azure/functions';
2
+ import { InternalEvent, InternalResult } from '@opennextjs/aws/types/open-next.js';
3
+
4
+ /**
5
+ * Converts Azure HTTP requests to OpenNext InternalEvent format
6
+ */
7
+ declare function convertFromAzureHttp(request: HttpRequest): Promise<InternalEvent>;
8
+ /**
9
+ * Converts OpenNext InternalResult to Azure HTTP response
10
+ */
11
+ declare function convertToAzureHttp(result: InternalResult): Promise<{
12
+ status: number;
13
+ headers: Record<string, string>;
14
+ body?: string;
15
+ }>;
16
+ declare const _default: {
17
+ convertFrom: typeof convertFromAzureHttp;
18
+ convertTo: typeof convertToAzureHttp;
19
+ name: string;
20
+ };
21
+
22
+ export { _default as default };
@@ -0,0 +1,22 @@
1
+ import { HttpRequest } from '@azure/functions';
2
+ import { InternalEvent, InternalResult } from '@opennextjs/aws/types/open-next.js';
3
+
4
+ /**
5
+ * Converts Azure HTTP requests to OpenNext InternalEvent format
6
+ */
7
+ declare function convertFromAzureHttp(request: HttpRequest): Promise<InternalEvent>;
8
+ /**
9
+ * Converts OpenNext InternalResult to Azure HTTP response
10
+ */
11
+ declare function convertToAzureHttp(result: InternalResult): Promise<{
12
+ status: number;
13
+ headers: Record<string, string>;
14
+ body?: string;
15
+ }>;
16
+ declare const _default: {
17
+ convertFrom: typeof convertFromAzureHttp;
18
+ convertTo: typeof convertToAzureHttp;
19
+ name: string;
20
+ };
21
+
22
+ export { _default as default };
@@ -0,0 +1,97 @@
1
+ import { Buffer } from 'node:buffer';
2
+
3
+ async function convertFromAzureHttp(request) {
4
+ const url = new URL(request.url);
5
+ let pathname = url.pathname;
6
+ pathname = normalizePath(pathname);
7
+ const query = {};
8
+ url.searchParams.forEach((value, key) => {
9
+ const existing = query[key];
10
+ if (existing) {
11
+ query[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
12
+ } else {
13
+ query[key] = value;
14
+ }
15
+ });
16
+ const headers = {};
17
+ for (const [key, value] of Object.entries(request.headers)) {
18
+ if (value) {
19
+ headers[key.toLowerCase()] = value;
20
+ }
21
+ }
22
+ const cookies = {};
23
+ const cookieHeader = headers.cookie;
24
+ if (cookieHeader) {
25
+ cookieHeader.split(";").forEach((cookie) => {
26
+ const [key, ...valueParts] = cookie.trim().split("=");
27
+ if (key) {
28
+ cookies[key] = valueParts.join("=");
29
+ }
30
+ });
31
+ }
32
+ const body = request.method !== "GET" && request.method !== "HEAD" ? Buffer.from(await request.arrayBuffer()) : void 0;
33
+ return {
34
+ type: "core",
35
+ method: request.method,
36
+ rawPath: pathname,
37
+ url: request.url,
38
+ body,
39
+ headers,
40
+ query,
41
+ cookies,
42
+ remoteAddress: headers["x-forwarded-for"] || headers["x-real-ip"] || "::1"
43
+ };
44
+ }
45
+ function normalizePath(pathname) {
46
+ if (!pathname || pathname === "/" || pathname === "") {
47
+ return "/";
48
+ }
49
+ if (!pathname.startsWith("/")) {
50
+ pathname = "/" + pathname;
51
+ }
52
+ return pathname;
53
+ }
54
+ async function convertToAzureHttp(result) {
55
+ const headers = {};
56
+ for (const [key, value] of Object.entries(result.headers)) {
57
+ if (value === null || value === void 0) {
58
+ continue;
59
+ }
60
+ if (Array.isArray(value)) {
61
+ headers[key] = value.join(", ");
62
+ } else {
63
+ headers[key] = String(value);
64
+ }
65
+ }
66
+ let body;
67
+ if (result.body) {
68
+ const chunks = [];
69
+ const reader = result.body.getReader();
70
+ try {
71
+ let done = false;
72
+ while (!done) {
73
+ const result2 = await reader.read();
74
+ done = result2.done;
75
+ if (result2.value) {
76
+ chunks.push(result2.value);
77
+ }
78
+ }
79
+ } finally {
80
+ reader.releaseLock();
81
+ }
82
+ const buffer = Buffer.concat(chunks);
83
+ body = result.isBase64Encoded ? buffer.toString("base64") : buffer.toString("utf8");
84
+ }
85
+ return {
86
+ status: result.statusCode,
87
+ headers,
88
+ body
89
+ };
90
+ }
91
+ const azureHttp = {
92
+ convertFrom: convertFromAzureHttp,
93
+ convertTo: convertToAzureHttp,
94
+ name: "azure-http"
95
+ };
96
+
97
+ export { azureHttp as default };
@@ -0,0 +1,10 @@
1
+ import { InternalEvent, InternalResult } from '@opennextjs/aws/types/open-next.js';
2
+ import { WrapperHandler } from '@opennextjs/aws/types/overrides.js';
3
+
4
+ declare const _default: {
5
+ wrapper: WrapperHandler<InternalEvent, InternalResult>;
6
+ name: string;
7
+ supportStreaming: true;
8
+ };
9
+
10
+ export { _default as default };
@@ -0,0 +1,10 @@
1
+ import { InternalEvent, InternalResult } from '@opennextjs/aws/types/open-next.js';
2
+ import { WrapperHandler } from '@opennextjs/aws/types/overrides.js';
3
+
4
+ declare const _default: {
5
+ wrapper: WrapperHandler<InternalEvent, InternalResult>;
6
+ name: string;
7
+ supportStreaming: true;
8
+ };
9
+
10
+ export { _default as default };
@@ -0,0 +1,102 @@
1
+ import { Writable } from 'node:stream';
2
+
3
+ const NULL_BODY_STATUSES = /* @__PURE__ */ new Set([101, 204, 205, 304]);
4
+ const STATIC_ASSET_PATTERNS = [
5
+ /^\/_next\/static\//,
6
+ /^\/_next\/data\//,
7
+ /^\/favicon\.ico$/,
8
+ /^\/robots\.txt$/,
9
+ /^\/sitemap\.xml$/,
10
+ /^\/[^\/]+\.(svg|png|jpg|jpeg|gif|webp|ico|woff|woff2|ttf|eot)$/
11
+ ];
12
+ function isStaticAssetRequest(pathname) {
13
+ return STATIC_ASSET_PATTERNS.some((pattern) => pattern.test(pathname));
14
+ }
15
+ const handler = async (handler2, converter) => async (context, request) => {
16
+ try {
17
+ const internalEvent = await converter.convertFrom(request);
18
+ if (isStaticAssetRequest(internalEvent.rawPath)) {
19
+ const blobUrl = `https://${process.env.AZURE_STORAGE_ACCOUNT_NAME}.blob.core.windows.net/assets${internalEvent.rawPath}`;
20
+ context.res = {
21
+ status: 301,
22
+ headers: {
23
+ Location: blobUrl,
24
+ "Cache-Control": "public, max-age=31536000, immutable"
25
+ }
26
+ };
27
+ return;
28
+ }
29
+ let streamFinished = null;
30
+ let resolveStream = null;
31
+ const streamCreator = {
32
+ writeHeaders(prelude) {
33
+ const { statusCode, cookies, headers } = prelude;
34
+ const responseHeaders = { ...headers };
35
+ if (cookies.length > 0) {
36
+ responseHeaders["set-cookie"] = cookies.join(", ");
37
+ }
38
+ if (NULL_BODY_STATUSES.has(statusCode)) {
39
+ context.res = {
40
+ status: statusCode,
41
+ headers: responseHeaders
42
+ };
43
+ return new Writable({
44
+ write(chunk, encoding, callback) {
45
+ callback();
46
+ }
47
+ });
48
+ }
49
+ const chunks = [];
50
+ streamFinished = new Promise((resolve) => {
51
+ resolveStream = resolve;
52
+ });
53
+ return new Writable({
54
+ write(chunk, encoding, callback) {
55
+ chunks.push(chunk);
56
+ callback();
57
+ },
58
+ final(callback) {
59
+ const body = Buffer.concat(chunks);
60
+ const bodyString = body.toString("utf8");
61
+ context.res = {
62
+ status: statusCode,
63
+ headers: responseHeaders,
64
+ body: bodyString
65
+ };
66
+ callback();
67
+ resolveStream?.();
68
+ }
69
+ });
70
+ },
71
+ retainChunks: true
72
+ };
73
+ await handler2(internalEvent, { streamCreator });
74
+ if (streamFinished) {
75
+ await streamFinished;
76
+ }
77
+ if (!context.res) {
78
+ context.res = {
79
+ status: 200,
80
+ headers: { "content-type": "text/html" },
81
+ body: ""
82
+ };
83
+ }
84
+ } catch (error) {
85
+ context.res = {
86
+ status: 500,
87
+ headers: { "content-type": "application/json" },
88
+ body: JSON.stringify({
89
+ error: "Internal Server Error",
90
+ message: error instanceof Error ? error.message : String(error),
91
+ stack: error instanceof Error ? error.stack : void 0
92
+ })
93
+ };
94
+ }
95
+ };
96
+ const azureFunctions = {
97
+ wrapper: handler,
98
+ name: "azure-functions",
99
+ supportStreaming: true
100
+ };
101
+
102
+ export { azureFunctions as default };
@@ -0,0 +1,2 @@
1
+
2
+ export { };
@@ -0,0 +1,2 @@
1
+
2
+ export { };
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { i as init, b as build, d as deploy } from '../deploy.js';
4
+ import { exec } from 'node:child_process';
5
+ import { promisify } from 'node:util';
6
+ import fs from 'node:fs/promises';
7
+ import path from 'node:path';
8
+ import 'node:url';
9
+ import '@opennextjs/aws/build.js';
10
+ import 'node:fs';
11
+ import 'node:readline';
12
+
13
+ const execAsync = promisify(exec);
14
+ async function tail(options) {
15
+ const cwd = process.cwd();
16
+ const configPath = path.join(cwd, "azure.config.json");
17
+ let config = {};
18
+ try {
19
+ const configContent = await fs.readFile(configPath, "utf-8");
20
+ config = JSON.parse(configContent);
21
+ } catch {
22
+ console.warn("\u26A0\uFE0F azure.config.json not found.");
23
+ console.warn(" Provide --app-name and --resource-group or run from a project directory.\n");
24
+ }
25
+ const appName = options?.appName || config.appName;
26
+ const resourceGroup = options?.resourceGroup || config.resourceGroup;
27
+ const environment = config.environment || "dev";
28
+ if (!appName || !resourceGroup) {
29
+ console.error("\u274C Missing required information!");
30
+ console.error(" Provide --app-name and --resource-group or run from a project with azure.config.json\n");
31
+ process.exit(1);
32
+ }
33
+ const functionAppName = `${appName}-func-${environment}`;
34
+ console.log(`\u{1F4E1} Opening Azure Portal Log Stream for ${functionAppName}...
35
+ `);
36
+ try {
37
+ const { stdout } = await execAsync("az account show --query '{tenant:tenantId, subscription:id}' -o json");
38
+ const account = JSON.parse(stdout);
39
+ const portalUrl = `https://portal.azure.com/#@${account.tenant}/resource/subscriptions/${account.subscription}/resourceGroups/${resourceGroup}/providers/Microsoft.Web/sites/${functionAppName}/logStream`;
40
+ console.log(`\u{1F310} Opening: ${portalUrl}
41
+ `);
42
+ const openCommand = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
43
+ await execAsync(`${openCommand} "${portalUrl}"`);
44
+ console.log("\u2713 Log Stream page opened in your browser!");
45
+ console.log("\nTip: Enable Application Insights in azure.config.json for better log filtering.\n");
46
+ } catch (error) {
47
+ console.error(`
48
+ \u274C Failed to open log stream: ${error.message}`);
49
+ process.exit(1);
50
+ }
51
+ }
52
+
53
+ const program = new Command();
54
+ program.name("opennextjs-azure").description("CLI tool for building and deploying Next.js apps to Azure").version("0.1.0");
55
+ program.command("init").description("Initialize Azure infrastructure in your project").option("--scaffold", "Scaffold a new Next.js project if in empty directory").option("--no-typescript", "Disable TypeScript (default: enabled)").option("--no-tailwind", "Disable Tailwind CSS (default: enabled with v3)").option("--no-eslint", "Disable ESLint (default: enabled)").option("--no-src-dir", "Disable src/ directory (default: enabled)").option("--no-app-router", "Use Pages Router instead of App Router").option("--import-alias <alias>", "Import alias (default: @/*)").option("--package-manager <pm>", "Package manager: npm, yarn, pnpm, bun (default: pnpm)").action(async (options) => {
56
+ await init(options);
57
+ });
58
+ program.command("build").description("Build Next.js app for Azure deployment").option("-c, --config <path>", "Path to open-next.config.ts file").action(async (options) => {
59
+ await build(options.config);
60
+ });
61
+ program.command("deploy").description("Deploy Next.js app to Azure (provisions infrastructure + deploys)").option("-n, --app-name <name>", "Application name (overrides azure.config.json)").option("-g, --resource-group <name>", "Azure resource group name").option("-l, --location <location>", "Azure region").option("-e, --environment <env>", "Environment: dev, staging, or prod").option("--skip-infrastructure", "Skip infrastructure provisioning").action(async (options) => {
62
+ await deploy(options);
63
+ });
64
+ program.command("tail").description("Open Azure Portal Log Stream in browser (live logs)").option("-n, --app-name <name>", "Application name").option("-g, --resource-group <name>", "Azure resource group name").action(async (options) => {
65
+ await tail(options);
66
+ });
67
+ program.parse();
@@ -0,0 +1,3 @@
1
+ import '@opennextjs/aws/types/open-next.js';
2
+ export { d as defineAzureConfig, g as getAzureConfig } from '../shared/opennextjs-azure.d619537c.mjs';
3
+ import '@opennextjs/aws/types/overrides.js';
@@ -0,0 +1,3 @@
1
+ import '@opennextjs/aws/types/open-next.js';
2
+ export { d as defineAzureConfig, g as getAzureConfig } from '../shared/opennextjs-azure.d619537c.js';
3
+ import '@opennextjs/aws/types/overrides.js';
@@ -0,0 +1,68 @@
1
+ function defineAzureConfig(config = {}) {
2
+ return {
3
+ default: {
4
+ override: {
5
+ wrapper: () => import('../adapters/wrappers/azure-functions.js').then((m) => m.default),
6
+ converter: () => import('../adapters/converters/azure-http.js').then((m) => m.default),
7
+ incrementalCache: resolveIncremental(config.incrementalCache),
8
+ tagCache: resolveTag(config.tagCache),
9
+ queue: resolveQueue(config.queue),
10
+ proxyExternalRequest: "fetch"
11
+ },
12
+ routePreloadingBehavior: config.routePreloadingBehavior || "none"
13
+ },
14
+ middleware: config.middleware || {
15
+ external: false
16
+ },
17
+ dangerous: config.dangerous,
18
+ buildCommand: config.buildCommand,
19
+ buildOutputPath: config.buildOutputPath || ".",
20
+ appPath: config.appPath || ".",
21
+ packageJsonPath: config.packageJsonPath
22
+ };
23
+ }
24
+ function resolveIncremental(value) {
25
+ if (!value || value === "azure-blob") {
26
+ return () => import('../overrides/incrementalCache/azure-blob.js').then((m) => new m.default());
27
+ }
28
+ if (typeof value === "function") {
29
+ return value;
30
+ }
31
+ return () => value;
32
+ }
33
+ function resolveTag(value) {
34
+ if (!value || value === "azure-table") {
35
+ return () => import('../overrides/tagCache/azure-table.js').then((m) => new m.default());
36
+ }
37
+ if (typeof value === "function") {
38
+ return value;
39
+ }
40
+ return () => value;
41
+ }
42
+ function resolveQueue(value) {
43
+ if (!value || value === "azure-queue") {
44
+ return () => import('../overrides/queue/azure-queue.js').then((m) => new m.default());
45
+ }
46
+ if (typeof value === "function") {
47
+ return value;
48
+ }
49
+ return () => value;
50
+ }
51
+ function getAzureConfig() {
52
+ return {
53
+ deployment: {
54
+ target: process.env.AZURE_DEPLOYMENT_TARGET || "functions",
55
+ region: process.env.AZURE_REGION || "eastus"
56
+ },
57
+ storage: {
58
+ connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING,
59
+ accountName: process.env.AZURE_STORAGE_ACCOUNT_NAME,
60
+ accountKey: process.env.AZURE_STORAGE_ACCOUNT_KEY,
61
+ containerName: process.env.AZURE_STORAGE_CONTAINER_NAME || "nextjs-cache",
62
+ tableName: process.env.AZURE_TABLE_NAME || "nextjstags",
63
+ queueName: process.env.AZURE_QUEUE_NAME || "nextjsrevalidation"
64
+ }
65
+ };
66
+ }
67
+
68
+ export { defineAzureConfig, getAzureConfig };