@lunora/cli 1.0.0-alpha.73 → 1.0.0-alpha.75
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/index.d.mts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +3 -3
- package/dist/packem_chunks/handler.mjs +2 -2
- package/dist/packem_chunks/handler17.mjs +1 -1
- package/dist/packem_chunks/runInitCommand.mjs +53 -14
- package/dist/packem_shared/{buildRegistryIndex-CqM5FwGI.mjs → buildRegistryIndex-BS5ig822.mjs} +1 -1
- package/dist/packem_shared/{commands-vnRRkhNE.mjs → commands-ClEvcz3V.mjs} +103 -5
- package/dist/packem_shared/{parseManifest-BBrXCg9V.mjs → parseManifest-Dbp-Q2q3.mjs} +20 -0
- package/dist/packem_shared/runAddCommand-DGjFXGqa.mjs +4 -0
- package/dist/packem_shared/{storage-bGfgwOgF.mjs → storage-DEXpJkXh.mjs} +1 -1
- package/package.json +1 -1
- package/dist/packem_shared/runAddCommand-BCcJzs_k.mjs +0 -4
package/dist/index.d.mts
CHANGED
|
@@ -651,6 +651,13 @@ interface RegistryEnvVariable {
|
|
|
651
651
|
/** A default/example value for non-secret vars. */
|
|
652
652
|
value?: string;
|
|
653
653
|
}
|
|
654
|
+
/** A re-export the item needs injected into the worker entry point (class-B/C only). */
|
|
655
|
+
interface EntrypointReexport {
|
|
656
|
+
/** Optional JS comment placed above the re-export line. */
|
|
657
|
+
comment?: string;
|
|
658
|
+
/** Module specifier (e.g. `"_generated/workflows"` → `export * from "./lunora/_generated/workflows"`). */
|
|
659
|
+
module: string;
|
|
660
|
+
}
|
|
654
661
|
/** The `registry.json` manifest shape. */
|
|
655
662
|
interface RegistryManifest {
|
|
656
663
|
/** wrangler.jsonc additions (best-effort structural edits). */
|
|
@@ -662,6 +669,8 @@ interface RegistryManifest {
|
|
|
662
669
|
devDependencies?: Readonly<Record<string, string>>;
|
|
663
670
|
/** Post-install guidance printed after the item is added (per-item next steps). */
|
|
664
671
|
docs?: string;
|
|
672
|
+
/** Worker-entry re-exports the item needs (class-B/C only). */
|
|
673
|
+
entrypointReexports?: ReadonlyArray<EntrypointReexport>;
|
|
665
674
|
/** Environment variables the item needs; scaffolded into `.dev.vars`. */
|
|
666
675
|
envVars?: ReadonlyArray<RegistryEnvVariable>;
|
|
667
676
|
files: ReadonlyArray<RegistryFile>;
|
package/dist/index.d.ts
CHANGED
|
@@ -651,6 +651,13 @@ interface RegistryEnvVariable {
|
|
|
651
651
|
/** A default/example value for non-secret vars. */
|
|
652
652
|
value?: string;
|
|
653
653
|
}
|
|
654
|
+
/** A re-export the item needs injected into the worker entry point (class-B/C only). */
|
|
655
|
+
interface EntrypointReexport {
|
|
656
|
+
/** Optional JS comment placed above the re-export line. */
|
|
657
|
+
comment?: string;
|
|
658
|
+
/** Module specifier (e.g. `"_generated/workflows"` → `export * from "./lunora/_generated/workflows"`). */
|
|
659
|
+
module: string;
|
|
660
|
+
}
|
|
654
661
|
/** The `registry.json` manifest shape. */
|
|
655
662
|
interface RegistryManifest {
|
|
656
663
|
/** wrangler.jsonc additions (best-effort structural edits). */
|
|
@@ -662,6 +669,8 @@ interface RegistryManifest {
|
|
|
662
669
|
devDependencies?: Readonly<Record<string, string>>;
|
|
663
670
|
/** Post-install guidance printed after the item is added (per-item next steps). */
|
|
664
671
|
docs?: string;
|
|
672
|
+
/** Worker-entry re-exports the item needs (class-B/C only). */
|
|
673
|
+
entrypointReexports?: ReadonlyArray<EntrypointReexport>;
|
|
665
674
|
/** Environment variables the item needs; scaffolded into `.dev.vars`. */
|
|
666
675
|
envVars?: ReadonlyArray<RegistryEnvVariable>;
|
|
667
676
|
files: ReadonlyArray<RegistryFile>;
|
package/dist/index.mjs
CHANGED
|
@@ -13,7 +13,7 @@ export { createLogger, pail } from './packem_shared/createLogger-CIWSHrTL.mjs';
|
|
|
13
13
|
export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType } from './packem_shared/diffSnapshots-BeDvvNiF.mjs';
|
|
14
14
|
export { default as schemaIrToSnapshot } from './packem_shared/schemaIrToSnapshot-DdsljJT-.mjs';
|
|
15
15
|
export { createRecordingSpawner, defaultSpawner } from './packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
|
|
16
|
-
export { default as parseManifest } from './packem_shared/parseManifest-
|
|
16
|
+
export { default as parseManifest } from './packem_shared/parseManifest-Dbp-Q2q3.mjs';
|
|
17
17
|
export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
|
|
18
|
-
export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-
|
|
19
|
-
export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-
|
|
18
|
+
export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-BS5ig822.mjs';
|
|
19
|
+
export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-ClEvcz3V.mjs';
|
|
@@ -4,8 +4,8 @@ import { join, basename } from '@visulima/path';
|
|
|
4
4
|
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
|
|
5
5
|
import { v as validateOutputFormat, i as isJsonFormat, p as printJson, l as loggerForFormat } from '../packem_shared/output-format-B4642rjE.mjs';
|
|
6
6
|
import { t as tuiText, a as tuiSelect } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
|
|
7
|
-
import { n as normalizeFeature, E as EMAIL_ITEM, s as sanitizeBucketName, d as deriveBucketName, p as promptBucketName, r as resolveTypedDestination, M as MAIL_DESTINATION_PROMPT, a as sanitizeDatabaseName, b as deriveDatabaseName, c as promptDatabaseName, D as DEFAULT_AUTH_ITEM, e as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS, w as withStorageBucketName, f as withMailDestination, g as withAuthDatabaseName } from '../packem_shared/storage-
|
|
8
|
-
import { r as runAddCommand } from '../packem_shared/commands-
|
|
7
|
+
import { n as normalizeFeature, E as EMAIL_ITEM, s as sanitizeBucketName, d as deriveBucketName, p as promptBucketName, r as resolveTypedDestination, M as MAIL_DESTINATION_PROMPT, a as sanitizeDatabaseName, b as deriveDatabaseName, c as promptDatabaseName, D as DEFAULT_AUTH_ITEM, e as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS, w as withStorageBucketName, f as withMailDestination, g as withAuthDatabaseName } from '../packem_shared/storage-DEXpJkXh.mjs';
|
|
8
|
+
import { r as runAddCommand } from '../packem_shared/commands-ClEvcz3V.mjs';
|
|
9
9
|
|
|
10
10
|
const providerToItem = (provider) => {
|
|
11
11
|
const value = provider.trim().toLowerCase();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as defineHandler } from '../packem_shared/command-lYnl4QyF.mjs';
|
|
2
|
-
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-
|
|
2
|
+
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-ClEvcz3V.mjs';
|
|
3
3
|
|
|
4
4
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
5
5
|
const subcommand = argument[0];
|
|
@@ -11,11 +11,11 @@ import { d as detectPackageManager, a as detectInstalledManagers, i as installAr
|
|
|
11
11
|
import MagicString from 'magic-string';
|
|
12
12
|
import { Project, SyntaxKind } from 'ts-morph';
|
|
13
13
|
import { P as PromptCancelledError } from '../packem_shared/prompt-cancelled-APzX1Im-.mjs';
|
|
14
|
-
import { c as resolveTagVersions, d as resolveSourceRef, e as resolvePinnedSourceRef, f as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-
|
|
14
|
+
import { c as resolveTagVersions, d as resolveSourceRef, e as resolvePinnedSourceRef, f as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-ClEvcz3V.mjs';
|
|
15
15
|
import { defaultSpawner } from '../packem_shared/createRecordingSpawner-Cw5Iu73G.mjs';
|
|
16
16
|
import { d as tuiMascot, e as tuiStep, f as tuiMoonrise, t as tuiText, g as tuiHeadline, h as tuiInfo, a as tuiSelect, b as tuiConfirm, w as withTuiSpinner, i as tuiNextSteps, j as tuiTasks, k as tuiMultiSelect, l as withTuiBadgeProgress } from '../packem_shared/tui-prompts-BjEN8XgP.mjs';
|
|
17
17
|
import { logStep } from '../packem_shared/createLogger-CIWSHrTL.mjs';
|
|
18
|
-
import { E as EMAIL_ITEM, p as promptBucketName, w as withStorageBucketName, M as MAIL_DESTINATION_PROMPT, r as resolveTypedDestination, f as withMailDestination, e as promptAuthProvider, c as promptDatabaseName, g as withAuthDatabaseName } from '../packem_shared/storage-
|
|
18
|
+
import { E as EMAIL_ITEM, p as promptBucketName, w as withStorageBucketName, M as MAIL_DESTINATION_PROMPT, r as resolveTypedDestination, f as withMailDestination, e as promptAuthProvider, c as promptDatabaseName, g as withAuthDatabaseName } from '../packem_shared/storage-DEXpJkXh.mjs';
|
|
19
19
|
import dns from 'node:dns/promises';
|
|
20
20
|
|
|
21
21
|
const GITHUB_CONTENT = `name: Deploy
|
|
@@ -1470,7 +1470,49 @@ const ADAPTERS = {
|
|
|
1470
1470
|
};
|
|
1471
1471
|
const isOverlayFramework = (value) => Object.hasOwn(ADAPTERS, value);
|
|
1472
1472
|
|
|
1473
|
-
const
|
|
1473
|
+
const RATELIMIT_SCHEMA = `import type { Middleware } from "lunorash/server";
|
|
1474
|
+
import { defineSchemaExtension, defineTable, definePlugin, v } from "lunorash/server";
|
|
1475
|
+
import { createDbStore, RateLimiter } from "lunorash/ratelimit";
|
|
1476
|
+
import type { RateLimitConfigMap } from "lunorash/ratelimit";
|
|
1477
|
+
|
|
1478
|
+
export const limits = {
|
|
1479
|
+
default: { kind: "token bucket", period: 60_000, rate: 10 },
|
|
1480
|
+
} as const satisfies RateLimitConfigMap;
|
|
1481
|
+
|
|
1482
|
+
export type LimitName = keyof typeof limits;
|
|
1483
|
+
|
|
1484
|
+
export const makeRateLimiter = (ctx: { db: unknown }): RateLimiter<LimitName> =>
|
|
1485
|
+
new RateLimiter<LimitName>({
|
|
1486
|
+
config: limits,
|
|
1487
|
+
store: createDbStore({ db: ctx.db as never, table: "ratelimit_buckets" }),
|
|
1488
|
+
});
|
|
1489
|
+
|
|
1490
|
+
const middleware: Middleware<{ api?: Record<string, unknown>; db: unknown }, { api: Record<string, unknown>; db: unknown }> = ({ ctx, next }) =>
|
|
1491
|
+
next({
|
|
1492
|
+
ctx: {
|
|
1493
|
+
...ctx,
|
|
1494
|
+
api: { ...ctx.api, ratelimit: makeRateLimiter(ctx) },
|
|
1495
|
+
},
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
export const ratelimit = definePlugin("ratelimit", {
|
|
1499
|
+
extension: defineSchemaExtension("ratelimit", {
|
|
1500
|
+
tables: {
|
|
1501
|
+
buckets: defineTable({
|
|
1502
|
+
key: v.string(),
|
|
1503
|
+
value: v.number(),
|
|
1504
|
+
ts: v.number(),
|
|
1505
|
+
prev: v.optional(v.number()),
|
|
1506
|
+
})
|
|
1507
|
+
.index("by_key", ["key"])
|
|
1508
|
+
.externallyManaged(),
|
|
1509
|
+
},
|
|
1510
|
+
}),
|
|
1511
|
+
middleware,
|
|
1512
|
+
});
|
|
1513
|
+
`;
|
|
1514
|
+
const LUNORA_SCHEMA = `import { ratelimit } from "./ratelimit/schema.js";
|
|
1515
|
+
import { defineSchema, defineTable, v } from "lunorash/server";
|
|
1474
1516
|
|
|
1475
1517
|
export default defineSchema({
|
|
1476
1518
|
messages: defineTable({
|
|
@@ -1479,22 +1521,17 @@ export default defineSchema({
|
|
|
1479
1521
|
})
|
|
1480
1522
|
.shardBy("channelId")
|
|
1481
1523
|
.index("by_channel", ["channelId"]),
|
|
1482
|
-
});
|
|
1524
|
+
}).extend(ratelimit.extension);
|
|
1483
1525
|
`;
|
|
1484
|
-
const LUNORA_MESSAGES = `import { RateLimiter, rateLimit } from "
|
|
1526
|
+
const LUNORA_MESSAGES = `import { RateLimiter, rateLimit, createDbStore } from "lunorash/ratelimit";
|
|
1485
1527
|
|
|
1486
1528
|
import { mutation, query, v } from "#lunora/_generated/server.js";
|
|
1487
1529
|
|
|
1488
|
-
|
|
1489
|
-
* One in-memory limiter so the public \`send\` mutation isn't an open flood target
|
|
1490
|
-
* out of the box. The default store is in-memory (per-isolate, resets on
|
|
1491
|
-
* eviction) — fine for a starter; run \`lunora add ratelimit\` for the durable,
|
|
1492
|
-
* \`ctx.db\`-backed store when you ship to production.
|
|
1493
|
-
*/
|
|
1494
|
-
const limiter = new RateLimiter({
|
|
1530
|
+
const limiter = (ctx: { db: unknown }) => new RateLimiter({
|
|
1495
1531
|
config: {
|
|
1496
1532
|
send: { kind: "token bucket", period: 60_000, rate: 30 },
|
|
1497
1533
|
},
|
|
1534
|
+
store: createDbStore({ db: ctx.db as never, table: "ratelimit_buckets" }),
|
|
1498
1535
|
});
|
|
1499
1536
|
|
|
1500
1537
|
export const list = query.input({ channelId: v.string().meta({ schema: { maxLength: 256 } }), limit: v.optional(v.number()) }).query(async ({ args, ctx }) => {
|
|
@@ -1620,6 +1657,7 @@ const patchBaseViteConfig = (target, logger) => {
|
|
|
1620
1657
|
const applyLunoraOverlay = async (options) => {
|
|
1621
1658
|
const { adapter, distTag, logger, name, target } = options;
|
|
1622
1659
|
const written = [];
|
|
1660
|
+
writeFile(target, join$1("lunora", "ratelimit", "schema.ts"), RATELIMIT_SCHEMA, written);
|
|
1623
1661
|
writeFile(target, join$1("lunora", "schema.ts"), LUNORA_SCHEMA, written);
|
|
1624
1662
|
writeFile(target, join$1("lunora", "messages.ts"), LUNORA_MESSAGES, written);
|
|
1625
1663
|
writeFile(target, join$1("src", "server.ts"), SERVER_ENTRY, written);
|
|
@@ -1819,6 +1857,7 @@ const stampLunoraDeps = (packageJsonText, distTag, versions) => {
|
|
|
1819
1857
|
const PNPM_BUILT_DEPENDENCIES = [
|
|
1820
1858
|
"@parcel/watcher",
|
|
1821
1859
|
"esbuild",
|
|
1860
|
+
"lmdb",
|
|
1822
1861
|
"msgpackr-extract",
|
|
1823
1862
|
"rs-module-lexer",
|
|
1824
1863
|
"sharp",
|
|
@@ -1833,8 +1872,8 @@ const pnpmWorkspaceYaml = () => [
|
|
|
1833
1872
|
"# without the interactive `pnpm approve-builds` step; deny the optional native",
|
|
1834
1873
|
"# builds a scaffold doesn't need (so no C/C++ toolchain is required).",
|
|
1835
1874
|
"allowBuilds:",
|
|
1836
|
-
...PNPM_BUILT_DEPENDENCIES.map((name) => ` ${name}: true`),
|
|
1837
|
-
...PNPM_DENIED_BUILD_DEPENDENCIES.map((name) => ` ${name}: false`),
|
|
1875
|
+
...PNPM_BUILT_DEPENDENCIES.map((name) => ` '${name}': true`),
|
|
1876
|
+
...PNPM_DENIED_BUILD_DEPENDENCIES.map((name) => ` '${name}': false`),
|
|
1838
1877
|
""
|
|
1839
1878
|
].join("\n");
|
|
1840
1879
|
const collectFiles = (directory) => {
|
package/dist/packem_shared/{buildRegistryIndex-CqM5FwGI.mjs → buildRegistryIndex-BS5ig822.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { join } from '@visulima/path';
|
|
3
|
-
import parseManifest from './parseManifest-
|
|
3
|
+
import parseManifest from './parseManifest-Dbp-Q2q3.mjs';
|
|
4
4
|
|
|
5
5
|
const CONTROL_CHARS = /[\u0000-\u001F\u007F-\u009F]/gu;
|
|
6
6
|
const stripControlChars = (value) => value === void 0 ? void 0 : value.replaceAll(CONTROL_CHARS, "");
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
-
import { dirname, join } from '@visulima/path';
|
|
2
|
+
import { dirname, join, relative } from '@visulima/path';
|
|
3
3
|
import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
|
|
4
4
|
import { modify, applyEdits, parse } from 'jsonc-parser';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { LunoraError } from '@lunora/errors';
|
|
7
7
|
import { b as tuiConfirm } from './tui-prompts-BjEN8XgP.mjs';
|
|
8
|
-
import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-
|
|
8
|
+
import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BS5ig822.mjs';
|
|
9
9
|
import { insertSchemaExtension } from './insertSchemaExtension-DAqbfr9Z.mjs';
|
|
10
10
|
import { createHash } from 'node:crypto';
|
|
11
11
|
import { tmpdir } from 'node:os';
|
|
12
12
|
import { downloadTemplate } from 'giget';
|
|
13
|
-
import parseManifest from './parseManifest-
|
|
13
|
+
import parseManifest from './parseManifest-Dbp-Q2q3.mjs';
|
|
14
14
|
|
|
15
15
|
const DEFAULT_SOURCE_REF_FALLBACK = "alpha";
|
|
16
16
|
const STABLE_BRANCH = "main";
|
|
@@ -148,8 +148,8 @@ const resolveDepRange = (range) => {
|
|
|
148
148
|
}
|
|
149
149
|
return rest;
|
|
150
150
|
};
|
|
151
|
-
const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
|
|
152
|
-
const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|runtime|server|values)(\/[^'"]*)?\1/gu;
|
|
151
|
+
const UMBRELLA_REEXPORTED_DEPS = /* @__PURE__ */ new Set(["@lunora/client", "@lunora/do", "@lunora/ratelimit", "@lunora/runtime", "@lunora/server", "@lunora/values"]);
|
|
152
|
+
const UMBRELLA_IMPORT_RE = /(['"])@lunora\/(client|do|ratelimit|runtime|server|values)(\/[^'"]*)?\1/gu;
|
|
153
153
|
const projectUsesUmbrella = (projectRoot) => {
|
|
154
154
|
const packageJsonPath = join(projectRoot, "package.json");
|
|
155
155
|
if (!existsSync(packageJsonPath)) {
|
|
@@ -522,6 +522,93 @@ const reconcileFile = (file, itemKey, itemDirectory, projectRoot, logger, lock,
|
|
|
522
522
|
}
|
|
523
523
|
return reconcileWholeFile(file, itemKey, itemDirectory, projectRoot, logger, lock, reconcileOptions, useUmbrella);
|
|
524
524
|
};
|
|
525
|
+
const WORKER_ENTRY_FALLBACKS = ["src/server.ts", "src/server/index.ts", "src/index.ts", "src/worker.ts"];
|
|
526
|
+
const WRANGLER_MAIN_RE = /"main"\s*:\s*"([^"]+)"/u;
|
|
527
|
+
const readWranglerMain = (projectRoot) => {
|
|
528
|
+
for (const file of ["wrangler.jsonc", "wrangler.json"]) {
|
|
529
|
+
const path = join(projectRoot, file);
|
|
530
|
+
if (!existsSync(path)) {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
const match = WRANGLER_MAIN_RE.exec(readFileSync(path, "utf8"));
|
|
534
|
+
if (match?.[1]) {
|
|
535
|
+
return match[1];
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
return void 0;
|
|
539
|
+
};
|
|
540
|
+
const findWorkerEntry = (projectRoot) => {
|
|
541
|
+
const main = readWranglerMain(projectRoot);
|
|
542
|
+
const candidates = main === void 0 ? WORKER_ENTRY_FALLBACKS : [main, ...WORKER_ENTRY_FALLBACKS];
|
|
543
|
+
for (const candidate of candidates) {
|
|
544
|
+
const absolute = join(projectRoot, candidate);
|
|
545
|
+
if (!existsSync(absolute)) {
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const content = readFileSync(absolute, "utf8");
|
|
549
|
+
if (!content.includes("createShardDO(")) {
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
return { entryPath: absolute, main: candidate, source: content };
|
|
553
|
+
}
|
|
554
|
+
return void 0;
|
|
555
|
+
};
|
|
556
|
+
const computeRelativeSpecifier = (entryPath, projectRoot, moduleName) => {
|
|
557
|
+
const importPath = relative(dirname(entryPath), join(projectRoot, "lunora", moduleName)).replaceAll("\\", "/");
|
|
558
|
+
return importPath.startsWith(".") ? importPath : `./${importPath}`;
|
|
559
|
+
};
|
|
560
|
+
const logClassAFallback = (entrypointReexports, logger) => {
|
|
561
|
+
for (const reexport of entrypointReexports) {
|
|
562
|
+
const specifier = `./lunora/${reexport.module}.js`;
|
|
563
|
+
const instruction = `Add \`export * from "${specifier}"\` to your worker entry`;
|
|
564
|
+
const suffix = reexport.comment ? ` (${reexport.comment})` : "";
|
|
565
|
+
logger.warn(`${instruction}${suffix}`);
|
|
566
|
+
}
|
|
567
|
+
return 0;
|
|
568
|
+
};
|
|
569
|
+
const buildReexportLines = (entrypointReexports, entryPath, projectRoot, source) => {
|
|
570
|
+
const lines = [];
|
|
571
|
+
for (const reexport of entrypointReexports) {
|
|
572
|
+
const specifier = computeRelativeSpecifier(entryPath, projectRoot, reexport.module);
|
|
573
|
+
const escapedSpecifier = specifier.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
574
|
+
const existingRe = new RegExp(String.raw`export\s+\*\s+from\s+["']${escapedSpecifier}\.js["']`, "u");
|
|
575
|
+
if (existingRe.test(source)) {
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
if (reexport.comment) {
|
|
579
|
+
lines.push(`
|
|
580
|
+
// ${reexport.comment}`);
|
|
581
|
+
}
|
|
582
|
+
lines.push(`export * from "${specifier}.js";`);
|
|
583
|
+
}
|
|
584
|
+
return lines;
|
|
585
|
+
};
|
|
586
|
+
const applyEntrypointReexports = (entrypointReexports, projectRoot, logger, diff) => {
|
|
587
|
+
if (entrypointReexports.length === 0) {
|
|
588
|
+
return 0;
|
|
589
|
+
}
|
|
590
|
+
const entry = findWorkerEntry(projectRoot);
|
|
591
|
+
if (entry === void 0) {
|
|
592
|
+
return logClassAFallback(entrypointReexports, logger);
|
|
593
|
+
}
|
|
594
|
+
const linesToAppend = buildReexportLines(entrypointReexports, entry.entryPath, projectRoot, entry.source);
|
|
595
|
+
if (linesToAppend.length === 0) {
|
|
596
|
+
return 0;
|
|
597
|
+
}
|
|
598
|
+
if (diff) {
|
|
599
|
+
for (const line of linesToAppend) {
|
|
600
|
+
if (line !== "") {
|
|
601
|
+
logger.info(`~ entrypoint: ${line}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return linesToAppend.length;
|
|
605
|
+
}
|
|
606
|
+
const separator = entry.source.endsWith("\n") ? "" : "\n";
|
|
607
|
+
writeFileSync(entry.entryPath, `${entry.source}${separator}${linesToAppend.join("\n")}
|
|
608
|
+
`, "utf8");
|
|
609
|
+
logger.success(`wrote ${String(linesToAppend.length)} entrypoint re-export(s) to ${relative(projectRoot, entry.entryPath)}`);
|
|
610
|
+
return linesToAppend.length;
|
|
611
|
+
};
|
|
525
612
|
const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
526
613
|
const written = [];
|
|
527
614
|
const skipped = [];
|
|
@@ -534,6 +621,9 @@ const reconcileItems = (items, cwd, logger, reconcileOptions = {}) => {
|
|
|
534
621
|
const outcome = reconcileFile(file, manifest.name, directory, cwd, logger, lock, reconcileOptions, useUmbrella);
|
|
535
622
|
(outcome.kind === "written" ? written : skipped).push(outcome.path);
|
|
536
623
|
}
|
|
624
|
+
if (manifest.entrypointReexports !== void 0) {
|
|
625
|
+
applyEntrypointReexports(manifest.entrypointReexports, cwd, logger, reconcileOptions.diff === true);
|
|
626
|
+
}
|
|
537
627
|
if (reconcileOptions.diff) {
|
|
538
628
|
continue;
|
|
539
629
|
}
|
|
@@ -707,6 +797,11 @@ const printPlan = (logger, manifest) => {
|
|
|
707
797
|
const valueSuffix = variable.secret ? " (secret)" : ` = ${JSON.stringify(variable.value ?? "")}`;
|
|
708
798
|
logger.info(` env ${variable.name}${valueSuffix}`);
|
|
709
799
|
}
|
|
800
|
+
for (const reexport of manifest.entrypointReexports ?? []) {
|
|
801
|
+
const specifier = `./lunora/${reexport.module}`;
|
|
802
|
+
const suffix = reexport.comment ? ` // ${reexport.comment}` : "";
|
|
803
|
+
logger.info(` entry ${specifier}${suffix}`);
|
|
804
|
+
}
|
|
710
805
|
};
|
|
711
806
|
const printJsonPlan = (items) => {
|
|
712
807
|
const planSnapshot = items.map(({ manifest }) => {
|
|
@@ -718,6 +813,9 @@ const printJsonPlan = (items) => {
|
|
|
718
813
|
}),
|
|
719
814
|
deps: Object.keys(manifest.deps ?? {}),
|
|
720
815
|
devDependencies: Object.keys(manifest.devDependencies ?? {}),
|
|
816
|
+
entrypointReexports: (manifest.entrypointReexports ?? []).map((reexport) => {
|
|
817
|
+
return { module: reexport.module, ...reexport.comment ? { comment: reexport.comment } : {} };
|
|
818
|
+
}),
|
|
721
819
|
envVars: (manifest.envVars ?? []).map((variable) => {
|
|
722
820
|
return { name: variable.name, ...variable.secret ? { secret: true } : { value: variable.value ?? "" } };
|
|
723
821
|
}),
|
|
@@ -3,6 +3,7 @@ import { LunoraError } from '@lunora/errors';
|
|
|
3
3
|
const NEWLINE_PRESENT = /[\r\n]/u;
|
|
4
4
|
const VALID_ENV_NAME = /^[A-Za-z_]\w*$/u;
|
|
5
5
|
const VALID_ITEM_NAME = /^[A-Za-z0-9][\w-]*$/u;
|
|
6
|
+
const VALID_MODULE_PATH = /^[\w.-]+(?:\/[\w.-]+)*$/u;
|
|
6
7
|
const parseManifest = (raw, itemName) => {
|
|
7
8
|
if (typeof raw !== "object" || raw === null) {
|
|
8
9
|
throw new LunoraError("INTERNAL", `registry.json for "${itemName}" is not an object`);
|
|
@@ -60,6 +61,24 @@ const parseManifest = (raw, itemName) => {
|
|
|
60
61
|
const bindingRecord = value;
|
|
61
62
|
return Array.isArray(bindingRecord.path) && bindingRecord.path.every((segment) => typeof segment === "string");
|
|
62
63
|
}) : void 0;
|
|
64
|
+
const entrypointReexports = Array.isArray(record.entrypointReexports) ? record.entrypointReexports.filter(
|
|
65
|
+
(value) => typeof value === "object" && value !== null && typeof value.module === "string"
|
|
66
|
+
).map((entry) => {
|
|
67
|
+
if (entry.module.includes("..") || entry.module.startsWith("/") || !VALID_MODULE_PATH.test(entry.module)) {
|
|
68
|
+
throw new LunoraError(
|
|
69
|
+
"INTERNAL",
|
|
70
|
+
`registry.json "${itemName}": entrypointReexports[].module "${entry.module}" must be a safe relative module path without path traversal or unsafe characters`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const reexport = { module: entry.module };
|
|
74
|
+
if (typeof entry.comment === "string") {
|
|
75
|
+
if (NEWLINE_PRESENT.test(entry.comment)) {
|
|
76
|
+
throw new LunoraError("INTERNAL", `registry.json "${itemName}": entrypointReexports[].comment must not contain a newline`);
|
|
77
|
+
}
|
|
78
|
+
reexport.comment = entry.comment;
|
|
79
|
+
}
|
|
80
|
+
return reexport;
|
|
81
|
+
}) : void 0;
|
|
63
82
|
const envVariables = Array.isArray(record.envVars) ? record.envVars.filter(
|
|
64
83
|
(value) => typeof value === "object" && value !== null && typeof value.name === "string"
|
|
65
84
|
).map((entry) => {
|
|
@@ -90,6 +109,7 @@ const parseManifest = (raw, itemName) => {
|
|
|
90
109
|
description: typeof record.description === "string" ? record.description : void 0,
|
|
91
110
|
devDependencies,
|
|
92
111
|
docs: typeof record.docs === "string" ? record.docs : void 0,
|
|
112
|
+
entrypointReexports,
|
|
93
113
|
envVars: envVariables,
|
|
94
114
|
files,
|
|
95
115
|
name,
|
package/package.json
CHANGED