@beignet/cli 0.0.32 → 0.0.33
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 +49 -0
- package/README.md +26 -16
- package/dist/choices.d.ts +3 -3
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +2 -0
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/db.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +279 -1
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +797 -58
- package/dist/inspect.js.map +1 -1
- package/dist/make/inbox.d.ts.map +1 -1
- package/dist/make/inbox.js +5 -3
- package/dist/make/inbox.js.map +1 -1
- package/dist/make/payments.d.ts.map +1 -1
- package/dist/make/payments.js +58 -29
- package/dist/make/payments.js.map +1 -1
- package/dist/make/shared.d.ts +11 -2
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +44 -10
- package/dist/make/shared.js.map +1 -1
- package/dist/make/tenancy.d.ts.map +1 -1
- package/dist/make/tenancy.js +6 -4
- package/dist/make/tenancy.js.map +1 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +129 -55
- package/dist/make.js.map +1 -1
- package/dist/outbox.d.ts +114 -1
- package/dist/outbox.d.ts.map +1 -1
- package/dist/outbox.js +190 -0
- package/dist/outbox.js.map +1 -1
- package/dist/preflight.js +1 -1
- package/dist/preflight.js.map +1 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +46 -0
- package/dist/provider-add.js.map +1 -1
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +2 -3
- package/dist/task.js.map +1 -1
- package/dist/templates/agents.d.ts.map +1 -1
- package/dist/templates/agents.js +6 -3
- package/dist/templates/agents.js.map +1 -1
- package/dist/templates/base.js +3 -3
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +2 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +6 -1
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +3 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +11 -1
- package/dist/templates/shared.js.map +1 -1
- package/dist/templates/testing.d.ts +5 -0
- package/dist/templates/testing.d.ts.map +1 -0
- package/dist/templates/testing.js +542 -0
- package/dist/templates/testing.js.map +1 -0
- package/dist/templates/todos.js +2 -2
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +12 -4
- package/src/choices.ts +3 -0
- package/src/config.ts +2 -0
- package/src/db.ts +1 -1
- package/src/index.ts +437 -1
- package/src/inspect.ts +1247 -154
- package/src/make/inbox.ts +5 -3
- package/src/make/payments.ts +58 -29
- package/src/make/shared.ts +78 -11
- package/src/make/tenancy.ts +6 -4
- package/src/make.ts +172 -54
- package/src/outbox.ts +363 -0
- package/src/preflight.ts +1 -1
- package/src/provider-add.ts +47 -0
- package/src/task.ts +2 -3
- package/src/templates/agents.ts +6 -3
- package/src/templates/base.ts +3 -3
- package/src/templates/index.ts +2 -0
- package/src/templates/server.ts +6 -1
- package/src/templates/shared.ts +13 -1
- package/src/templates/testing.ts +545 -0
- package/src/templates/todos.ts +2 -2
- package/src/test-helpers/generated-app.ts +10 -6
package/src/make.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
assertUseCaseApp,
|
|
31
31
|
beignetDependencyVersion,
|
|
32
32
|
constantCase,
|
|
33
|
+
createListenersRegistrySource,
|
|
33
34
|
type DatabaseName,
|
|
34
35
|
type DrizzleDialect,
|
|
35
36
|
defineUploadsInitializerInfo,
|
|
@@ -66,6 +67,7 @@ import {
|
|
|
66
67
|
jobFilePath,
|
|
67
68
|
type ListenerNames,
|
|
68
69
|
listenerFilePath,
|
|
70
|
+
listenersFilePath,
|
|
69
71
|
type MakeResult,
|
|
70
72
|
mergeMakeResultInto,
|
|
71
73
|
mergeMakeResults,
|
|
@@ -115,6 +117,7 @@ import {
|
|
|
115
117
|
updateInfrastructureGatePolicies,
|
|
116
118
|
updateInfrastructurePortStub,
|
|
117
119
|
updateInfrastructurePorts,
|
|
120
|
+
updateListenersRegistrySource,
|
|
118
121
|
updateOpenApiRoute,
|
|
119
122
|
updatePackageDependencies,
|
|
120
123
|
updatePackageJson,
|
|
@@ -143,6 +146,7 @@ import {
|
|
|
143
146
|
identifiersFromArrayExpression,
|
|
144
147
|
insertAfterImports,
|
|
145
148
|
} from "./registry-edits.js";
|
|
149
|
+
import { testSupportTemplateFiles } from "./templates/testing.js";
|
|
146
150
|
|
|
147
151
|
export type { MakeFeatureAddon, MakeFeatureRecipe } from "./choices.js";
|
|
148
152
|
export {
|
|
@@ -1240,7 +1244,7 @@ export async function makeSeed(
|
|
|
1240
1244
|
if (
|
|
1241
1245
|
await updatePackageScripts(
|
|
1242
1246
|
targetDir,
|
|
1243
|
-
{ "db:seed": `
|
|
1247
|
+
{ "db:seed": `tsx ${seedEntrypointPath(config)}` },
|
|
1244
1248
|
{ dryRun: Boolean(options.dryRun) },
|
|
1245
1249
|
)
|
|
1246
1250
|
) {
|
|
@@ -1319,6 +1323,9 @@ export async function makeListener(
|
|
|
1319
1323
|
if (!options.skipEventAssert) {
|
|
1320
1324
|
await assertListenerEventExists(targetDir, names.event, config);
|
|
1321
1325
|
}
|
|
1326
|
+
await updateListenerRegistry(targetDir, names, config, {
|
|
1327
|
+
dryRun: true,
|
|
1328
|
+
});
|
|
1322
1329
|
await updateListenerProviderWiring(targetDir, names, config, {
|
|
1323
1330
|
dryRun: true,
|
|
1324
1331
|
});
|
|
@@ -1339,6 +1346,24 @@ export async function makeListener(
|
|
|
1339
1346
|
},
|
|
1340
1347
|
});
|
|
1341
1348
|
|
|
1349
|
+
const registryResult = await updateListenerRegistry(
|
|
1350
|
+
targetDir,
|
|
1351
|
+
names,
|
|
1352
|
+
config,
|
|
1353
|
+
{
|
|
1354
|
+
dryRun: Boolean(options.dryRun),
|
|
1355
|
+
},
|
|
1356
|
+
);
|
|
1357
|
+
if (registryResult === "created")
|
|
1358
|
+
result.createdFiles.push(listenersFilePath(config));
|
|
1359
|
+
if (registryResult === "updated")
|
|
1360
|
+
result.updatedFiles.push(listenersFilePath(config));
|
|
1361
|
+
if (registryResult === "skipped")
|
|
1362
|
+
result.skippedFiles.push(listenersFilePath(config));
|
|
1363
|
+
if (!result.files.includes(listenersFilePath(config))) {
|
|
1364
|
+
result.files.push(listenersFilePath(config));
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1342
1367
|
const providerResult = await updateListenerProviderWiring(
|
|
1343
1368
|
targetDir,
|
|
1344
1369
|
names,
|
|
@@ -1360,6 +1385,49 @@ export async function makeListener(
|
|
|
1360
1385
|
return result;
|
|
1361
1386
|
}
|
|
1362
1387
|
|
|
1388
|
+
async function updateListenerRegistry(
|
|
1389
|
+
targetDir: string,
|
|
1390
|
+
names: ListenerNames,
|
|
1391
|
+
config: ResolvedBeignetConfig,
|
|
1392
|
+
options: { dryRun: boolean },
|
|
1393
|
+
): Promise<WriteGeneratedFileResult> {
|
|
1394
|
+
const file = listenersFilePath(config);
|
|
1395
|
+
const filePath = path.join(targetDir, file);
|
|
1396
|
+
const listenerIndex = featureArtifactIndexFile("listener", names, config);
|
|
1397
|
+
const original = await readOptionalFile(filePath);
|
|
1398
|
+
|
|
1399
|
+
if (original === undefined) {
|
|
1400
|
+
if (!options.dryRun) {
|
|
1401
|
+
await writeGeneratedFile(
|
|
1402
|
+
targetDir,
|
|
1403
|
+
{
|
|
1404
|
+
path: file,
|
|
1405
|
+
content: createListenersRegistrySource({
|
|
1406
|
+
registryName: listenerIndex.registryName,
|
|
1407
|
+
listenerModule: aliasModule(listenerIndex.path),
|
|
1408
|
+
}),
|
|
1409
|
+
},
|
|
1410
|
+
{ force: false, dryRun: false },
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
return "created";
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
const result = updateListenersRegistrySource(original, {
|
|
1417
|
+
registryName: listenerIndex.registryName,
|
|
1418
|
+
listenerModule: aliasModule(listenerIndex.path),
|
|
1419
|
+
});
|
|
1420
|
+
if (result.kind === "missing") {
|
|
1421
|
+
throw new Error(
|
|
1422
|
+
`Could not find the exported listeners array in ${file}. Register ${listenerIndex.registryName} manually, or restore the generated listeners file before running make listener.`,
|
|
1423
|
+
);
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
if (result.kind === "unchanged") return "skipped";
|
|
1427
|
+
if (!options.dryRun) await writeFile(filePath, result.source);
|
|
1428
|
+
return "updated";
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1363
1431
|
async function assertListenerEventExists(
|
|
1364
1432
|
targetDir: string,
|
|
1365
1433
|
names: EventNames,
|
|
@@ -1701,6 +1769,7 @@ async function updateOutboxPorts(
|
|
|
1701
1769
|
const filePath = path.join(targetDir, config.paths.ports);
|
|
1702
1770
|
const original = await readFile(filePath, "utf8");
|
|
1703
1771
|
let next = addNamedTypeImport(original, "OutboxPort", "@beignet/core/outbox");
|
|
1772
|
+
next = addNamedTypeImport(next, "OutboxAdminPort", "@beignet/core/outbox");
|
|
1704
1773
|
if (!typeHasTopLevelProperty(next, "AppPorts", "outbox")) {
|
|
1705
1774
|
next = insertTypeProperty(
|
|
1706
1775
|
next,
|
|
@@ -1709,6 +1778,14 @@ async function updateOutboxPorts(
|
|
|
1709
1778
|
`Could not find AppPorts in ${config.paths.ports}. Add outbox: OutboxPort; manually, or restore the generated ports file before running make outbox.`,
|
|
1710
1779
|
);
|
|
1711
1780
|
}
|
|
1781
|
+
if (!typeHasTopLevelProperty(next, "AppPorts", "outboxAdmin")) {
|
|
1782
|
+
next = insertTypeProperty(
|
|
1783
|
+
next,
|
|
1784
|
+
"AppPorts",
|
|
1785
|
+
"outboxAdmin: OutboxAdminPort;",
|
|
1786
|
+
`Could not find AppPorts in ${config.paths.ports}. Add outboxAdmin: OutboxAdminPort; manually, or restore the generated ports file before running make outbox.`,
|
|
1787
|
+
);
|
|
1788
|
+
}
|
|
1712
1789
|
if (!typeHasTopLevelProperty(next, "AppTransactionPorts", "outbox")) {
|
|
1713
1790
|
next = insertTypeProperty(
|
|
1714
1791
|
next,
|
|
@@ -1730,7 +1807,8 @@ async function updateOutboxInfrastructurePorts(
|
|
|
1730
1807
|
): Promise<boolean> {
|
|
1731
1808
|
const filePath = path.join(targetDir, config.paths.infrastructurePorts);
|
|
1732
1809
|
const original = await readFile(filePath, "utf8");
|
|
1733
|
-
|
|
1810
|
+
let next = appendDeferredPortKey(original, "outbox");
|
|
1811
|
+
next = appendDeferredPortKey(next, "outboxAdmin");
|
|
1734
1812
|
|
|
1735
1813
|
if (next === original) return false;
|
|
1736
1814
|
if (!options.dryRun) await writeFile(filePath, next);
|
|
@@ -1748,13 +1826,20 @@ async function updateOutboxDatabaseProvider(
|
|
|
1748
1826
|
|
|
1749
1827
|
const database = await detectResourceDatabase(targetDir, config);
|
|
1750
1828
|
const factoryName = drizzleOutboxPortFactoryName(database);
|
|
1829
|
+
const adminFactoryName = drizzleOutboxAdminPortFactoryName(database);
|
|
1751
1830
|
const original = await readFile(filePath, "utf8");
|
|
1752
1831
|
let next = addNamedImport(
|
|
1753
1832
|
original,
|
|
1754
1833
|
factoryName,
|
|
1755
1834
|
`@beignet/provider-db-drizzle/${database}`,
|
|
1756
1835
|
);
|
|
1836
|
+
next = addNamedImport(
|
|
1837
|
+
next,
|
|
1838
|
+
adminFactoryName,
|
|
1839
|
+
`@beignet/provider-db-drizzle/${database}`,
|
|
1840
|
+
);
|
|
1757
1841
|
next = appendPickStringLiteralMember(next, "AppPorts", "outbox");
|
|
1842
|
+
next = appendPickStringLiteralMember(next, "AppPorts", "outboxAdmin");
|
|
1758
1843
|
|
|
1759
1844
|
if (!/\bconst\s+outbox\s*=/.test(next)) {
|
|
1760
1845
|
const beforeOutboxConst = next;
|
|
@@ -1769,6 +1854,21 @@ async function updateOutboxDatabaseProvider(
|
|
|
1769
1854
|
}
|
|
1770
1855
|
}
|
|
1771
1856
|
|
|
1857
|
+
if (!/\bconst\s+outboxAdmin\s*=/.test(next)) {
|
|
1858
|
+
const beforeOutboxAdminConst = next;
|
|
1859
|
+
next = beforeOutboxAdminConst.replace(
|
|
1860
|
+
new RegExp(
|
|
1861
|
+
`(\\n\\s*const outbox = ${factoryName}\\((dbPort\\.(?:drizzle|db))\\);)`,
|
|
1862
|
+
),
|
|
1863
|
+
`$1\n\t\tconst outboxAdmin = ${adminFactoryName}($2);`,
|
|
1864
|
+
);
|
|
1865
|
+
if (next === beforeOutboxAdminConst) {
|
|
1866
|
+
throw new Error(
|
|
1867
|
+
`Could not find the outbox port in ${file}. Add const outboxAdmin = ${adminFactoryName}(dbPort.drizzle) manually, or restore the generated database provider before running make outbox.`,
|
|
1868
|
+
);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1772
1872
|
if (!/\boutbox\s*,/.test(next)) {
|
|
1773
1873
|
const withOutboxPort = next.replace(
|
|
1774
1874
|
/(\n\s*idempotency,\n)/,
|
|
@@ -1782,6 +1882,19 @@ async function updateOutboxDatabaseProvider(
|
|
|
1782
1882
|
next = withOutboxPort;
|
|
1783
1883
|
}
|
|
1784
1884
|
|
|
1885
|
+
if (!/\boutboxAdmin\s*,/.test(next)) {
|
|
1886
|
+
const withOutboxAdminPort = next.replace(
|
|
1887
|
+
/(\n\s*outbox,\n)/,
|
|
1888
|
+
`$1\t\t\toutboxAdmin,\n`,
|
|
1889
|
+
);
|
|
1890
|
+
if (withOutboxAdminPort === next) {
|
|
1891
|
+
throw new Error(
|
|
1892
|
+
`Could not find outbox in providedPorts in ${file}. Add outboxAdmin to providedPorts manually, or restore the generated database provider before running make outbox.`,
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
next = withOutboxAdminPort;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1785
1898
|
if (!new RegExp(`outbox:\\s*${factoryName}\\(tx\\)`).test(next)) {
|
|
1786
1899
|
const withTransactionOutbox = next.replace(
|
|
1787
1900
|
/(\n\s*idempotency: createDrizzle[A-Za-z]+IdempotencyPort\(tx\),\n)/,
|
|
@@ -1881,6 +1994,12 @@ function drizzleOutboxPortFactoryName(database: DatabaseName): string {
|
|
|
1881
1994
|
return "createDrizzleSqliteOutboxPort";
|
|
1882
1995
|
}
|
|
1883
1996
|
|
|
1997
|
+
function drizzleOutboxAdminPortFactoryName(database: DatabaseName): string {
|
|
1998
|
+
if (database === "postgres") return "createDrizzlePostgresOutboxAdminPort";
|
|
1999
|
+
if (database === "mysql") return "createDrizzleMysqlOutboxAdminPort";
|
|
2000
|
+
return "createDrizzleSqliteOutboxAdminPort";
|
|
2001
|
+
}
|
|
2002
|
+
|
|
1884
2003
|
async function applyOutboxDrainWiring(
|
|
1885
2004
|
result: MakeResult,
|
|
1886
2005
|
targetDir: string,
|
|
@@ -2514,6 +2633,7 @@ function resourceFiles(
|
|
|
2514
2633
|
const infraDir = infrastructureDir(config);
|
|
2515
2634
|
const featureDir = resourceFeatureDir(names, config);
|
|
2516
2635
|
const files: GeneratedFile[] = [
|
|
2636
|
+
...testSupportTemplateFiles,
|
|
2517
2637
|
{
|
|
2518
2638
|
path: resourceContractFilePath(names, config),
|
|
2519
2639
|
content: contractFile(names, config, mode, options),
|
|
@@ -2670,6 +2790,7 @@ function testFiles(
|
|
|
2670
2790
|
config: ResolvedBeignetConfig,
|
|
2671
2791
|
): GeneratedFile[] {
|
|
2672
2792
|
return [
|
|
2793
|
+
...testSupportTemplateFiles,
|
|
2673
2794
|
{
|
|
2674
2795
|
path: useCaseTestFilePath(names, config),
|
|
2675
2796
|
content: useCaseTestFile(names, config),
|
|
@@ -3470,7 +3591,7 @@ function listUseCaseFile(
|
|
|
3470
3591
|
|
|
3471
3592
|
return `import "@beignet/core/server-only";
|
|
3472
3593
|
import { normalizeCursorPage } from "@beignet/core/pagination";
|
|
3473
|
-
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3594
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3474
3595
|
import {
|
|
3475
3596
|
decode${names.singularPascal}Cursor,
|
|
3476
3597
|
List${names.pluralPascal}InputSchema,
|
|
@@ -3482,7 +3603,7 @@ export const list${names.pluralPascal}UseCase = useCase
|
|
|
3482
3603
|
.input(List${names.pluralPascal}InputSchema)
|
|
3483
3604
|
.output(List${names.pluralPascal}OutputSchema)
|
|
3484
3605
|
.run(async ({ ctx, input }) => {
|
|
3485
|
-
${options.tenant ? `\t\tconst
|
|
3606
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""} const page = normalizeCursorPage(input, {
|
|
3486
3607
|
defaultLimit: 20,
|
|
3487
3608
|
maxLimit: 100,
|
|
3488
3609
|
});
|
|
@@ -3493,7 +3614,7 @@ ${options.tenant ? `\t\tconst tenantId = ctx.tenant?.id;\n\t\tif (!tenantId) {\n
|
|
|
3493
3614
|
name: input.name,
|
|
3494
3615
|
sortBy: input.sortBy,
|
|
3495
3616
|
sortDirection: input.sortDirection,
|
|
3496
|
-
${options.tenant ?
|
|
3617
|
+
}${options.tenant ? ", scope" : ""});
|
|
3497
3618
|
});
|
|
3498
3619
|
`;
|
|
3499
3620
|
}
|
|
@@ -3509,7 +3630,7 @@ function createUseCaseFile(
|
|
|
3509
3630
|
);
|
|
3510
3631
|
|
|
3511
3632
|
return `import "@beignet/core/server-only";
|
|
3512
|
-
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3633
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3513
3634
|
import {
|
|
3514
3635
|
Create${names.singularPascal}InputSchema,
|
|
3515
3636
|
${names.singularPascal}Schema,
|
|
@@ -3521,9 +3642,7 @@ export const create${names.singularPascal}UseCase = useCase
|
|
|
3521
3642
|
.input(Create${names.singularPascal}InputSchema)
|
|
3522
3643
|
.output(${names.singularPascal}Schema)
|
|
3523
3644
|
.run(async ({ ctx, input }) => {
|
|
3524
|
-
${options.tenant ? `\t\tconst
|
|
3525
|
-
...input,
|
|
3526
|
-
${options.tenant ? `\t\t\ttenantId,\n` : ""} });
|
|
3645
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""}${options.auth ? `\t\tawait ctx.gate.authorize("${names.pluralCamel}.create");\n\n` : ""} const ${names.singularCamel} = await ctx.ports.${names.pluralCamel}.create(input${options.tenant ? ", scope" : ""});
|
|
3527
3646
|
${options.events ? `\n\t\tawait ctx.ports.eventBus.publish(${names.singularPascal}Created, {\n\t\t\tid: ${names.singularCamel}.id,\n\t\t});\n` : ""}
|
|
3528
3647
|
return ${names.singularCamel};
|
|
3529
3648
|
});
|
|
@@ -3541,7 +3660,7 @@ function getUseCaseFile(
|
|
|
3541
3660
|
);
|
|
3542
3661
|
|
|
3543
3662
|
return `import "@beignet/core/server-only";
|
|
3544
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3663
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3545
3664
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3546
3665
|
import {
|
|
3547
3666
|
${names.singularPascal}IdInputSchema,
|
|
@@ -3553,7 +3672,7 @@ export const get${names.singularPascal}UseCase = useCase
|
|
|
3553
3672
|
.input(${names.singularPascal}IdInputSchema)
|
|
3554
3673
|
.output(${names.singularPascal}Schema)
|
|
3555
3674
|
.run(async ({ ctx, input }) => {
|
|
3556
|
-
${options.tenant ? `\t\tconst
|
|
3675
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""} const ${names.singularCamel} = await ctx.ports.${names.pluralCamel}.findById(input.id${options.tenant ? ", scope" : ""});
|
|
3557
3676
|
if (!${names.singularCamel}) {
|
|
3558
3677
|
throw appError("${names.singularPascal}NotFound", {
|
|
3559
3678
|
details: { id: input.id },
|
|
@@ -3576,7 +3695,7 @@ function updateUseCaseFile(
|
|
|
3576
3695
|
);
|
|
3577
3696
|
|
|
3578
3697
|
return `import "@beignet/core/server-only";
|
|
3579
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3698
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3580
3699
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3581
3700
|
${options.events ? `import { ${names.singularPascal}Updated } from "../domain/events";\n` : ""}import {
|
|
3582
3701
|
Update${names.singularPascal}InputSchema,
|
|
@@ -3588,7 +3707,7 @@ export const update${names.singularPascal}UseCase = useCase
|
|
|
3588
3707
|
.input(Update${names.singularPascal}InputSchema)
|
|
3589
3708
|
.output(${names.singularPascal}Schema)
|
|
3590
3709
|
.run(async ({ ctx, input }) => {
|
|
3591
|
-
${options.tenant ? `\t\tconst
|
|
3710
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""} const existing = await ctx.ports.${names.pluralCamel}.findById(input.id${options.tenant ? ", scope" : ""});
|
|
3592
3711
|
if (!existing) {
|
|
3593
3712
|
throw appError("${names.singularPascal}NotFound", {
|
|
3594
3713
|
details: { id: input.id },
|
|
@@ -3602,7 +3721,7 @@ ${options.auth ? `\t\tawait ctx.gate.authorize("${names.pluralCamel}.update", ex
|
|
|
3602
3721
|
|
|
3603
3722
|
const ${names.singularCamel} = await ctx.ports.${names.pluralCamel}.update({
|
|
3604
3723
|
...input,
|
|
3605
|
-
${options.tenant ?
|
|
3724
|
+
}${options.tenant ? ", scope" : ""});
|
|
3606
3725
|
if (!${names.singularCamel}) {
|
|
3607
3726
|
throw appError("${names.singularPascal}Conflict", {
|
|
3608
3727
|
details: { id: input.id, expectedVersion: existing.version },
|
|
@@ -3626,7 +3745,7 @@ function deleteUseCaseFile(
|
|
|
3626
3745
|
|
|
3627
3746
|
return `import "@beignet/core/server-only";
|
|
3628
3747
|
import { z } from "zod";
|
|
3629
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3748
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3630
3749
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3631
3750
|
${options.events ? `import { ${names.singularPascal}Deleted } from "../domain/events";\n` : ""}import { ${names.singularPascal}IdInputSchema } from "../schemas";
|
|
3632
3751
|
|
|
@@ -3635,7 +3754,7 @@ export const delete${names.singularPascal}UseCase = useCase
|
|
|
3635
3754
|
.input(${names.singularPascal}IdInputSchema)
|
|
3636
3755
|
.output(z.void())
|
|
3637
3756
|
.run(async ({ ctx, input }) => {
|
|
3638
|
-
${options.tenant ? `\t\tconst
|
|
3757
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""}${options.auth ? `\t\tconst existing = await ctx.ports.${names.pluralCamel}.findById(input.id${options.tenant ? ", scope" : ""});\n\t\tif (!existing) {\n\t\t\tthrow appError("${names.singularPascal}NotFound", {\n\t\t\t\tdetails: { id: input.id },\n\t\t\t});\n\t\t}\n\t\tawait ctx.gate.authorize("${names.pluralCamel}.delete", existing);\n\n` : ""} const deleted = await ctx.ports.${names.pluralCamel}.delete(input.id${options.tenant ? ", scope" : ""});
|
|
3639
3758
|
if (!deleted) {
|
|
3640
3759
|
throw appError("${names.singularPascal}NotFound", {
|
|
3641
3760
|
details: { id: input.id },
|
|
@@ -3675,7 +3794,7 @@ function repositoryPortFile(
|
|
|
3675
3794
|
PageResult,
|
|
3676
3795
|
SortDirection,
|
|
3677
3796
|
} from "@beignet/core/pagination";
|
|
3678
|
-
import type {
|
|
3797
|
+
${options.tenant ? `import type { TenantScope } from "@beignet/core/tenancy";\n` : ""}import type {
|
|
3679
3798
|
Create${names.singularPascal}Input,
|
|
3680
3799
|
${names.singularPascal}Cursor,
|
|
3681
3800
|
${names.singularPascal}SortBy,
|
|
@@ -3689,13 +3808,12 @@ export type List${names.pluralPascal}Query = {
|
|
|
3689
3808
|
name?: string;
|
|
3690
3809
|
sortBy: ${names.singularPascal}SortBy;
|
|
3691
3810
|
sortDirection: SortDirection;
|
|
3692
|
-
|
|
3693
|
-
${options.tenant ? `export type ${names.singularPascal}TenantFilter = { tenantId: string };\nexport type Create${names.singularPascal}RepositoryInput = Create${names.singularPascal}Input & ${names.singularPascal}TenantFilter;\n${mode === "resource" ? `export type Update${names.singularPascal}RepositoryInput = Update${names.singularPascal}Input & ${names.singularPascal}TenantFilter;\n` : ""}` : ""}
|
|
3811
|
+
};
|
|
3694
3812
|
|
|
3695
3813
|
export interface ${names.singularPascal}Repository {
|
|
3696
|
-
list(query: List${names.pluralPascal}Query): Promise<List${names.pluralPascal}Result>;
|
|
3697
|
-
create(input: ${options.tenant ?
|
|
3698
|
-
${mode === "resource" ? ` findById(id: string${options.tenant ?
|
|
3814
|
+
list(query: List${names.pluralPascal}Query${options.tenant ? ", scope: TenantScope" : ""}): Promise<List${names.pluralPascal}Result>;
|
|
3815
|
+
create(input: Create${names.singularPascal}Input${options.tenant ? ", scope: TenantScope" : ""}): Promise<${names.singularPascal}>;
|
|
3816
|
+
${mode === "resource" ? ` findById(id: string${options.tenant ? ", scope: TenantScope" : ""}): Promise<${names.singularPascal} | null>;\n update(input: Update${names.singularPascal}Input${options.tenant ? ", scope: TenantScope" : ""}): Promise<${names.singularPascal} | null>;\n delete(id: string${options.tenant ? ", scope: TenantScope" : ""}): Promise<boolean>;\n` : ""}}
|
|
3699
3817
|
`;
|
|
3700
3818
|
}
|
|
3701
3819
|
|
|
@@ -3729,14 +3847,14 @@ function inMemoryRepositoryFile(
|
|
|
3729
3847
|
? " || existing.deletedAt !== null"
|
|
3730
3848
|
: "";
|
|
3731
3849
|
const deleteImplementation = options.softDelete
|
|
3732
|
-
? `${options.tenant ? `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (!existing || existing.tenantId !==
|
|
3850
|
+
? `${options.tenant ? `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (!existing || existing.tenantId !== tenantId || existing.deletedAt !== null) return false;\n` : `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (!existing || existing.deletedAt !== null) return false;\n`} const now = new Date().toISOString();
|
|
3733
3851
|
${names.pluralCamel}.set(id, { ...existing, deletedAt: now, updatedAt: now });
|
|
3734
3852
|
return true;`
|
|
3735
|
-
: `${options.tenant ? `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (existing?.tenantId !==
|
|
3853
|
+
: `${options.tenant ? `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (existing?.tenantId !== tenantId) return false;\n` : ""} return ${names.pluralCamel}.delete(id);`;
|
|
3736
3854
|
|
|
3737
3855
|
return `import "@beignet/core/server-only";
|
|
3738
3856
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
3739
|
-
import type { ${names.singularPascal}Repository } from "${aliasModule(repositoryPortPath)}";
|
|
3857
|
+
${options.tenant ? `import { tenantScopeId } from "@beignet/core/tenancy";\n` : ""}import type { ${names.singularPascal}Repository } from "${aliasModule(repositoryPortPath)}";
|
|
3740
3858
|
import { encode${names.singularPascal}Cursor } from "${aliasModule(resourceSchemaFilePath(names, config))}";
|
|
3741
3859
|
import type {
|
|
3742
3860
|
Create${names.singularPascal}Input,
|
|
@@ -3804,10 +3922,10 @@ export function createInMemory${names.singularPascal}Repository(
|
|
|
3804
3922
|
);
|
|
3805
3923
|
|
|
3806
3924
|
return {
|
|
3807
|
-
async list(query) {
|
|
3925
|
+
async list(query${options.tenant ? ", scope" : ""}) {
|
|
3808
3926
|
const name = query.name?.toLocaleLowerCase();
|
|
3809
|
-
const all${names.pluralPascal} = Array.from(${names.pluralCamel}.values())
|
|
3810
|
-
${activeFilter}${options.tenant ? `\t\t\t\t.filter((${names.singularCamel}) => ${names.singularCamel}.tenantId ===
|
|
3927
|
+
${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""} const all${names.pluralPascal} = Array.from(${names.pluralCamel}.values())
|
|
3928
|
+
${activeFilter}${options.tenant ? `\t\t\t\t.filter((${names.singularCamel}) => ${names.singularCamel}.tenantId === tenantId)\n` : ""} .filter((${names.singularCamel}) => !name || ${names.singularCamel}.name.toLocaleLowerCase().includes(name))
|
|
3811
3929
|
.sort((left, right) => compare${names.pluralPascal}(left, right, query))
|
|
3812
3930
|
.filter((${names.singularCamel}) => isAfter${names.singularPascal}Cursor(${names.singularCamel}, query));
|
|
3813
3931
|
const pageItems = all${names.pluralPascal}.slice(0, query.page.limit);
|
|
@@ -3822,11 +3940,11 @@ ${activeFilter}${options.tenant ? `\t\t\t\t.filter((${names.singularCamel}) => $
|
|
|
3822
3940
|
nextCursor,
|
|
3823
3941
|
);
|
|
3824
3942
|
},
|
|
3825
|
-
async create(input) {
|
|
3943
|
+
async create(input${options.tenant ? ", scope" : ""}) {
|
|
3826
3944
|
const now = new Date().toISOString();
|
|
3827
|
-
const ${names.singularCamel}: ${names.singularPascal} = {
|
|
3945
|
+
${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""} const ${names.singularCamel}: ${names.singularPascal} = {
|
|
3828
3946
|
id: crypto.randomUUID(),
|
|
3829
|
-
${options.tenant ? `\t\t\t\ttenantId
|
|
3947
|
+
${options.tenant ? `\t\t\t\ttenantId,\n` : ""} name: input.name,
|
|
3830
3948
|
version: 1,
|
|
3831
3949
|
createdAt: now,
|
|
3832
3950
|
updatedAt: now,
|
|
@@ -3836,7 +3954,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
3836
3954
|
${newDeletedAtField} });
|
|
3837
3955
|
return ${names.singularCamel};
|
|
3838
3956
|
},
|
|
3839
|
-
${mode === "resource" ? ` async findById(id: string${options.tenant ? ",
|
|
3957
|
+
${mode === "resource" ? ` async findById(id: string${options.tenant ? ", scope" : ""}) {\n${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""} const ${names.singularCamel} = ${names.pluralCamel}.get(id) ?? null;\n${options.tenant ? `\t\t\tif (${names.singularCamel}?.tenantId !== tenantId) return null;\n` : ""}${findDeletedGuard} return ${names.singularCamel} ? to${names.singularPascal}(${names.singularCamel}) : null;\n },\n async update(input${options.tenant ? ", scope" : ""}) {\n${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""} const existing = ${names.pluralCamel}.get(input.id);\n if (!existing${options.tenant ? " || existing.tenantId !== tenantId" : ""}${updateDeletedGuard} || existing.version !== input.version) return null;\n\n const next: ${mapValueType} = {\n ...existing,\n name: input.name,\n version: existing.version + 1,\n updatedAt: new Date().toISOString(),\n };\n ${names.pluralCamel}.set(input.id, next);\n return to${names.singularPascal}(next);\n },\n async delete(id: string${options.tenant ? ", scope" : ""}) {\n${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""}${deleteImplementation}\n },\n` : ""}
|
|
3840
3958
|
};
|
|
3841
3959
|
}
|
|
3842
3960
|
`;
|
|
@@ -3960,20 +4078,20 @@ function drizzleRepositoryFile(
|
|
|
3960
4078
|
const findWhere = drizzleResourceWhere(names, options, [
|
|
3961
4079
|
`eq(schema.${names.pluralCamel}.id, id)`,
|
|
3962
4080
|
...(options.tenant
|
|
3963
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4081
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3964
4082
|
: []),
|
|
3965
4083
|
]);
|
|
3966
4084
|
const updateWhere = drizzleResourceWhere(names, options, [
|
|
3967
4085
|
`eq(schema.${names.pluralCamel}.id, input.id)`,
|
|
3968
4086
|
`eq(schema.${names.pluralCamel}.version, input.version)`,
|
|
3969
4087
|
...(options.tenant
|
|
3970
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4088
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3971
4089
|
: []),
|
|
3972
4090
|
]);
|
|
3973
4091
|
const deleteWhere = drizzleResourceWhere(names, options, [
|
|
3974
4092
|
`eq(schema.${names.pluralCamel}.id, id)`,
|
|
3975
4093
|
...(options.tenant
|
|
3976
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4094
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3977
4095
|
: []),
|
|
3978
4096
|
]);
|
|
3979
4097
|
const drizzleImports = [
|
|
@@ -3991,7 +4109,7 @@ function drizzleRepositoryFile(
|
|
|
3991
4109
|
// After a version-guarded update succeeds, re-select by identity only —
|
|
3992
4110
|
// matching what `.returning()` yields on the dialects that support it.
|
|
3993
4111
|
const updateReselectWhere = options.tenant
|
|
3994
|
-
? `and(eq(schema.${names.pluralCamel}.id, input.id), eq(schema.${names.pluralCamel}.tenantId,
|
|
4112
|
+
? `and(eq(schema.${names.pluralCamel}.id, input.id), eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope)))`
|
|
3995
4113
|
: `eq(schema.${names.pluralCamel}.id, input.id)`;
|
|
3996
4114
|
// drizzle-orm/mysql2 has no `.returning(...)`; mutations resolve to a
|
|
3997
4115
|
// `[ResultSetHeader, FieldPacket[]]` tuple typed as `unknown` through the
|
|
@@ -4013,13 +4131,13 @@ function affectedRows(result: unknown): number {
|
|
|
4013
4131
|
`
|
|
4014
4132
|
: "";
|
|
4015
4133
|
const createMethod = dialect.supportsReturning
|
|
4016
|
-
? ` async create(input) {
|
|
4134
|
+
? ` async create(input${options.tenant ? ", scope" : ""}) {
|
|
4017
4135
|
const now = new Date().toISOString();
|
|
4018
4136
|
const [row] = await db
|
|
4019
4137
|
.insert(schema.${names.pluralCamel})
|
|
4020
4138
|
.values({
|
|
4021
4139
|
id: crypto.randomUUID(),
|
|
4022
|
-
${options.tenant ? `\t\t\t\t\ttenantId:
|
|
4140
|
+
${options.tenant ? `\t\t\t\t\ttenantId: tenantScopeId(scope),\n` : ""} name: input.name,
|
|
4023
4141
|
version: 1,
|
|
4024
4142
|
createdAt: now,
|
|
4025
4143
|
updatedAt: now,
|
|
@@ -4033,11 +4151,11 @@ ${options.tenant ? `\t\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input
|
|
|
4033
4151
|
return to${names.singularPascal}(row);
|
|
4034
4152
|
},
|
|
4035
4153
|
`
|
|
4036
|
-
: ` async create(input) {
|
|
4154
|
+
: ` async create(input${options.tenant ? ", scope" : ""}) {
|
|
4037
4155
|
const now = new Date().toISOString();
|
|
4038
4156
|
const ${names.singularCamel}: ${names.singularPascal} = {
|
|
4039
4157
|
id: crypto.randomUUID(),
|
|
4040
|
-
${options.tenant ? `\t\t\t\ttenantId:
|
|
4158
|
+
${options.tenant ? `\t\t\t\ttenantId: tenantScopeId(scope),\n` : ""} name: input.name,
|
|
4041
4159
|
version: 1,
|
|
4042
4160
|
createdAt: now,
|
|
4043
4161
|
updatedAt: now,
|
|
@@ -4051,7 +4169,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4051
4169
|
mode !== "resource"
|
|
4052
4170
|
? ""
|
|
4053
4171
|
: dialect.supportsReturning
|
|
4054
|
-
? ` async findById(id: string${options.tenant ? ",
|
|
4172
|
+
? ` async findById(id: string${options.tenant ? ", scope" : ""}) {
|
|
4055
4173
|
const [row] = await db
|
|
4056
4174
|
.select()
|
|
4057
4175
|
.from(schema.${names.pluralCamel})
|
|
@@ -4060,7 +4178,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4060
4178
|
|
|
4061
4179
|
return row ? to${names.singularPascal}(row) : null;
|
|
4062
4180
|
},
|
|
4063
|
-
async update(input) {
|
|
4181
|
+
async update(input${options.tenant ? ", scope" : ""}) {
|
|
4064
4182
|
const [row] = await db
|
|
4065
4183
|
.update(schema.${names.pluralCamel})
|
|
4066
4184
|
.set({
|
|
@@ -4073,12 +4191,12 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4073
4191
|
|
|
4074
4192
|
return row ? to${names.singularPascal}(row) : null;
|
|
4075
4193
|
},
|
|
4076
|
-
async delete(id: string${options.tenant ? ",
|
|
4194
|
+
async delete(id: string${options.tenant ? ", scope" : ""}) {
|
|
4077
4195
|
${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst rows = await db\n\t\t\t\t.update(schema.${names.pluralCamel})\n\t\t\t\t.set({ deletedAt: now, updatedAt: now })\n\t\t\t\t.where(${deleteWhere})\n\t\t\t\t.returning({ id: schema.${names.pluralCamel}.id });\n` : `\t\t\tconst rows = await db\n\t\t\t\t.delete(schema.${names.pluralCamel})\n\t\t\t\t.where(${deleteWhere})\n\t\t\t\t.returning({ id: schema.${names.pluralCamel}.id });\n`}
|
|
4078
4196
|
return rows.length > 0;
|
|
4079
4197
|
},
|
|
4080
4198
|
`
|
|
4081
|
-
: ` async findById(id: string${options.tenant ? ",
|
|
4199
|
+
: ` async findById(id: string${options.tenant ? ", scope" : ""}) {
|
|
4082
4200
|
const [row] = await db
|
|
4083
4201
|
.select()
|
|
4084
4202
|
.from(schema.${names.pluralCamel})
|
|
@@ -4087,7 +4205,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4087
4205
|
|
|
4088
4206
|
return row ? to${names.singularPascal}(row) : null;
|
|
4089
4207
|
},
|
|
4090
|
-
async update(input) {
|
|
4208
|
+
async update(input${options.tenant ? ", scope" : ""}) {
|
|
4091
4209
|
const result = await db
|
|
4092
4210
|
.update(schema.${names.pluralCamel})
|
|
4093
4211
|
.set({
|
|
@@ -4107,7 +4225,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4107
4225
|
|
|
4108
4226
|
return row ? to${names.singularPascal}(row) : null;
|
|
4109
4227
|
},
|
|
4110
|
-
async delete(id: string${options.tenant ? ",
|
|
4228
|
+
async delete(id: string${options.tenant ? ", scope" : ""}) {
|
|
4111
4229
|
${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst result = await db\n\t\t\t\t.update(schema.${names.pluralCamel})\n\t\t\t\t.set({ deletedAt: now, updatedAt: now })\n\t\t\t\t.where(${deleteWhere});\n` : `\t\t\tconst result = await db\n\t\t\t\t.delete(schema.${names.pluralCamel})\n\t\t\t\t.where(${deleteWhere});\n`}
|
|
4112
4230
|
return affectedRows(result) > 0;
|
|
4113
4231
|
},
|
|
@@ -4115,7 +4233,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4115
4233
|
|
|
4116
4234
|
return `import "@beignet/core/server-only";
|
|
4117
4235
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
4118
|
-
import type { ${dialect.dbTypeName} } from "@beignet/provider-db-drizzle/${dialect.subpath}";
|
|
4236
|
+
${options.tenant ? `import { tenantScopeId } from "@beignet/core/tenancy";\n` : ""}import type { ${dialect.dbTypeName} } from "@beignet/provider-db-drizzle/${dialect.subpath}";
|
|
4119
4237
|
import { ${drizzleImports.join(", ")} } from "drizzle-orm";
|
|
4120
4238
|
import type { ${names.singularPascal}Repository } from "${aliasModule(repositoryPortPath)}";
|
|
4121
4239
|
import { encode${names.singularPascal}Cursor } from "${aliasModule(resourceSchemaFilePath(names, config))}";
|
|
@@ -4162,9 +4280,9 @@ function ${names.singularCamel}CursorFilter(
|
|
|
4162
4280
|
|
|
4163
4281
|
function ${names.singularCamel}ListWhere(
|
|
4164
4282
|
query: List${names.pluralPascal}Query,
|
|
4165
|
-
): SQL<unknown> | undefined {
|
|
4283
|
+
${options.tenant ? `\tscope: Parameters<${names.singularPascal}Repository["list"]>[1],\n` : ""}): SQL<unknown> | undefined {
|
|
4166
4284
|
const filters: SQL<unknown>[] = [
|
|
4167
|
-
${options.tenant ? `\t\teq(schema.${names.pluralCamel}.tenantId,
|
|
4285
|
+
${options.tenant ? `\t\teq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope)),\n` : ""}${options.softDelete ? `\t\tisNull(schema.${names.pluralCamel}.deletedAt),\n` : ""} ];
|
|
4168
4286
|
const cursor = ${names.singularCamel}CursorFilter(query);
|
|
4169
4287
|
|
|
4170
4288
|
if (query.name) {
|
|
@@ -4198,8 +4316,8 @@ export function createDrizzle${names.singularPascal}Repository(
|
|
|
4198
4316
|
db: ${dialect.dbTypeName}<typeof schema>,
|
|
4199
4317
|
): ${names.singularPascal}Repository {
|
|
4200
4318
|
return {
|
|
4201
|
-
async list(query) {
|
|
4202
|
-
const where = ${names.singularCamel}ListWhere(query);
|
|
4319
|
+
async list(query${options.tenant ? ", scope" : ""}) {
|
|
4320
|
+
const where = ${names.singularCamel}ListWhere(query${options.tenant ? ", scope" : ""});
|
|
4203
4321
|
const rows = await db
|
|
4204
4322
|
.select()
|
|
4205
4323
|
.from(schema.${names.pluralCamel})
|
|
@@ -4379,7 +4497,7 @@ function useCaseTestFile(
|
|
|
4379
4497
|
): string {
|
|
4380
4498
|
const filePath = useCaseTestFilePath(names, config);
|
|
4381
4499
|
|
|
4382
|
-
return `import { describe, expect, it } from "
|
|
4500
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
4383
4501
|
import { createUseCaseTester } from "@beignet/core/application";
|
|
4384
4502
|
import { createInMemoryDevtools } from "@beignet/devtools";
|
|
4385
4503
|
import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
|
|
@@ -4551,7 +4669,7 @@ function resourcePolicyTestFile(
|
|
|
4551
4669
|
names: ResourceNames,
|
|
4552
4670
|
options: ResourceGenerationOptions,
|
|
4553
4671
|
): string {
|
|
4554
|
-
return `import { describe, expect, it } from "
|
|
4672
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
4555
4673
|
import { createPolicyTester } from "@beignet/core/ports/testing";
|
|
4556
4674
|
import { ${names.singularCamel}Policy } from "../policy";
|
|
4557
4675
|
import type { ${names.singularPascal} } from "../schemas";
|
|
@@ -5169,7 +5287,7 @@ function testFile(
|
|
|
5169
5287
|
);
|
|
5170
5288
|
const requestTarget = "app";
|
|
5171
5289
|
|
|
5172
|
-
return `import { describe, expect, it } from "
|
|
5290
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
5173
5291
|
import { createUseCaseTester } from "@beignet/core/application";
|
|
5174
5292
|
${mode === "resource" ? `import { isAppError } from "@beignet/core/errors";\n` : ""}import { defineRoutes } from "@beignet/web";
|
|
5175
5293
|
import { createTestApp } from "@beignet/web/testing";
|