qlara 0.1.1 → 0.1.3
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/aws.cjs +43 -34
- package/dist/aws.d.cts +10 -0
- package/dist/aws.d.ts +10 -0
- package/dist/aws.js +46 -40
- package/dist/cli.js +49 -45
- package/dist/index.js +0 -1
- package/dist/plugin/next.js +0 -1
- package/package.json +3 -2
- package/src/provider/aws/edge-handler.ts +2 -1
- package/src/provider/aws/renderer.ts +25 -19
- package/dist/chunk-3RG5ZIWI.js +0 -10
package/dist/aws.cjs
CHANGED
|
@@ -464,42 +464,30 @@ var import_esbuild = require("esbuild");
|
|
|
464
464
|
var import_node_fs2 = require("fs");
|
|
465
465
|
var import_node_path2 = require("path");
|
|
466
466
|
var import_node_url = require("url");
|
|
467
|
+
var import_node_module = require("module");
|
|
467
468
|
var import_archiver = __toESM(require("archiver"), 1);
|
|
468
469
|
var import_node_stream = require("stream");
|
|
469
470
|
var import_meta = {};
|
|
470
471
|
var BUNDLE_DIR = (0, import_node_path2.join)(".qlara", "bundles");
|
|
471
|
-
function getModuleDir() {
|
|
472
|
-
if (typeof __dirname !== "undefined") {
|
|
473
|
-
return __dirname;
|
|
474
|
-
}
|
|
475
|
-
return (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
476
|
-
}
|
|
477
|
-
function findPackageRoot(startDir) {
|
|
478
|
-
let dir = startDir;
|
|
479
|
-
for (let i = 0; i < 10; i++) {
|
|
480
|
-
const pkgPath = (0, import_node_path2.join)(dir, "package.json");
|
|
481
|
-
if ((0, import_node_fs2.existsSync)(pkgPath)) {
|
|
482
|
-
try {
|
|
483
|
-
const { readFileSync: readFileSync3 } = require("fs");
|
|
484
|
-
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
485
|
-
if (pkg.name === "qlara") return dir;
|
|
486
|
-
} catch {
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
const parent = (0, import_node_path2.dirname)(dir);
|
|
490
|
-
if (parent === dir) break;
|
|
491
|
-
dir = parent;
|
|
492
|
-
}
|
|
493
|
-
return startDir;
|
|
494
|
-
}
|
|
495
472
|
function resolveEntry(name) {
|
|
496
|
-
const
|
|
497
|
-
const sameDirTs = (0, import_node_path2.resolve)(
|
|
473
|
+
const thisDir = (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
474
|
+
const sameDirTs = (0, import_node_path2.resolve)(thisDir, `${name}.ts`);
|
|
498
475
|
if ((0, import_node_fs2.existsSync)(sameDirTs)) return sameDirTs;
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
476
|
+
try {
|
|
477
|
+
const esmRequire = (0, import_node_module.createRequire)(import_meta.url);
|
|
478
|
+
const pkgJsonPath = esmRequire.resolve("qlara/package.json");
|
|
479
|
+
const pkgRoot = (0, import_node_path2.dirname)(pkgJsonPath);
|
|
480
|
+
const srcTs = (0, import_node_path2.join)(pkgRoot, "src", "provider", "aws", `${name}.ts`);
|
|
481
|
+
if ((0, import_node_fs2.existsSync)(srcTs)) return srcTs;
|
|
482
|
+
} catch {
|
|
483
|
+
}
|
|
484
|
+
const fromDist = (0, import_node_path2.resolve)(thisDir, "..", "src", "provider", "aws", `${name}.ts`);
|
|
485
|
+
if ((0, import_node_fs2.existsSync)(fromDist)) return fromDist;
|
|
486
|
+
throw new Error(
|
|
487
|
+
`[qlara] Could not find ${name}.ts Lambda source file. Searched:
|
|
488
|
+
${sameDirTs}
|
|
489
|
+
${fromDist}`
|
|
490
|
+
);
|
|
503
491
|
}
|
|
504
492
|
async function createZip(filePath, entryName) {
|
|
505
493
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -532,14 +520,15 @@ async function bundleEdgeHandler(config) {
|
|
|
532
520
|
define: {
|
|
533
521
|
__QLARA_BUCKET_NAME__: JSON.stringify(config.bucketName),
|
|
534
522
|
__QLARA_RENDERER_ARN__: JSON.stringify(config.rendererArn),
|
|
535
|
-
__QLARA_REGION__: JSON.stringify(config.region)
|
|
523
|
+
__QLARA_REGION__: JSON.stringify(config.region),
|
|
524
|
+
__QLARA_CACHE_TTL__: String(config.cacheTtl)
|
|
536
525
|
},
|
|
537
526
|
// Bundle everything — Lambda@Edge must be self-contained
|
|
538
527
|
external: []
|
|
539
528
|
});
|
|
540
529
|
return createZip(outfile, "edge-handler.js");
|
|
541
530
|
}
|
|
542
|
-
async function bundleRenderer(routeFile) {
|
|
531
|
+
async function bundleRenderer(routeFile, cacheTtl = 3600) {
|
|
543
532
|
(0, import_node_fs2.mkdirSync)(BUNDLE_DIR, { recursive: true });
|
|
544
533
|
const outfile = (0, import_node_path2.join)(BUNDLE_DIR, "renderer.js");
|
|
545
534
|
const alias = {};
|
|
@@ -560,6 +549,9 @@ async function bundleRenderer(routeFile) {
|
|
|
560
549
|
outfile,
|
|
561
550
|
minify: true,
|
|
562
551
|
alias,
|
|
552
|
+
define: {
|
|
553
|
+
__QLARA_CACHE_TTL__: String(cacheTtl)
|
|
554
|
+
},
|
|
563
555
|
external: []
|
|
564
556
|
});
|
|
565
557
|
return createZip(outfile, "renderer.js");
|
|
@@ -776,6 +768,7 @@ function byoiResources(config) {
|
|
|
776
768
|
function aws(awsConfig = {}) {
|
|
777
769
|
const region = "us-east-1";
|
|
778
770
|
const byoi = isByoi(awsConfig);
|
|
771
|
+
const cacheTtl = awsConfig.cacheTtl ?? 3600;
|
|
779
772
|
const stackName = byoi ? "" : awsConfig.stackName || STACK_NAME_PREFIX;
|
|
780
773
|
return {
|
|
781
774
|
name: "aws",
|
|
@@ -857,7 +850,8 @@ function aws(awsConfig = {}) {
|
|
|
857
850
|
const edgeZip = await bundleEdgeHandler({
|
|
858
851
|
bucketName: res.bucketName,
|
|
859
852
|
rendererArn: res.rendererFunctionArn,
|
|
860
|
-
region: res.region
|
|
853
|
+
region: res.region,
|
|
854
|
+
cacheTtl
|
|
861
855
|
});
|
|
862
856
|
const lambda = new import_client_lambda.LambdaClient({ region: res.region });
|
|
863
857
|
console.log("[qlara/aws] Waiting for edge handler to be ready...");
|
|
@@ -934,7 +928,7 @@ function aws(awsConfig = {}) {
|
|
|
934
928
|
const cf = new import_client_cloudfront.CloudFrontClient({ region: res.region });
|
|
935
929
|
await updateCloudFrontEdgeVersion(cf, res.distributionId, newVersionArn);
|
|
936
930
|
console.log("[qlara/aws] Bundling renderer...");
|
|
937
|
-
const rendererZip = await bundleRenderer(config.routeFile);
|
|
931
|
+
const rendererZip = await bundleRenderer(config.routeFile, cacheTtl);
|
|
938
932
|
await (0, import_client_lambda.waitUntilFunctionUpdatedV2)(
|
|
939
933
|
{ client: lambda, maxWaitTime: 120 },
|
|
940
934
|
{ FunctionName: res.rendererFunctionArn }
|
|
@@ -1009,6 +1003,21 @@ function aws(awsConfig = {}) {
|
|
|
1009
1003
|
`[qlara/aws] Could not update renderer permissions: ${err.message}`
|
|
1010
1004
|
);
|
|
1011
1005
|
}
|
|
1006
|
+
console.log("[qlara/aws] Warming up renderer...");
|
|
1007
|
+
try {
|
|
1008
|
+
await lambda.send(
|
|
1009
|
+
new import_client_lambda.InvokeCommand({
|
|
1010
|
+
FunctionName: res.rendererFunctionArn,
|
|
1011
|
+
InvocationType: "RequestResponse",
|
|
1012
|
+
Payload: JSON.stringify({ warmup: true })
|
|
1013
|
+
})
|
|
1014
|
+
);
|
|
1015
|
+
console.log("[qlara/aws] Renderer warmed up");
|
|
1016
|
+
} catch (err) {
|
|
1017
|
+
console.warn(
|
|
1018
|
+
`[qlara/aws] Renderer warm-up failed (non-critical): ${err.message}`
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1012
1021
|
console.log("[qlara/aws] Invalidating CloudFront cache...");
|
|
1013
1022
|
await cf.send(
|
|
1014
1023
|
new import_client_cloudfront.CreateInvalidationCommand({
|
package/dist/aws.d.cts
CHANGED
|
@@ -3,6 +3,16 @@ import { P as ProviderResources, Q as QlaraProvider } from './types-gl2xFqEX.cjs
|
|
|
3
3
|
interface AwsConfig {
|
|
4
4
|
stackName?: string;
|
|
5
5
|
bucketName?: string;
|
|
6
|
+
/**
|
|
7
|
+
* How long (in seconds) CloudFront should cache dynamically rendered pages.
|
|
8
|
+
* This sets the `s-maxage` value on the Cache-Control header.
|
|
9
|
+
*
|
|
10
|
+
* - Browsers always revalidate with CloudFront (`max-age=0`)
|
|
11
|
+
* - CloudFront serves from edge cache for this duration
|
|
12
|
+
*
|
|
13
|
+
* @default 3600 (1 hour)
|
|
14
|
+
*/
|
|
15
|
+
cacheTtl?: number;
|
|
6
16
|
distributionId?: string;
|
|
7
17
|
distributionDomain?: string;
|
|
8
18
|
edgeFunctionArn?: string;
|
package/dist/aws.d.ts
CHANGED
|
@@ -3,6 +3,16 @@ import { P as ProviderResources, Q as QlaraProvider } from './types-gl2xFqEX.js'
|
|
|
3
3
|
interface AwsConfig {
|
|
4
4
|
stackName?: string;
|
|
5
5
|
bucketName?: string;
|
|
6
|
+
/**
|
|
7
|
+
* How long (in seconds) CloudFront should cache dynamically rendered pages.
|
|
8
|
+
* This sets the `s-maxage` value on the Cache-Control header.
|
|
9
|
+
*
|
|
10
|
+
* - Browsers always revalidate with CloudFront (`max-age=0`)
|
|
11
|
+
* - CloudFront serves from edge cache for this duration
|
|
12
|
+
*
|
|
13
|
+
* @default 3600 (1 hour)
|
|
14
|
+
*/
|
|
15
|
+
cacheTtl?: number;
|
|
6
16
|
distributionId?: string;
|
|
7
17
|
distributionDomain?: string;
|
|
8
18
|
edgeFunctionArn?: string;
|
package/dist/aws.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__require
|
|
3
|
-
} from "./chunk-3RG5ZIWI.js";
|
|
4
|
-
|
|
5
1
|
// src/provider/aws/index.ts
|
|
6
2
|
import {
|
|
7
3
|
CloudFormationClient,
|
|
@@ -19,6 +15,7 @@ import {
|
|
|
19
15
|
UpdateFunctionConfigurationCommand,
|
|
20
16
|
PublishVersionCommand,
|
|
21
17
|
AddPermissionCommand,
|
|
18
|
+
InvokeCommand,
|
|
22
19
|
waitUntilFunctionUpdatedV2
|
|
23
20
|
} from "@aws-sdk/client-lambda";
|
|
24
21
|
import {
|
|
@@ -464,41 +461,29 @@ import { build } from "esbuild";
|
|
|
464
461
|
import { mkdirSync, existsSync } from "fs";
|
|
465
462
|
import { resolve, join as join2, dirname } from "path";
|
|
466
463
|
import { fileURLToPath } from "url";
|
|
464
|
+
import { createRequire } from "module";
|
|
467
465
|
import archiver from "archiver";
|
|
468
466
|
import { Writable } from "stream";
|
|
469
467
|
var BUNDLE_DIR = join2(".qlara", "bundles");
|
|
470
|
-
function getModuleDir() {
|
|
471
|
-
if (typeof __dirname !== "undefined") {
|
|
472
|
-
return __dirname;
|
|
473
|
-
}
|
|
474
|
-
return dirname(fileURLToPath(import.meta.url));
|
|
475
|
-
}
|
|
476
|
-
function findPackageRoot(startDir) {
|
|
477
|
-
let dir = startDir;
|
|
478
|
-
for (let i = 0; i < 10; i++) {
|
|
479
|
-
const pkgPath = join2(dir, "package.json");
|
|
480
|
-
if (existsSync(pkgPath)) {
|
|
481
|
-
try {
|
|
482
|
-
const { readFileSync: readFileSync3 } = __require("fs");
|
|
483
|
-
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
484
|
-
if (pkg.name === "qlara") return dir;
|
|
485
|
-
} catch {
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
const parent = dirname(dir);
|
|
489
|
-
if (parent === dir) break;
|
|
490
|
-
dir = parent;
|
|
491
|
-
}
|
|
492
|
-
return startDir;
|
|
493
|
-
}
|
|
494
468
|
function resolveEntry(name) {
|
|
495
|
-
const
|
|
496
|
-
const sameDirTs = resolve(
|
|
469
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
470
|
+
const sameDirTs = resolve(thisDir, `${name}.ts`);
|
|
497
471
|
if (existsSync(sameDirTs)) return sameDirTs;
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
472
|
+
try {
|
|
473
|
+
const esmRequire = createRequire(import.meta.url);
|
|
474
|
+
const pkgJsonPath = esmRequire.resolve("qlara/package.json");
|
|
475
|
+
const pkgRoot = dirname(pkgJsonPath);
|
|
476
|
+
const srcTs = join2(pkgRoot, "src", "provider", "aws", `${name}.ts`);
|
|
477
|
+
if (existsSync(srcTs)) return srcTs;
|
|
478
|
+
} catch {
|
|
479
|
+
}
|
|
480
|
+
const fromDist = resolve(thisDir, "..", "src", "provider", "aws", `${name}.ts`);
|
|
481
|
+
if (existsSync(fromDist)) return fromDist;
|
|
482
|
+
throw new Error(
|
|
483
|
+
`[qlara] Could not find ${name}.ts Lambda source file. Searched:
|
|
484
|
+
${sameDirTs}
|
|
485
|
+
${fromDist}`
|
|
486
|
+
);
|
|
502
487
|
}
|
|
503
488
|
async function createZip(filePath, entryName) {
|
|
504
489
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -531,14 +516,15 @@ async function bundleEdgeHandler(config) {
|
|
|
531
516
|
define: {
|
|
532
517
|
__QLARA_BUCKET_NAME__: JSON.stringify(config.bucketName),
|
|
533
518
|
__QLARA_RENDERER_ARN__: JSON.stringify(config.rendererArn),
|
|
534
|
-
__QLARA_REGION__: JSON.stringify(config.region)
|
|
519
|
+
__QLARA_REGION__: JSON.stringify(config.region),
|
|
520
|
+
__QLARA_CACHE_TTL__: String(config.cacheTtl)
|
|
535
521
|
},
|
|
536
522
|
// Bundle everything — Lambda@Edge must be self-contained
|
|
537
523
|
external: []
|
|
538
524
|
});
|
|
539
525
|
return createZip(outfile, "edge-handler.js");
|
|
540
526
|
}
|
|
541
|
-
async function bundleRenderer(routeFile) {
|
|
527
|
+
async function bundleRenderer(routeFile, cacheTtl = 3600) {
|
|
542
528
|
mkdirSync(BUNDLE_DIR, { recursive: true });
|
|
543
529
|
const outfile = join2(BUNDLE_DIR, "renderer.js");
|
|
544
530
|
const alias = {};
|
|
@@ -559,6 +545,9 @@ async function bundleRenderer(routeFile) {
|
|
|
559
545
|
outfile,
|
|
560
546
|
minify: true,
|
|
561
547
|
alias,
|
|
548
|
+
define: {
|
|
549
|
+
__QLARA_CACHE_TTL__: String(cacheTtl)
|
|
550
|
+
},
|
|
562
551
|
external: []
|
|
563
552
|
});
|
|
564
553
|
return createZip(outfile, "renderer.js");
|
|
@@ -568,7 +557,7 @@ async function bundleRenderer(routeFile) {
|
|
|
568
557
|
var STACK_NAME_PREFIX = "qlara";
|
|
569
558
|
|
|
570
559
|
// src/fallback.ts
|
|
571
|
-
import { readFileSync as
|
|
560
|
+
import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
572
561
|
import { join as join3 } from "path";
|
|
573
562
|
var FALLBACK_FILENAME = "_fallback.html";
|
|
574
563
|
var FALLBACK_PLACEHOLDER = "__QLARA_FALLBACK__";
|
|
@@ -647,7 +636,7 @@ function generateFallbacks(buildDir, routes) {
|
|
|
647
636
|
continue;
|
|
648
637
|
}
|
|
649
638
|
const templatePath = join3(routeDir, files[0]);
|
|
650
|
-
const templateHtml =
|
|
639
|
+
const templateHtml = readFileSync3(templatePath, "utf-8");
|
|
651
640
|
const fallbackHtml = generateFallbackFromTemplate(templateHtml, route.pattern);
|
|
652
641
|
const fallbackPath = join3(routeDir, FALLBACK_FILENAME);
|
|
653
642
|
writeFileSync(fallbackPath, fallbackHtml);
|
|
@@ -775,6 +764,7 @@ function byoiResources(config) {
|
|
|
775
764
|
function aws(awsConfig = {}) {
|
|
776
765
|
const region = "us-east-1";
|
|
777
766
|
const byoi = isByoi(awsConfig);
|
|
767
|
+
const cacheTtl = awsConfig.cacheTtl ?? 3600;
|
|
778
768
|
const stackName = byoi ? "" : awsConfig.stackName || STACK_NAME_PREFIX;
|
|
779
769
|
return {
|
|
780
770
|
name: "aws",
|
|
@@ -856,7 +846,8 @@ function aws(awsConfig = {}) {
|
|
|
856
846
|
const edgeZip = await bundleEdgeHandler({
|
|
857
847
|
bucketName: res.bucketName,
|
|
858
848
|
rendererArn: res.rendererFunctionArn,
|
|
859
|
-
region: res.region
|
|
849
|
+
region: res.region,
|
|
850
|
+
cacheTtl
|
|
860
851
|
});
|
|
861
852
|
const lambda = new LambdaClient({ region: res.region });
|
|
862
853
|
console.log("[qlara/aws] Waiting for edge handler to be ready...");
|
|
@@ -933,7 +924,7 @@ function aws(awsConfig = {}) {
|
|
|
933
924
|
const cf = new CloudFrontClient({ region: res.region });
|
|
934
925
|
await updateCloudFrontEdgeVersion(cf, res.distributionId, newVersionArn);
|
|
935
926
|
console.log("[qlara/aws] Bundling renderer...");
|
|
936
|
-
const rendererZip = await bundleRenderer(config.routeFile);
|
|
927
|
+
const rendererZip = await bundleRenderer(config.routeFile, cacheTtl);
|
|
937
928
|
await waitUntilFunctionUpdatedV2(
|
|
938
929
|
{ client: lambda, maxWaitTime: 120 },
|
|
939
930
|
{ FunctionName: res.rendererFunctionArn }
|
|
@@ -1008,6 +999,21 @@ function aws(awsConfig = {}) {
|
|
|
1008
999
|
`[qlara/aws] Could not update renderer permissions: ${err.message}`
|
|
1009
1000
|
);
|
|
1010
1001
|
}
|
|
1002
|
+
console.log("[qlara/aws] Warming up renderer...");
|
|
1003
|
+
try {
|
|
1004
|
+
await lambda.send(
|
|
1005
|
+
new InvokeCommand({
|
|
1006
|
+
FunctionName: res.rendererFunctionArn,
|
|
1007
|
+
InvocationType: "RequestResponse",
|
|
1008
|
+
Payload: JSON.stringify({ warmup: true })
|
|
1009
|
+
})
|
|
1010
|
+
);
|
|
1011
|
+
console.log("[qlara/aws] Renderer warmed up");
|
|
1012
|
+
} catch (err) {
|
|
1013
|
+
console.warn(
|
|
1014
|
+
`[qlara/aws] Renderer warm-up failed (non-critical): ${err.message}`
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1011
1017
|
console.log("[qlara/aws] Invalidating CloudFront cache...");
|
|
1012
1018
|
await cf.send(
|
|
1013
1019
|
new CreateInvalidationCommand({
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/cli.ts
|
|
10
|
-
import { readFileSync as
|
|
4
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
|
|
11
5
|
import { join as join4 } from "path";
|
|
12
6
|
|
|
13
7
|
// src/provider/aws/constants.ts
|
|
@@ -32,6 +26,7 @@ import {
|
|
|
32
26
|
UpdateFunctionConfigurationCommand,
|
|
33
27
|
PublishVersionCommand,
|
|
34
28
|
AddPermissionCommand,
|
|
29
|
+
InvokeCommand,
|
|
35
30
|
waitUntilFunctionUpdatedV2
|
|
36
31
|
} from "@aws-sdk/client-lambda";
|
|
37
32
|
import {
|
|
@@ -477,41 +472,29 @@ import { build } from "esbuild";
|
|
|
477
472
|
import { mkdirSync, existsSync } from "fs";
|
|
478
473
|
import { resolve, join as join2, dirname } from "path";
|
|
479
474
|
import { fileURLToPath } from "url";
|
|
475
|
+
import { createRequire } from "module";
|
|
480
476
|
import archiver from "archiver";
|
|
481
477
|
import { Writable } from "stream";
|
|
482
478
|
var BUNDLE_DIR = join2(".qlara", "bundles");
|
|
483
|
-
function getModuleDir() {
|
|
484
|
-
if (typeof __dirname !== "undefined") {
|
|
485
|
-
return __dirname;
|
|
486
|
-
}
|
|
487
|
-
return dirname(fileURLToPath(import.meta.url));
|
|
488
|
-
}
|
|
489
|
-
function findPackageRoot(startDir) {
|
|
490
|
-
let dir = startDir;
|
|
491
|
-
for (let i = 0; i < 10; i++) {
|
|
492
|
-
const pkgPath = join2(dir, "package.json");
|
|
493
|
-
if (existsSync(pkgPath)) {
|
|
494
|
-
try {
|
|
495
|
-
const { readFileSync: readFileSync4 } = __require("fs");
|
|
496
|
-
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
497
|
-
if (pkg.name === "qlara") return dir;
|
|
498
|
-
} catch {
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
const parent = dirname(dir);
|
|
502
|
-
if (parent === dir) break;
|
|
503
|
-
dir = parent;
|
|
504
|
-
}
|
|
505
|
-
return startDir;
|
|
506
|
-
}
|
|
507
479
|
function resolveEntry(name) {
|
|
508
|
-
const
|
|
509
|
-
const sameDirTs = resolve(
|
|
480
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
481
|
+
const sameDirTs = resolve(thisDir, `${name}.ts`);
|
|
510
482
|
if (existsSync(sameDirTs)) return sameDirTs;
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
483
|
+
try {
|
|
484
|
+
const esmRequire = createRequire(import.meta.url);
|
|
485
|
+
const pkgJsonPath = esmRequire.resolve("qlara/package.json");
|
|
486
|
+
const pkgRoot = dirname(pkgJsonPath);
|
|
487
|
+
const srcTs = join2(pkgRoot, "src", "provider", "aws", `${name}.ts`);
|
|
488
|
+
if (existsSync(srcTs)) return srcTs;
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
const fromDist = resolve(thisDir, "..", "src", "provider", "aws", `${name}.ts`);
|
|
492
|
+
if (existsSync(fromDist)) return fromDist;
|
|
493
|
+
throw new Error(
|
|
494
|
+
`[qlara] Could not find ${name}.ts Lambda source file. Searched:
|
|
495
|
+
${sameDirTs}
|
|
496
|
+
${fromDist}`
|
|
497
|
+
);
|
|
515
498
|
}
|
|
516
499
|
async function createZip(filePath, entryName) {
|
|
517
500
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -544,14 +527,15 @@ async function bundleEdgeHandler(config) {
|
|
|
544
527
|
define: {
|
|
545
528
|
__QLARA_BUCKET_NAME__: JSON.stringify(config.bucketName),
|
|
546
529
|
__QLARA_RENDERER_ARN__: JSON.stringify(config.rendererArn),
|
|
547
|
-
__QLARA_REGION__: JSON.stringify(config.region)
|
|
530
|
+
__QLARA_REGION__: JSON.stringify(config.region),
|
|
531
|
+
__QLARA_CACHE_TTL__: String(config.cacheTtl)
|
|
548
532
|
},
|
|
549
533
|
// Bundle everything — Lambda@Edge must be self-contained
|
|
550
534
|
external: []
|
|
551
535
|
});
|
|
552
536
|
return createZip(outfile, "edge-handler.js");
|
|
553
537
|
}
|
|
554
|
-
async function bundleRenderer(routeFile) {
|
|
538
|
+
async function bundleRenderer(routeFile, cacheTtl = 3600) {
|
|
555
539
|
mkdirSync(BUNDLE_DIR, { recursive: true });
|
|
556
540
|
const outfile = join2(BUNDLE_DIR, "renderer.js");
|
|
557
541
|
const alias = {};
|
|
@@ -572,13 +556,16 @@ async function bundleRenderer(routeFile) {
|
|
|
572
556
|
outfile,
|
|
573
557
|
minify: true,
|
|
574
558
|
alias,
|
|
559
|
+
define: {
|
|
560
|
+
__QLARA_CACHE_TTL__: String(cacheTtl)
|
|
561
|
+
},
|
|
575
562
|
external: []
|
|
576
563
|
});
|
|
577
564
|
return createZip(outfile, "renderer.js");
|
|
578
565
|
}
|
|
579
566
|
|
|
580
567
|
// src/fallback.ts
|
|
581
|
-
import { readFileSync as
|
|
568
|
+
import { readFileSync as readFileSync3, writeFileSync, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
582
569
|
import { join as join3 } from "path";
|
|
583
570
|
var FALLBACK_FILENAME = "_fallback.html";
|
|
584
571
|
var FALLBACK_PLACEHOLDER = "__QLARA_FALLBACK__";
|
|
@@ -657,7 +644,7 @@ function generateFallbacks(buildDir, routes) {
|
|
|
657
644
|
continue;
|
|
658
645
|
}
|
|
659
646
|
const templatePath = join3(routeDir, files[0]);
|
|
660
|
-
const templateHtml =
|
|
647
|
+
const templateHtml = readFileSync3(templatePath, "utf-8");
|
|
661
648
|
const fallbackHtml = generateFallbackFromTemplate(templateHtml, route.pattern);
|
|
662
649
|
const fallbackPath = join3(routeDir, FALLBACK_FILENAME);
|
|
663
650
|
writeFileSync(fallbackPath, fallbackHtml);
|
|
@@ -785,6 +772,7 @@ function byoiResources(config) {
|
|
|
785
772
|
function aws(awsConfig = {}) {
|
|
786
773
|
const region = "us-east-1";
|
|
787
774
|
const byoi = isByoi(awsConfig);
|
|
775
|
+
const cacheTtl = awsConfig.cacheTtl ?? 3600;
|
|
788
776
|
const stackName = byoi ? "" : awsConfig.stackName || STACK_NAME_PREFIX;
|
|
789
777
|
return {
|
|
790
778
|
name: "aws",
|
|
@@ -866,7 +854,8 @@ function aws(awsConfig = {}) {
|
|
|
866
854
|
const edgeZip = await bundleEdgeHandler({
|
|
867
855
|
bucketName: res.bucketName,
|
|
868
856
|
rendererArn: res.rendererFunctionArn,
|
|
869
|
-
region: res.region
|
|
857
|
+
region: res.region,
|
|
858
|
+
cacheTtl
|
|
870
859
|
});
|
|
871
860
|
const lambda = new LambdaClient({ region: res.region });
|
|
872
861
|
console.log("[qlara/aws] Waiting for edge handler to be ready...");
|
|
@@ -943,7 +932,7 @@ function aws(awsConfig = {}) {
|
|
|
943
932
|
const cf = new CloudFrontClient({ region: res.region });
|
|
944
933
|
await updateCloudFrontEdgeVersion(cf, res.distributionId, newVersionArn);
|
|
945
934
|
console.log("[qlara/aws] Bundling renderer...");
|
|
946
|
-
const rendererZip = await bundleRenderer(config.routeFile);
|
|
935
|
+
const rendererZip = await bundleRenderer(config.routeFile, cacheTtl);
|
|
947
936
|
await waitUntilFunctionUpdatedV2(
|
|
948
937
|
{ client: lambda, maxWaitTime: 120 },
|
|
949
938
|
{ FunctionName: res.rendererFunctionArn }
|
|
@@ -1018,6 +1007,21 @@ function aws(awsConfig = {}) {
|
|
|
1018
1007
|
`[qlara/aws] Could not update renderer permissions: ${err.message}`
|
|
1019
1008
|
);
|
|
1020
1009
|
}
|
|
1010
|
+
console.log("[qlara/aws] Warming up renderer...");
|
|
1011
|
+
try {
|
|
1012
|
+
await lambda.send(
|
|
1013
|
+
new InvokeCommand({
|
|
1014
|
+
FunctionName: res.rendererFunctionArn,
|
|
1015
|
+
InvocationType: "RequestResponse",
|
|
1016
|
+
Payload: JSON.stringify({ warmup: true })
|
|
1017
|
+
})
|
|
1018
|
+
);
|
|
1019
|
+
console.log("[qlara/aws] Renderer warmed up");
|
|
1020
|
+
} catch (err) {
|
|
1021
|
+
console.warn(
|
|
1022
|
+
`[qlara/aws] Renderer warm-up failed (non-critical): ${err.message}`
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1021
1025
|
console.log("[qlara/aws] Invalidating CloudFront cache...");
|
|
1022
1026
|
await cf.send(
|
|
1023
1027
|
new CreateInvalidationCommand({
|
|
@@ -1101,11 +1105,11 @@ function loadConfig() {
|
|
|
1101
1105
|
console.error("[qlara] Run your framework build first (e.g. `next build`)");
|
|
1102
1106
|
process.exit(1);
|
|
1103
1107
|
}
|
|
1104
|
-
return JSON.parse(
|
|
1108
|
+
return JSON.parse(readFileSync4(CONFIG_PATH, "utf-8"));
|
|
1105
1109
|
}
|
|
1106
1110
|
function loadResources() {
|
|
1107
1111
|
if (!existsSync3(RESOURCES_PATH)) return null;
|
|
1108
|
-
return JSON.parse(
|
|
1112
|
+
return JSON.parse(readFileSync4(RESOURCES_PATH, "utf-8"));
|
|
1109
1113
|
}
|
|
1110
1114
|
function saveResources(resources) {
|
|
1111
1115
|
mkdirSync2(QLARA_DIR, { recursive: true });
|
package/dist/index.js
CHANGED
package/dist/plugin/next.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qlara",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Runtime ISR for static React apps — dynamic routing and SEO metadata for statically exported Next.js apps on AWS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -42,7 +42,8 @@
|
|
|
42
42
|
"types": "./dist/aws.d.ts",
|
|
43
43
|
"import": "./dist/aws.js",
|
|
44
44
|
"require": "./dist/aws.cjs"
|
|
45
|
-
}
|
|
45
|
+
},
|
|
46
|
+
"./package.json": "./package.json"
|
|
46
47
|
},
|
|
47
48
|
"files": [
|
|
48
49
|
"dist",
|
|
@@ -24,6 +24,7 @@ import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
|
|
|
24
24
|
declare const __QLARA_BUCKET_NAME__: string;
|
|
25
25
|
declare const __QLARA_RENDERER_ARN__: string;
|
|
26
26
|
declare const __QLARA_REGION__: string;
|
|
27
|
+
declare const __QLARA_CACHE_TTL__: number;
|
|
27
28
|
// ── Types (inlined to keep bundle self-contained) ────────────────
|
|
28
29
|
|
|
29
30
|
interface ManifestRoute {
|
|
@@ -238,7 +239,7 @@ function buildHtmlResponse(
|
|
|
238
239
|
{ key: 'Content-Type', value: 'text/html; charset=utf-8' },
|
|
239
240
|
],
|
|
240
241
|
'cache-control': [
|
|
241
|
-
{ key: 'Cache-Control', value:
|
|
242
|
+
{ key: 'Cache-Control', value: `public, max-age=0, s-maxage=${__QLARA_CACHE_TTL__}, stale-while-revalidate=60` },
|
|
242
243
|
],
|
|
243
244
|
};
|
|
244
245
|
|
|
@@ -52,6 +52,9 @@ import type {
|
|
|
52
52
|
// The routes module is resolved by esbuild at deploy time.
|
|
53
53
|
// esbuild's `alias` option maps this import to the developer's route file.
|
|
54
54
|
// At bundle time: '__qlara_routes__' → './qlara.routes.ts' (or wherever the dev put it)
|
|
55
|
+
// Injected at bundle time by esbuild define
|
|
56
|
+
declare const __QLARA_CACHE_TTL__: number;
|
|
57
|
+
|
|
55
58
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
56
59
|
// @ts-ignore — resolved at bundle time by esbuild alias
|
|
57
60
|
import routes from '__qlara_routes__';
|
|
@@ -752,20 +755,30 @@ function metadataToRscEntries(metadata: QlaraMetadata): string {
|
|
|
752
755
|
return entries.join('');
|
|
753
756
|
}
|
|
754
757
|
|
|
755
|
-
export async function handler(event: RendererEvent): Promise<RendererResult> {
|
|
758
|
+
export async function handler(event: RendererEvent & { warmup?: boolean }): Promise<RendererResult> {
|
|
759
|
+
// Warmup invocation — just initialize the runtime and return
|
|
760
|
+
if (event.warmup) {
|
|
761
|
+
return { statusCode: 200, body: 'warm' };
|
|
762
|
+
}
|
|
763
|
+
|
|
756
764
|
const { uri, bucket, routePattern, params } = event;
|
|
757
765
|
const region = process.env.AWS_REGION || 'us-east-1';
|
|
758
766
|
|
|
759
767
|
const s3 = new S3Client({ region });
|
|
760
768
|
|
|
761
769
|
try {
|
|
762
|
-
// 0. Check if
|
|
770
|
+
// 0. Check if already rendered + read fallback in parallel
|
|
763
771
|
const s3Key = deriveS3Key(uri);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
)
|
|
768
|
-
|
|
772
|
+
const fallbackKey = deriveFallbackKey(uri);
|
|
773
|
+
|
|
774
|
+
const [existingResult, fallbackResult] = await Promise.allSettled([
|
|
775
|
+
s3.send(new GetObjectCommand({ Bucket: bucket, Key: s3Key })),
|
|
776
|
+
s3.send(new GetObjectCommand({ Bucket: bucket, Key: fallbackKey })),
|
|
777
|
+
]);
|
|
778
|
+
|
|
779
|
+
// If page already exists, return it (guards against duplicate concurrent requests)
|
|
780
|
+
if (existingResult.status === 'fulfilled') {
|
|
781
|
+
const existingHtml = await existingResult.value.Body?.transformToString('utf-8');
|
|
769
782
|
if (existingHtml) {
|
|
770
783
|
return {
|
|
771
784
|
statusCode: 200,
|
|
@@ -773,26 +786,20 @@ export async function handler(event: RendererEvent): Promise<RendererResult> {
|
|
|
773
786
|
html: existingHtml,
|
|
774
787
|
};
|
|
775
788
|
}
|
|
776
|
-
} catch {
|
|
777
|
-
// Page doesn't exist yet — continue with rendering
|
|
778
789
|
}
|
|
779
790
|
|
|
780
|
-
//
|
|
781
|
-
const fallbackKey = deriveFallbackKey(uri);
|
|
791
|
+
// Extract fallback HTML
|
|
782
792
|
let fallbackHtml: string;
|
|
783
793
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
new GetObjectCommand({ Bucket: bucket, Key: fallbackKey })
|
|
787
|
-
);
|
|
788
|
-
fallbackHtml = (await response.Body?.transformToString('utf-8')) || '';
|
|
794
|
+
if (fallbackResult.status === 'fulfilled') {
|
|
795
|
+
fallbackHtml = (await fallbackResult.value.Body?.transformToString('utf-8')) || '';
|
|
789
796
|
if (!fallbackHtml) {
|
|
790
797
|
return {
|
|
791
798
|
statusCode: 500,
|
|
792
799
|
body: JSON.stringify({ error: `Empty fallback at ${fallbackKey}` }),
|
|
793
800
|
};
|
|
794
801
|
}
|
|
795
|
-
}
|
|
802
|
+
} else {
|
|
796
803
|
return {
|
|
797
804
|
statusCode: 500,
|
|
798
805
|
body: JSON.stringify({ error: `Fallback not found: ${fallbackKey}` }),
|
|
@@ -824,8 +831,7 @@ export async function handler(event: RendererEvent): Promise<RendererResult> {
|
|
|
824
831
|
Key: s3Key,
|
|
825
832
|
Body: html,
|
|
826
833
|
ContentType: 'text/html; charset=utf-8',
|
|
827
|
-
CacheControl:
|
|
828
|
-
'public, max-age=0, s-maxage=31536000, stale-while-revalidate=86400',
|
|
834
|
+
CacheControl: `public, max-age=0, s-maxage=${__QLARA_CACHE_TTL__}, stale-while-revalidate=60`,
|
|
829
835
|
})
|
|
830
836
|
);
|
|
831
837
|
|
package/dist/chunk-3RG5ZIWI.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
export {
|
|
9
|
-
__require
|
|
10
|
-
};
|