@beignet/cli 0.0.37 → 0.0.38
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/CHANGELOG.md +17 -0
- package/README.md +18 -5
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/inspect.js +15 -15
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +27 -6
- package/dist/lint.js.map +1 -1
- package/dist/make/inbox.js +7 -7
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts +2 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +15 -11
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +1 -1
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +49 -37
- package/dist/make/shared.js.map +1 -1
- package/dist/make/tenancy.js +8 -8
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +27 -16
- package/dist/make.js.map +1 -1
- package/dist/operational-error-reporting.d.ts +15 -0
- package/dist/operational-error-reporting.d.ts.map +1 -0
- package/dist/operational-error-reporting.js +42 -0
- package/dist/operational-error-reporting.js.map +1 -0
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +73 -0
- package/dist/outbox.js.map +1 -1
- package/dist/provider-add.js +17 -17
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.js +3 -3
- package/dist/provider-audit.js.map +1 -1
- package/dist/schedule.d.ts.map +1 -1
- package/dist/schedule.js +28 -1
- package/dist/schedule.js.map +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +37 -4
- package/dist/task.js.map +1 -1
- package/dist/templates/index.js +2 -2
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +5 -5
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/testing.js +1 -1
- package/dist/templates/todos.js +6 -6
- package/package.json +4 -4
- package/src/config.ts +2 -2
- package/src/inspect.ts +15 -17
- package/src/lint.ts +35 -4
- package/src/make/inbox.ts +7 -7
- package/src/make/payments.ts +23 -15
- package/src/make/shared.ts +56 -40
- package/src/make/tenancy.ts +8 -8
- package/src/make.ts +30 -16
- package/src/operational-error-reporting.ts +60 -0
- package/src/outbox.ts +77 -0
- package/src/provider-add.ts +21 -17
- package/src/provider-audit.ts +3 -3
- package/src/schedule.ts +32 -1
- package/src/task.ts +41 -4
- package/src/templates/index.ts +2 -2
- package/src/templates/server.ts +5 -5
- package/src/templates/testing.ts +1 -1
- package/src/templates/todos.ts +6 -6
package/src/inspect.ts
CHANGED
|
@@ -558,7 +558,7 @@ function inspectConvention(
|
|
|
558
558
|
]);
|
|
559
559
|
const missingResourceGenerator = missingRequirements(files, [
|
|
560
560
|
config.paths.appContext,
|
|
561
|
-
config.paths.
|
|
561
|
+
config.paths.portWiring,
|
|
562
562
|
config.paths.ports,
|
|
563
563
|
config.paths.server,
|
|
564
564
|
config.paths.useCaseBuilder,
|
|
@@ -1515,9 +1515,9 @@ async function inspectCanonicalConformance(
|
|
|
1515
1515
|
"server/providers.ts should own provider registration, even when the provider list is small.",
|
|
1516
1516
|
},
|
|
1517
1517
|
{
|
|
1518
|
-
file: config.paths.
|
|
1518
|
+
file: config.paths.portWiring,
|
|
1519
1519
|
reason:
|
|
1520
|
-
"infra/
|
|
1520
|
+
"infra/port-wiring.ts should wire concrete app ports for the selected runtime.",
|
|
1521
1521
|
},
|
|
1522
1522
|
{
|
|
1523
1523
|
file: config.paths.routesBuilder,
|
|
@@ -2067,7 +2067,7 @@ async function inspectProductionReadiness(
|
|
|
2067
2067
|
);
|
|
2068
2068
|
const configFiles = operationalConfigFiles(files, config);
|
|
2069
2069
|
const infrastructurePath = directoryPath(
|
|
2070
|
-
path.dirname(config.paths.
|
|
2070
|
+
path.dirname(config.paths.portWiring),
|
|
2071
2071
|
);
|
|
2072
2072
|
const schemaDir = `${infrastructurePath}/db/schema`;
|
|
2073
2073
|
let hasSecurityHeadersHook = false;
|
|
@@ -2567,7 +2567,7 @@ async function inspectPaymentsProductionReadiness(
|
|
|
2567
2567
|
diagnostics.push({
|
|
2568
2568
|
severity: "warning",
|
|
2569
2569
|
code: "BEIGNET_BILLING_ENTITLEMENTS_PROVIDER_MISSING",
|
|
2570
|
-
file: path.dirname(config.paths.
|
|
2570
|
+
file: path.dirname(config.paths.portWiring),
|
|
2571
2571
|
message:
|
|
2572
2572
|
"features/billing/entitlements.ts exists, but no infra or server provider appears to call createBillingEntitlements(...). Wire the billing-backed entitlements port into AppPorts so paid feature gates use app billing state.",
|
|
2573
2573
|
});
|
|
@@ -3034,7 +3034,7 @@ async function hasBillingEntitlementsProviderWiring(
|
|
|
3034
3034
|
config: ResolvedBeignetConfig,
|
|
3035
3035
|
sourceCache: Map<string, string>,
|
|
3036
3036
|
): Promise<boolean> {
|
|
3037
|
-
const infraPath = path.dirname(config.paths.
|
|
3037
|
+
const infraPath = path.dirname(config.paths.portWiring);
|
|
3038
3038
|
const serverPath = path.dirname(config.paths.server);
|
|
3039
3039
|
const candidateFiles = files.filter(
|
|
3040
3040
|
(file) =>
|
|
@@ -3419,13 +3419,13 @@ function isInfraOrServerFile(
|
|
|
3419
3419
|
): boolean {
|
|
3420
3420
|
const serverDir = directoryPath(path.dirname(config.paths.server));
|
|
3421
3421
|
const infrastructureDir = directoryPath(
|
|
3422
|
-
path.dirname(config.paths.
|
|
3422
|
+
path.dirname(config.paths.portWiring),
|
|
3423
3423
|
);
|
|
3424
3424
|
|
|
3425
3425
|
return (
|
|
3426
3426
|
file === config.paths.server ||
|
|
3427
3427
|
file.startsWith(`${serverDir}/`) ||
|
|
3428
|
-
file === config.paths.
|
|
3428
|
+
file === config.paths.portWiring ||
|
|
3429
3429
|
file.startsWith(`${infrastructureDir}/`)
|
|
3430
3430
|
);
|
|
3431
3431
|
}
|
|
@@ -3550,9 +3550,7 @@ async function inspectWorkflowRegistrationDrift(
|
|
|
3550
3550
|
}
|
|
3551
3551
|
}
|
|
3552
3552
|
|
|
3553
|
-
const infraDir = directoryPath(
|
|
3554
|
-
path.dirname(config.paths.infrastructurePorts),
|
|
3555
|
-
);
|
|
3553
|
+
const infraDir = directoryPath(path.dirname(config.paths.portWiring));
|
|
3556
3554
|
const listenerTarget = drift.listeners.wiringFile
|
|
3557
3555
|
? `${drift.listeners.wiringFile}, which already calls registerListeners(...)`
|
|
3558
3556
|
: drift.listeners.eventBusFile
|
|
@@ -4382,7 +4380,7 @@ async function inspectDatabaseLifecycleDrift(
|
|
|
4382
4380
|
const sourceCache = new Map<string, string>();
|
|
4383
4381
|
const resources = await collectResourceNames(targetDir, files, config);
|
|
4384
4382
|
const infrastructurePath = directoryPath(
|
|
4385
|
-
path.dirname(config.paths.
|
|
4383
|
+
path.dirname(config.paths.portWiring),
|
|
4386
4384
|
);
|
|
4387
4385
|
const featuresPath = directoryPath(config.paths.features);
|
|
4388
4386
|
const repositoriesPath = path.join(
|
|
@@ -7301,21 +7299,21 @@ async function inspectResourceSlices(
|
|
|
7301
7299
|
"errors.ts",
|
|
7302
7300
|
);
|
|
7303
7301
|
const infrastructureDir = `${directoryPath(
|
|
7304
|
-
path.dirname(config.paths.
|
|
7302
|
+
path.dirname(config.paths.portWiring),
|
|
7305
7303
|
)}/${resource}/`;
|
|
7306
7304
|
const drizzleSchemaFile = path.join(
|
|
7307
|
-
path.dirname(config.paths.
|
|
7305
|
+
path.dirname(config.paths.portWiring),
|
|
7308
7306
|
"db",
|
|
7309
7307
|
"schema",
|
|
7310
7308
|
`${resource}.ts`,
|
|
7311
7309
|
);
|
|
7312
7310
|
const drizzleRepositoryFile = path.join(
|
|
7313
|
-
path.dirname(config.paths.
|
|
7311
|
+
path.dirname(config.paths.portWiring),
|
|
7314
7312
|
resource,
|
|
7315
7313
|
`drizzle-${singular}-repository.ts`,
|
|
7316
7314
|
);
|
|
7317
7315
|
const memoryRepositoryFile = path.join(
|
|
7318
|
-
path.dirname(config.paths.
|
|
7316
|
+
path.dirname(config.paths.portWiring),
|
|
7319
7317
|
resource,
|
|
7320
7318
|
`in-memory-${singular}-repository.ts`,
|
|
7321
7319
|
);
|
|
@@ -7653,7 +7651,7 @@ async function collectResourceNames(
|
|
|
7653
7651
|
const portsPath = directoryPath(path.dirname(config.paths.ports));
|
|
7654
7652
|
const featuresPath = directoryPath(config.paths.features);
|
|
7655
7653
|
const infrastructurePath = directoryPath(
|
|
7656
|
-
path.dirname(config.paths.
|
|
7654
|
+
path.dirname(config.paths.portWiring),
|
|
7657
7655
|
);
|
|
7658
7656
|
|
|
7659
7657
|
for (const file of files) {
|
package/src/lint.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import ts from "typescript";
|
|
3
4
|
import { createPainter } from "./ansi.js";
|
|
4
5
|
import {
|
|
5
6
|
type BeignetConfig,
|
|
@@ -89,6 +90,7 @@ export async function lintApp(
|
|
|
89
90
|
const diagnostics: LintDiagnostic[] = [];
|
|
90
91
|
const sourceFiles = lintSourceFiles(files);
|
|
91
92
|
const sourceFileSet = new Set(sourceFiles);
|
|
93
|
+
const atAliasRoot = resolveAtAliasRoot(targetDir);
|
|
92
94
|
const sourceByFile = new Map<string, string>();
|
|
93
95
|
const importGraph: ImportGraph = new Map();
|
|
94
96
|
|
|
@@ -96,7 +98,10 @@ export async function lintApp(
|
|
|
96
98
|
const sourceLayer = classifyPath(file, config);
|
|
97
99
|
const source = await readFile(path.join(targetDir, file), "utf8");
|
|
98
100
|
sourceByFile.set(file, source);
|
|
99
|
-
importGraph.set(
|
|
101
|
+
importGraph.set(
|
|
102
|
+
file,
|
|
103
|
+
parseImports(source, file, sourceFileSet, atAliasRoot),
|
|
104
|
+
);
|
|
100
105
|
|
|
101
106
|
if (sourceLayer === "test" || sourceLayer === "unknown") continue;
|
|
102
107
|
|
|
@@ -281,15 +286,40 @@ function parseImports(
|
|
|
281
286
|
source: string,
|
|
282
287
|
importerFile: string,
|
|
283
288
|
files: Set<string>,
|
|
289
|
+
atAliasRoot: string,
|
|
284
290
|
): ImportReference[] {
|
|
285
291
|
const references = parseImportSpecifiers(source);
|
|
286
292
|
|
|
287
293
|
return references.map((reference) => ({
|
|
288
294
|
...reference,
|
|
289
|
-
...resolveImport(reference.importPath, importerFile, files),
|
|
295
|
+
...resolveImport(reference.importPath, importerFile, files, atAliasRoot),
|
|
290
296
|
}));
|
|
291
297
|
}
|
|
292
298
|
|
|
299
|
+
function resolveAtAliasRoot(targetDir: string): string {
|
|
300
|
+
const tsconfigPath = path.join(targetDir, "tsconfig.json");
|
|
301
|
+
const parsed = ts.getParsedCommandLineOfConfigFile(
|
|
302
|
+
tsconfigPath,
|
|
303
|
+
{},
|
|
304
|
+
{
|
|
305
|
+
...ts.sys,
|
|
306
|
+
onUnRecoverableConfigFileDiagnostic: () => {},
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
const aliasTarget = parsed?.options.paths?.["@/*"]?.[0];
|
|
310
|
+
if (!aliasTarget) return "";
|
|
311
|
+
|
|
312
|
+
const targetWithoutWildcard = aliasTarget.replace(/[/\\]?\*$/, "");
|
|
313
|
+
const baseUrl = parsed.options.baseUrl ?? targetDir;
|
|
314
|
+
const absoluteTarget = path.resolve(baseUrl, targetWithoutWildcard);
|
|
315
|
+
const relativeTarget = normalizePath(
|
|
316
|
+
path.relative(targetDir, absoluteTarget),
|
|
317
|
+
);
|
|
318
|
+
if (relativeTarget === "" || relativeTarget === ".") return "";
|
|
319
|
+
if (relativeTarget === ".." || relativeTarget.startsWith("../")) return "";
|
|
320
|
+
return directoryPath(relativeTarget);
|
|
321
|
+
}
|
|
322
|
+
|
|
293
323
|
function parseImportSpecifiers(
|
|
294
324
|
source: string,
|
|
295
325
|
): Array<{ importPath: string; kind: ImportKind; offset: number }> {
|
|
@@ -590,11 +620,12 @@ function resolveImport(
|
|
|
590
620
|
importPath: string,
|
|
591
621
|
importerFile: string,
|
|
592
622
|
files: Set<string>,
|
|
623
|
+
atAliasRoot: string,
|
|
593
624
|
): Pick<ImportReference, "packageName" | "resolvedPath"> {
|
|
594
625
|
if (importPath.startsWith("@/")) {
|
|
595
626
|
return {
|
|
596
627
|
resolvedPath: resolveLocalImportPath(
|
|
597
|
-
stripKnownExtension(importPath.slice(2)),
|
|
628
|
+
stripKnownExtension(path.posix.join(atAliasRoot, importPath.slice(2))),
|
|
598
629
|
files,
|
|
599
630
|
),
|
|
600
631
|
};
|
|
@@ -1171,7 +1202,7 @@ function classifyPath(
|
|
|
1171
1202
|
if (
|
|
1172
1203
|
isUnder(
|
|
1173
1204
|
normalizedPath,
|
|
1174
|
-
directoryPath(path.dirname(config.paths.
|
|
1205
|
+
directoryPath(path.dirname(config.paths.portWiring)),
|
|
1175
1206
|
)
|
|
1176
1207
|
) {
|
|
1177
1208
|
return "infra";
|
package/src/make/inbox.ts
CHANGED
|
@@ -265,7 +265,7 @@ export async function updateInboxWiring(
|
|
|
265
265
|
options,
|
|
266
266
|
)
|
|
267
267
|
) {
|
|
268
|
-
updated.add(config.paths.
|
|
268
|
+
updated.add(config.paths.portWiring);
|
|
269
269
|
}
|
|
270
270
|
if (
|
|
271
271
|
await updateDrizzleSchemaIndexExports(
|
|
@@ -1119,7 +1119,7 @@ import {
|
|
|
1119
1119
|
\tcreateTestPorts,
|
|
1120
1120
|
} from "@beignet/core/testing";
|
|
1121
1121
|
import type { AppContext } from "${aliasModule(config.paths.appContext)}";
|
|
1122
|
-
import {
|
|
1122
|
+
import { initialPorts } from "${aliasModule(config.paths.portWiring)}";
|
|
1123
1123
|
import type { AppTransactionPorts } from "${aliasModule(config.paths.ports)}";
|
|
1124
1124
|
import { getUnreadCountUseCase } from "../use-cases/get-unread-count";
|
|
1125
1125
|
import { listInboxUseCase } from "../use-cases/list-inbox";
|
|
@@ -1140,9 +1140,9 @@ function createHarness(
|
|
|
1140
1140
|
\tconst inbox = options.inbox ?? createMemoryInboxRepository();
|
|
1141
1141
|
\tconst actor = options.actor ?? createTestUserActor("user_test");
|
|
1142
1142
|
\tconst fixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
|
|
1143
|
-
\t\tbase:
|
|
1143
|
+
\t\tbase: initialPorts,
|
|
1144
1144
|
\t\toverrides: {
|
|
1145
|
-
\t\t\tgate:
|
|
1145
|
+
\t\t\tgate: initialPorts.gate,
|
|
1146
1146
|
\t\t\tinbox,
|
|
1147
1147
|
\t\t},
|
|
1148
1148
|
\t});
|
|
@@ -1305,7 +1305,7 @@ import {
|
|
|
1305
1305
|
\tdefineInboxNotificationChannel,
|
|
1306
1306
|
\ttype InboxNotificationInput,
|
|
1307
1307
|
} from "${channelModule}";
|
|
1308
|
-
import {
|
|
1308
|
+
import { initialPorts } from "${aliasModule(config.paths.portWiring)}";
|
|
1309
1309
|
import { defineNotification } from "${aliasModule(config.paths.notificationsBuilder)}";
|
|
1310
1310
|
import type { AppTransactionPorts } from "${aliasModule(config.paths.ports)}";
|
|
1311
1311
|
import { createMemoryInboxRepository } from "./factories/inbox-notification";
|
|
@@ -1340,9 +1340,9 @@ const skippingNotification = defineNotification("inbox.channel-skip-test", {
|
|
|
1340
1340
|
function createHarness() {
|
|
1341
1341
|
\tconst inbox = createMemoryInboxRepository();
|
|
1342
1342
|
\tconst fixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
|
|
1343
|
-
\t\tbase:
|
|
1343
|
+
\t\tbase: initialPorts,
|
|
1344
1344
|
\t\toverrides: {
|
|
1345
|
-
\t\t\tgate:
|
|
1345
|
+
\t\t\tgate: initialPorts.gate,
|
|
1346
1346
|
\t\t\tinbox,
|
|
1347
1347
|
\t\t},
|
|
1348
1348
|
\t});
|
package/src/make/payments.ts
CHANGED
|
@@ -82,7 +82,13 @@ export async function makePayments(
|
|
|
82
82
|
|
|
83
83
|
const database = await detectResourceDatabase(targetDir, config);
|
|
84
84
|
const ui = await detectUiShell(targetDir, config);
|
|
85
|
-
const
|
|
85
|
+
const tenantScoped = await fileExists(
|
|
86
|
+
path.join(targetDir, config.paths.features, "workspaces/contracts.ts"),
|
|
87
|
+
);
|
|
88
|
+
const generatedFiles = paymentsFiles(config, database, {
|
|
89
|
+
ui,
|
|
90
|
+
idempotencyScope: tenantScoped ? "actor-tenant" : "actor",
|
|
91
|
+
});
|
|
86
92
|
const plannedFiles = await planGeneratedFiles(targetDir, generatedFiles, {
|
|
87
93
|
force: Boolean(options.force),
|
|
88
94
|
});
|
|
@@ -246,7 +252,7 @@ export async function updatePaymentsWiring(
|
|
|
246
252
|
}
|
|
247
253
|
if (await updatePaymentsEntitlementsPort(targetDir, config, options)) {
|
|
248
254
|
updated.add(config.paths.ports);
|
|
249
|
-
updated.add(config.paths.
|
|
255
|
+
updated.add(config.paths.portWiring);
|
|
250
256
|
}
|
|
251
257
|
if (
|
|
252
258
|
await updateInfrastructureDeferredPorts(
|
|
@@ -256,7 +262,7 @@ export async function updatePaymentsWiring(
|
|
|
256
262
|
options,
|
|
257
263
|
)
|
|
258
264
|
) {
|
|
259
|
-
updated.add(config.paths.
|
|
265
|
+
updated.add(config.paths.portWiring);
|
|
260
266
|
}
|
|
261
267
|
if (
|
|
262
268
|
await updateDrizzleSchemaIndexExports(
|
|
@@ -354,10 +360,7 @@ export async function updatePaymentsEntitlementsPort(
|
|
|
354
360
|
if (!options.dryRun) await writeFile(portsFilePath, portsNext);
|
|
355
361
|
}
|
|
356
362
|
|
|
357
|
-
const infrastructureFilePath = path.join(
|
|
358
|
-
targetDir,
|
|
359
|
-
config.paths.infrastructurePorts,
|
|
360
|
-
);
|
|
363
|
+
const infrastructureFilePath = path.join(targetDir, config.paths.portWiring);
|
|
361
364
|
const infrastructureOriginal = await readFile(infrastructureFilePath, "utf8");
|
|
362
365
|
const infrastructureNext = appendDeferredPortKey(
|
|
363
366
|
infrastructureOriginal,
|
|
@@ -368,7 +371,7 @@ export async function updatePaymentsEntitlementsPort(
|
|
|
368
371
|
!/["']entitlements["']/.test(infrastructureOriginal)
|
|
369
372
|
) {
|
|
370
373
|
throw new Error(
|
|
371
|
-
`Could not find the deferred port list in ${config.paths.
|
|
374
|
+
`Could not find the deferred port list in ${config.paths.portWiring}. Add "entitlements" manually, or restore the generated port wiring file before running make payments.`,
|
|
372
375
|
);
|
|
373
376
|
}
|
|
374
377
|
if (infrastructureNext !== infrastructureOriginal) {
|
|
@@ -558,7 +561,10 @@ export async function updateBillingSettingsNav(
|
|
|
558
561
|
export function paymentsFiles(
|
|
559
562
|
config: ResolvedBeignetConfig,
|
|
560
563
|
database: DatabaseName,
|
|
561
|
-
options: {
|
|
564
|
+
options: {
|
|
565
|
+
ui: boolean;
|
|
566
|
+
idempotencyScope?: "actor" | "actor-tenant";
|
|
567
|
+
} = { ui: false },
|
|
562
568
|
): GeneratedFile[] {
|
|
563
569
|
const uiFiles: GeneratedFile[] = options.ui
|
|
564
570
|
? [
|
|
@@ -611,7 +617,7 @@ export function paymentsFiles(
|
|
|
611
617
|
},
|
|
612
618
|
{
|
|
613
619
|
path: path.join(config.paths.features, "billing/contracts.ts"),
|
|
614
|
-
content: billingContractsFile(),
|
|
620
|
+
content: billingContractsFile(options.idempotencyScope ?? "actor"),
|
|
615
621
|
},
|
|
616
622
|
{
|
|
617
623
|
path: path.join(config.paths.features, "billing/use-cases/index.ts"),
|
|
@@ -846,7 +852,9 @@ export function isBillingAccountActive(
|
|
|
846
852
|
`;
|
|
847
853
|
}
|
|
848
854
|
|
|
849
|
-
export function billingContractsFile(
|
|
855
|
+
export function billingContractsFile(
|
|
856
|
+
idempotencyScope: "actor" | "actor-tenant" = "actor",
|
|
857
|
+
): string {
|
|
850
858
|
return `import { defineContractGroup } from "@beignet/core/contracts";
|
|
851
859
|
import { z } from "zod";
|
|
852
860
|
import {
|
|
@@ -883,7 +891,7 @@ export const createCheckoutSession = checkoutBilling
|
|
|
883
891
|
\t\tidempotency: {
|
|
884
892
|
\t\t\trequired: true,
|
|
885
893
|
\t\t\theader: "idempotency-key",
|
|
886
|
-
\t\t\tscope: "
|
|
894
|
+
\t\t\tscope: "${idempotencyScope}",
|
|
887
895
|
\t\t\tttlSec: 86_400,
|
|
888
896
|
\t\t},
|
|
889
897
|
\t})
|
|
@@ -1410,7 +1418,7 @@ import {
|
|
|
1410
1418
|
\tcreateTestPorts,
|
|
1411
1419
|
} from "@beignet/core/testing";
|
|
1412
1420
|
import type { AppContext } from "${aliasModule(config.paths.appContext)}";
|
|
1413
|
-
import {
|
|
1421
|
+
import { initialPorts } from "@/infra/port-wiring";
|
|
1414
1422
|
import type { AppTransactionPorts } from "@/ports";
|
|
1415
1423
|
import { createBillingEntitlements } from "../entitlements";
|
|
1416
1424
|
import type { BillingAccount, BillingRepository } from "../ports";
|
|
@@ -1451,11 +1459,11 @@ function createHarness(options: { actor?: AppContext["actor"] } = {}) {
|
|
|
1451
1459
|
\tconst billing = createMemoryBillingRepository();
|
|
1452
1460
|
\tconst actor = options.actor ?? createTestUserActor("user_test");
|
|
1453
1461
|
\tconst fixture = createTestPorts<AppContext["ports"], AppTransactionPorts>({
|
|
1454
|
-
\t\tbase:
|
|
1462
|
+
\t\tbase: initialPorts,
|
|
1455
1463
|
\t\toverrides: {
|
|
1456
1464
|
\t\t\tbilling,
|
|
1457
1465
|
\t\t\tentitlements: createBillingEntitlements(billing),
|
|
1458
|
-
\t\t\tgate:
|
|
1466
|
+
\t\t\tgate: initialPorts.gate,
|
|
1459
1467
|
\t\t},
|
|
1460
1468
|
\t});
|
|
1461
1469
|
\tconst createContext = createTestContextFactory<
|
package/src/make/shared.ts
CHANGED
|
@@ -273,7 +273,7 @@ export type FileChangeSet = {
|
|
|
273
273
|
* A provider-backed app port a generator depends on.
|
|
274
274
|
*
|
|
275
275
|
* Generated code that publishes events or sends notifications needs the
|
|
276
|
-
* matching port in `AppPorts`, the key deferred in `infra/
|
|
276
|
+
* matching port in `AppPorts`, the key deferred in `infra/port-wiring.ts`, and a
|
|
277
277
|
* dev-default provider registered in `server/providers.ts`. Wiring is
|
|
278
278
|
* generation-time by design: the starter stays lean and each generator wires
|
|
279
279
|
* its own dependencies.
|
|
@@ -434,8 +434,13 @@ export function wireListenersProviderSource(
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
if (!new RegExp(`\\bconst\\s+${providerName}\\b`).test(next)) {
|
|
437
|
+
next = addNamedImport(
|
|
438
|
+
next,
|
|
439
|
+
"tryReportException",
|
|
440
|
+
"@beignet/core/error-reporting",
|
|
441
|
+
);
|
|
437
442
|
const providerDefinition = `const ${providerName} = createProvider<
|
|
438
|
-
\tPick<AppPorts, "eventBus" | "logger">,
|
|
443
|
+
\tPick<AppPorts, "errorReporter" | "eventBus" | "logger">,
|
|
439
444
|
\tAppContext,
|
|
440
445
|
\tAppServiceContextInput
|
|
441
446
|
>()({
|
|
@@ -451,6 +456,20 @@ export function wireListenersProviderSource(
|
|
|
451
456
|
\t\t\t\t\terror,
|
|
452
457
|
\t\t\t\t\tlistenerName: listener.name,
|
|
453
458
|
\t\t\t\t});
|
|
459
|
+
\t\t\t\tvoid tryReportException({
|
|
460
|
+
\t\t\t\t\treporter: ports.errorReporter,
|
|
461
|
+
\t\t\t\t\terror,
|
|
462
|
+
\t\t\t\t\treportOptions: {
|
|
463
|
+
\t\t\t\t\t\tlevel: "error",
|
|
464
|
+
\t\t\t\t\t\tmechanism: "beignet.listener",
|
|
465
|
+
\t\t\t\t\t\thandled: false,
|
|
466
|
+
\t\t\t\t\t\ttags: {
|
|
467
|
+
\t\t\t\t\t\t\t"beignet.kind": "listener",
|
|
468
|
+
\t\t\t\t\t\t\t"beignet.listener": listener.name,
|
|
469
|
+
\t\t\t\t\t\t},
|
|
470
|
+
\t\t\t\t\t\tcontexts: { listener: { name: listener.name } },
|
|
471
|
+
\t\t\t\t\t},
|
|
472
|
+
\t\t\t\t});
|
|
454
473
|
\t\t\t},
|
|
455
474
|
\t\t});
|
|
456
475
|
|
|
@@ -496,7 +515,7 @@ export async function wirePortProviders(
|
|
|
496
515
|
options: { dryRun: boolean },
|
|
497
516
|
): Promise<{ updatedFiles: string[] }> {
|
|
498
517
|
const portsFile = config.paths.ports;
|
|
499
|
-
const infrastructureFile = config.paths.
|
|
518
|
+
const infrastructureFile = config.paths.portWiring;
|
|
500
519
|
const providersFile = providersFilePath(config);
|
|
501
520
|
const manualFor = (wiring: PortProviderWiring) =>
|
|
502
521
|
`Wire the ${wiring.portKey} port manually: add ${wiring.portKey}: ${wiring.portType}; to AppPorts in ${portsFile}, defer "${wiring.portKey}" in ${infrastructureFile}, and register ${wiring.providerFactory}() from ${wiring.providerModule} in ${providersFile}.`;
|
|
@@ -536,7 +555,7 @@ export async function wirePortProviders(
|
|
|
536
555
|
);
|
|
537
556
|
if (infrastructureOriginal === undefined) {
|
|
538
557
|
throw new Error(
|
|
539
|
-
`Could not find the generated
|
|
558
|
+
`Could not find the generated port wiring file ${infrastructureFile}. ${manualFor(active[0])}`,
|
|
540
559
|
);
|
|
541
560
|
}
|
|
542
561
|
|
|
@@ -548,7 +567,7 @@ export async function wirePortProviders(
|
|
|
548
567
|
!new RegExp(`["']${wiring.portKey}["']`).test(infrastructureNext)
|
|
549
568
|
) {
|
|
550
569
|
throw new Error(
|
|
551
|
-
`Could not find the deferred port list in ${infrastructureFile}. ${manualFor(wiring)} Or restore the generated
|
|
570
|
+
`Could not find the deferred port list in ${infrastructureFile}. ${manualFor(wiring)} Or restore the generated port wiring file before running beignet ${wiring.command}.`,
|
|
552
571
|
);
|
|
553
572
|
}
|
|
554
573
|
infrastructureNext = appended;
|
|
@@ -651,7 +670,7 @@ export async function assertStandardApp(
|
|
|
651
670
|
): Promise<void> {
|
|
652
671
|
const requiredFiles = [
|
|
653
672
|
config.paths.appContext,
|
|
654
|
-
config.paths.
|
|
673
|
+
config.paths.portWiring,
|
|
655
674
|
config.paths.ports,
|
|
656
675
|
config.paths.server,
|
|
657
676
|
config.paths.useCaseBuilder,
|
|
@@ -688,7 +707,7 @@ export async function assertTestApp(
|
|
|
688
707
|
): Promise<void> {
|
|
689
708
|
const requiredFiles = [
|
|
690
709
|
config.paths.appContext,
|
|
691
|
-
config.paths.
|
|
710
|
+
config.paths.portWiring,
|
|
692
711
|
useCaseFilePath(names, config),
|
|
693
712
|
];
|
|
694
713
|
|
|
@@ -707,7 +726,7 @@ export async function assertPortApp(
|
|
|
707
726
|
targetDir: string,
|
|
708
727
|
config: ResolvedBeignetConfig,
|
|
709
728
|
): Promise<void> {
|
|
710
|
-
const requiredFiles = [config.paths.ports, config.paths.
|
|
729
|
+
const requiredFiles = [config.paths.ports, config.paths.portWiring];
|
|
711
730
|
|
|
712
731
|
for (const file of requiredFiles) {
|
|
713
732
|
try {
|
|
@@ -725,10 +744,7 @@ export async function assertAdapterApp(
|
|
|
725
744
|
names: PortNames,
|
|
726
745
|
config: ResolvedBeignetConfig,
|
|
727
746
|
): Promise<void> {
|
|
728
|
-
const requiredFiles = [
|
|
729
|
-
config.paths.infrastructurePorts,
|
|
730
|
-
portFilePath(names, config),
|
|
731
|
-
];
|
|
747
|
+
const requiredFiles = [config.paths.portWiring, portFilePath(names, config)];
|
|
732
748
|
|
|
733
749
|
for (const file of requiredFiles) {
|
|
734
750
|
try {
|
|
@@ -740,16 +756,16 @@ export async function assertAdapterApp(
|
|
|
740
756
|
}
|
|
741
757
|
}
|
|
742
758
|
|
|
743
|
-
const
|
|
744
|
-
path.join(targetDir, config.paths.
|
|
759
|
+
const portWiring = await readFile(
|
|
760
|
+
path.join(targetDir, config.paths.portWiring),
|
|
745
761
|
"utf8",
|
|
746
762
|
);
|
|
747
763
|
if (
|
|
748
|
-
!
|
|
749
|
-
!
|
|
764
|
+
!portWiring.includes(portStubEntry(names)) &&
|
|
765
|
+
!portWiring.includes(adapterEntry(names))
|
|
750
766
|
) {
|
|
751
767
|
throw new Error(
|
|
752
|
-
`Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.
|
|
768
|
+
`Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.portWiring}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
|
|
753
769
|
);
|
|
754
770
|
}
|
|
755
771
|
}
|
|
@@ -822,7 +838,7 @@ export async function assertServerRuntimeApp(
|
|
|
822
838
|
const requiredFiles = [
|
|
823
839
|
config.paths.appContext,
|
|
824
840
|
config.paths.server,
|
|
825
|
-
config.paths.
|
|
841
|
+
config.paths.portWiring,
|
|
826
842
|
];
|
|
827
843
|
|
|
828
844
|
for (const file of requiredFiles) {
|
|
@@ -1254,11 +1270,11 @@ export async function updateInfrastructurePortStub(
|
|
|
1254
1270
|
config: ResolvedBeignetConfig,
|
|
1255
1271
|
options: { dryRun: boolean },
|
|
1256
1272
|
): Promise<boolean> {
|
|
1257
|
-
const filePath = path.join(targetDir, config.paths.
|
|
1273
|
+
const filePath = path.join(targetDir, config.paths.portWiring);
|
|
1258
1274
|
const original = await readFile(filePath, "utf8");
|
|
1259
1275
|
let next = original;
|
|
1260
1276
|
const portPath = portFilePath(names, config);
|
|
1261
|
-
const importLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.
|
|
1277
|
+
const importLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.portWiring, portPath)}";`;
|
|
1262
1278
|
|
|
1263
1279
|
if (!next.includes(importLine)) {
|
|
1264
1280
|
next = insertAfterImports(next, importLine);
|
|
@@ -1273,7 +1289,7 @@ export async function updateInfrastructurePortStub(
|
|
|
1273
1289
|
next = insertDefinePortsProperty(
|
|
1274
1290
|
next,
|
|
1275
1291
|
entry.trim(),
|
|
1276
|
-
`Could not find definePorts({ in ${config.paths.
|
|
1292
|
+
`Could not find definePorts({ in ${config.paths.portWiring}. Add the ${names.interfaceName} stub manually, or restore the generated port wiring file before running make port.`,
|
|
1277
1293
|
);
|
|
1278
1294
|
}
|
|
1279
1295
|
|
|
@@ -1288,18 +1304,18 @@ export async function updateInfrastructureAdapterWiring(
|
|
|
1288
1304
|
config: ResolvedBeignetConfig,
|
|
1289
1305
|
options: { dryRun: boolean },
|
|
1290
1306
|
): Promise<boolean> {
|
|
1291
|
-
const filePath = path.join(targetDir, config.paths.
|
|
1307
|
+
const filePath = path.join(targetDir, config.paths.portWiring);
|
|
1292
1308
|
const original = await readFile(filePath, "utf8");
|
|
1293
1309
|
let next = original;
|
|
1294
1310
|
const adapterPath = adapterFilePath(names, config);
|
|
1295
|
-
const adapterImportLine = `import { ${names.adapterFactoryName} } from "${relativeModule(config.paths.
|
|
1311
|
+
const adapterImportLine = `import { ${names.adapterFactoryName} } from "${relativeModule(config.paths.portWiring, adapterPath)}";`;
|
|
1296
1312
|
|
|
1297
1313
|
if (!next.includes(adapterImportLine)) {
|
|
1298
1314
|
next = insertAfterImports(next, adapterImportLine);
|
|
1299
1315
|
}
|
|
1300
1316
|
|
|
1301
1317
|
const portPath = portFilePath(names, config);
|
|
1302
|
-
const portImportLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.
|
|
1318
|
+
const portImportLine = `import type { ${names.interfaceName} } from "${relativeModule(config.paths.portWiring, portPath)}";`;
|
|
1303
1319
|
if (next.includes(portImportLine)) {
|
|
1304
1320
|
next = next.replace(`${portImportLine}\n`, "");
|
|
1305
1321
|
}
|
|
@@ -1310,7 +1326,7 @@ export async function updateInfrastructureAdapterWiring(
|
|
|
1310
1326
|
next = next.replace(stub, adapter);
|
|
1311
1327
|
} else if (!next.includes(adapter)) {
|
|
1312
1328
|
throw new Error(
|
|
1313
|
-
`Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.
|
|
1329
|
+
`Could not find the generated ${names.interfaceName} infrastructure stub in ${config.paths.portWiring}. Wire ${names.adapterFactoryName}() manually or restore the generated stub before running make adapter.`,
|
|
1314
1330
|
);
|
|
1315
1331
|
}
|
|
1316
1332
|
|
|
@@ -1325,15 +1341,15 @@ export async function updateInfrastructurePorts(
|
|
|
1325
1341
|
config: ResolvedBeignetConfig,
|
|
1326
1342
|
options: { dryRun: boolean },
|
|
1327
1343
|
): Promise<boolean> {
|
|
1328
|
-
const filePath = path.join(targetDir, config.paths.
|
|
1344
|
+
const filePath = path.join(targetDir, config.paths.portWiring);
|
|
1329
1345
|
const original = await readFile(filePath, "utf8");
|
|
1330
1346
|
let next = original;
|
|
1331
1347
|
const repositoryPath = path.join(
|
|
1332
|
-
path.dirname(config.paths.
|
|
1348
|
+
path.dirname(config.paths.portWiring),
|
|
1333
1349
|
names.pluralKebab,
|
|
1334
1350
|
`in-memory-${names.singularKebab}-repository.ts`,
|
|
1335
1351
|
);
|
|
1336
|
-
const importLine = `import { createInMemory${names.singularPascal}Repository } from "${relativeModule(config.paths.
|
|
1352
|
+
const importLine = `import { createInMemory${names.singularPascal}Repository } from "${relativeModule(config.paths.portWiring, repositoryPath)}";`;
|
|
1337
1353
|
|
|
1338
1354
|
if (!next.includes(importLine)) {
|
|
1339
1355
|
next = insertAfterImports(next, importLine);
|
|
@@ -1343,9 +1359,9 @@ export async function updateInfrastructurePorts(
|
|
|
1343
1359
|
if (!next.includes(repositoryConst)) {
|
|
1344
1360
|
next = replaceRequired(
|
|
1345
1361
|
next,
|
|
1346
|
-
/(\nexport const
|
|
1362
|
+
/(\nexport const initialPorts = definePorts(?:<[^>]+>)?\((?:\)\()?\{)/,
|
|
1347
1363
|
`\n${repositoryConst}\n$1`,
|
|
1348
|
-
`Could not find definePorts({ in ${config.paths.
|
|
1364
|
+
`Could not find definePorts({ in ${config.paths.portWiring}. Add ${repositoryConst} manually, or restore the generated port wiring file before running make resource.`,
|
|
1349
1365
|
);
|
|
1350
1366
|
}
|
|
1351
1367
|
|
|
@@ -1358,7 +1374,7 @@ export async function updateInfrastructurePorts(
|
|
|
1358
1374
|
next = insertDefinePortsProperty(
|
|
1359
1375
|
next,
|
|
1360
1376
|
entry.trim(),
|
|
1361
|
-
`Could not find definePorts({ in ${config.paths.
|
|
1377
|
+
`Could not find definePorts({ in ${config.paths.portWiring}. Add ${entry.trim()} to initialPorts manually, or restore the generated port wiring file before running make resource.`,
|
|
1362
1378
|
);
|
|
1363
1379
|
}
|
|
1364
1380
|
|
|
@@ -1371,7 +1387,7 @@ export async function updateInfrastructurePorts(
|
|
|
1371
1387
|
next = insertNoopUnitOfWorkProperty(
|
|
1372
1388
|
next,
|
|
1373
1389
|
transactionEntry.trim(),
|
|
1374
|
-
`Could not find the generated Unit of Work port list in ${config.paths.
|
|
1390
|
+
`Could not find the generated Unit of Work port list in ${config.paths.portWiring}. Add ${transactionEntry.trim()} manually, or restore the generated port wiring file before running make resource.`,
|
|
1375
1391
|
);
|
|
1376
1392
|
}
|
|
1377
1393
|
|
|
@@ -1386,7 +1402,7 @@ export async function updateInfrastructureGatePolicies(
|
|
|
1386
1402
|
config: ResolvedBeignetConfig,
|
|
1387
1403
|
options: { dryRun: boolean },
|
|
1388
1404
|
): Promise<boolean> {
|
|
1389
|
-
const filePath = path.join(targetDir, config.paths.
|
|
1405
|
+
const filePath = path.join(targetDir, config.paths.portWiring);
|
|
1390
1406
|
const original = await readFile(filePath, "utf8");
|
|
1391
1407
|
let next = original;
|
|
1392
1408
|
const policyPath = path.join(
|
|
@@ -1394,7 +1410,7 @@ export async function updateInfrastructureGatePolicies(
|
|
|
1394
1410
|
names.pluralKebab,
|
|
1395
1411
|
"policy.ts",
|
|
1396
1412
|
);
|
|
1397
|
-
const importLine = `import { ${names.singularCamel}Policy } from "${relativeModule(config.paths.
|
|
1413
|
+
const importLine = `import { ${names.singularCamel}Policy } from "${relativeModule(config.paths.portWiring, policyPath)}";`;
|
|
1398
1414
|
|
|
1399
1415
|
if (!next.includes(importLine)) {
|
|
1400
1416
|
next = insertAfterImports(next, importLine);
|
|
@@ -1403,7 +1419,7 @@ export async function updateInfrastructureGatePolicies(
|
|
|
1403
1419
|
next = appendCreateGatePolicy(
|
|
1404
1420
|
next,
|
|
1405
1421
|
`${names.singularCamel}Policy`,
|
|
1406
|
-
`Could not find createGate({ policies: [...] }) in ${config.paths.
|
|
1422
|
+
`Could not find createGate({ policies: [...] }) in ${config.paths.portWiring}. Add ${names.singularCamel}Policy to the gate policies manually, or restore the generated port wiring file before running make resource --auth.`,
|
|
1407
1423
|
);
|
|
1408
1424
|
|
|
1409
1425
|
if (next === original) return false;
|
|
@@ -1713,7 +1729,7 @@ export async function updateInfrastructureDeferredPorts(
|
|
|
1713
1729
|
config: ResolvedBeignetConfig,
|
|
1714
1730
|
options: { dryRun: boolean },
|
|
1715
1731
|
): Promise<boolean> {
|
|
1716
|
-
const filePath = path.join(targetDir, config.paths.
|
|
1732
|
+
const filePath = path.join(targetDir, config.paths.portWiring);
|
|
1717
1733
|
const original = await readFile(filePath, "utf8");
|
|
1718
1734
|
// Drizzle resources are provided by the app database provider at startup,
|
|
1719
1735
|
// so deferred-form ports files defer the new repository key. Identity-form
|
|
@@ -1745,11 +1761,11 @@ export async function updateServerTransactionPorts(
|
|
|
1745
1761
|
|
|
1746
1762
|
if (!/\bappPorts\b/.test(next)) {
|
|
1747
1763
|
throw new Error(
|
|
1748
|
-
`Could not find
|
|
1764
|
+
`Could not find initialPorts in ${config.paths.server}. Add ${names.pluralCamel}: initialPorts.${names.pluralCamel} to createTransactionPorts manually, or restore the generated server file before running make resource.`,
|
|
1749
1765
|
);
|
|
1750
1766
|
}
|
|
1751
1767
|
|
|
1752
|
-
const entry = `\t\t\t\t\t\t${names.pluralCamel}:
|
|
1768
|
+
const entry = `\t\t\t\t\t\t${names.pluralCamel}: initialPorts.${names.pluralCamel},`;
|
|
1753
1769
|
next = insertDrizzleTransactionPortsProperty(
|
|
1754
1770
|
next,
|
|
1755
1771
|
entry.trim(),
|
|
@@ -3048,14 +3064,14 @@ export function adapterFilePath(
|
|
|
3048
3064
|
config: ResolvedBeignetConfig,
|
|
3049
3065
|
): string {
|
|
3050
3066
|
return path.join(
|
|
3051
|
-
path.dirname(config.paths.
|
|
3067
|
+
path.dirname(config.paths.portWiring),
|
|
3052
3068
|
names.kebab,
|
|
3053
3069
|
`${names.kebab}-adapter.ts`,
|
|
3054
3070
|
);
|
|
3055
3071
|
}
|
|
3056
3072
|
|
|
3057
3073
|
export function infrastructureDir(config: ResolvedBeignetConfig): string {
|
|
3058
|
-
return path.dirname(config.paths.
|
|
3074
|
+
return path.dirname(config.paths.portWiring);
|
|
3059
3075
|
}
|
|
3060
3076
|
|
|
3061
3077
|
export function drizzleSchemaIndexPath(config: ResolvedBeignetConfig): string {
|