@beignet/cli 0.0.31 → 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 +107 -0
- package/README.md +70 -17
- package/dist/check.d.ts +2 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +16 -0
- package/dist/check.js.map +1 -1
- package/dist/choices.d.ts +22 -2
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +62 -0
- package/dist/choices.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/create-prompts.d.ts +6 -2
- package/dist/create-prompts.d.ts.map +1 -1
- package/dist/create-prompts.js +1 -1
- package/dist/create-prompts.js.map +1 -1
- package/dist/db.d.ts +5 -5
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +5 -5
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +386 -27
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +5 -0
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +805 -62
- 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 +12 -3
- package/dist/make/shared.d.ts.map +1 -1
- package/dist/make/shared.js +52 -11
- 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 +136 -65
- 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.d.ts +90 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +423 -0
- package/dist/preflight.js.map +1 -0
- package/dist/provider-add.d.ts +10 -1
- package/dist/provider-add.d.ts.map +1 -1
- package/dist/provider-add.js +255 -16
- 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.d.ts.map +1 -1
- package/dist/templates/base.js +5 -11
- 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 +10 -5
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +5 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +13 -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/check.ts +23 -0
- package/src/choices.ts +84 -0
- package/src/config.ts +4 -0
- package/src/create-prompts.ts +8 -2
- package/src/db.ts +12 -12
- package/src/index.ts +592 -37
- package/src/inspect.ts +1253 -156
- package/src/make/inbox.ts +5 -3
- package/src/make/payments.ts +58 -29
- package/src/make/shared.ts +87 -13
- package/src/make/tenancy.ts +6 -4
- package/src/make.ts +179 -65
- package/src/outbox.ts +363 -0
- package/src/preflight.ts +596 -0
- package/src/provider-add.ts +270 -16
- package/src/task.ts +2 -3
- package/src/templates/agents.ts +6 -3
- package/src/templates/base.ts +5 -13
- package/src/templates/index.ts +2 -0
- package/src/templates/server.ts +10 -5
- package/src/templates/shared.ts +15 -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,
|
|
@@ -138,12 +141,12 @@ import {
|
|
|
138
141
|
} from "./make/shared.js";
|
|
139
142
|
import {
|
|
140
143
|
appendToArrayExpression,
|
|
141
|
-
appendToNamedArray,
|
|
142
144
|
appendToOutboxRegistryArray,
|
|
143
145
|
arrayInitializerInfo,
|
|
144
146
|
identifiersFromArrayExpression,
|
|
145
147
|
insertAfterImports,
|
|
146
148
|
} from "./registry-edits.js";
|
|
149
|
+
import { testSupportTemplateFiles } from "./templates/testing.js";
|
|
147
150
|
|
|
148
151
|
export type { MakeFeatureAddon, MakeFeatureRecipe } from "./choices.js";
|
|
149
152
|
export {
|
|
@@ -1241,7 +1244,7 @@ export async function makeSeed(
|
|
|
1241
1244
|
if (
|
|
1242
1245
|
await updatePackageScripts(
|
|
1243
1246
|
targetDir,
|
|
1244
|
-
{ "db:seed": `
|
|
1247
|
+
{ "db:seed": `tsx ${seedEntrypointPath(config)}` },
|
|
1245
1248
|
{ dryRun: Boolean(options.dryRun) },
|
|
1246
1249
|
)
|
|
1247
1250
|
) {
|
|
@@ -1320,6 +1323,9 @@ export async function makeListener(
|
|
|
1320
1323
|
if (!options.skipEventAssert) {
|
|
1321
1324
|
await assertListenerEventExists(targetDir, names.event, config);
|
|
1322
1325
|
}
|
|
1326
|
+
await updateListenerRegistry(targetDir, names, config, {
|
|
1327
|
+
dryRun: true,
|
|
1328
|
+
});
|
|
1323
1329
|
await updateListenerProviderWiring(targetDir, names, config, {
|
|
1324
1330
|
dryRun: true,
|
|
1325
1331
|
});
|
|
@@ -1340,6 +1346,24 @@ export async function makeListener(
|
|
|
1340
1346
|
},
|
|
1341
1347
|
});
|
|
1342
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
|
+
|
|
1343
1367
|
const providerResult = await updateListenerProviderWiring(
|
|
1344
1368
|
targetDir,
|
|
1345
1369
|
names,
|
|
@@ -1361,6 +1385,49 @@ export async function makeListener(
|
|
|
1361
1385
|
return result;
|
|
1362
1386
|
}
|
|
1363
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
|
+
|
|
1364
1431
|
async function assertListenerEventExists(
|
|
1365
1432
|
targetDir: string,
|
|
1366
1433
|
names: EventNames,
|
|
@@ -1702,6 +1769,7 @@ async function updateOutboxPorts(
|
|
|
1702
1769
|
const filePath = path.join(targetDir, config.paths.ports);
|
|
1703
1770
|
const original = await readFile(filePath, "utf8");
|
|
1704
1771
|
let next = addNamedTypeImport(original, "OutboxPort", "@beignet/core/outbox");
|
|
1772
|
+
next = addNamedTypeImport(next, "OutboxAdminPort", "@beignet/core/outbox");
|
|
1705
1773
|
if (!typeHasTopLevelProperty(next, "AppPorts", "outbox")) {
|
|
1706
1774
|
next = insertTypeProperty(
|
|
1707
1775
|
next,
|
|
@@ -1710,6 +1778,14 @@ async function updateOutboxPorts(
|
|
|
1710
1778
|
`Could not find AppPorts in ${config.paths.ports}. Add outbox: OutboxPort; manually, or restore the generated ports file before running make outbox.`,
|
|
1711
1779
|
);
|
|
1712
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
|
+
}
|
|
1713
1789
|
if (!typeHasTopLevelProperty(next, "AppTransactionPorts", "outbox")) {
|
|
1714
1790
|
next = insertTypeProperty(
|
|
1715
1791
|
next,
|
|
@@ -1731,7 +1807,8 @@ async function updateOutboxInfrastructurePorts(
|
|
|
1731
1807
|
): Promise<boolean> {
|
|
1732
1808
|
const filePath = path.join(targetDir, config.paths.infrastructurePorts);
|
|
1733
1809
|
const original = await readFile(filePath, "utf8");
|
|
1734
|
-
|
|
1810
|
+
let next = appendDeferredPortKey(original, "outbox");
|
|
1811
|
+
next = appendDeferredPortKey(next, "outboxAdmin");
|
|
1735
1812
|
|
|
1736
1813
|
if (next === original) return false;
|
|
1737
1814
|
if (!options.dryRun) await writeFile(filePath, next);
|
|
@@ -1749,13 +1826,20 @@ async function updateOutboxDatabaseProvider(
|
|
|
1749
1826
|
|
|
1750
1827
|
const database = await detectResourceDatabase(targetDir, config);
|
|
1751
1828
|
const factoryName = drizzleOutboxPortFactoryName(database);
|
|
1829
|
+
const adminFactoryName = drizzleOutboxAdminPortFactoryName(database);
|
|
1752
1830
|
const original = await readFile(filePath, "utf8");
|
|
1753
1831
|
let next = addNamedImport(
|
|
1754
1832
|
original,
|
|
1755
1833
|
factoryName,
|
|
1756
1834
|
`@beignet/provider-db-drizzle/${database}`,
|
|
1757
1835
|
);
|
|
1836
|
+
next = addNamedImport(
|
|
1837
|
+
next,
|
|
1838
|
+
adminFactoryName,
|
|
1839
|
+
`@beignet/provider-db-drizzle/${database}`,
|
|
1840
|
+
);
|
|
1758
1841
|
next = appendPickStringLiteralMember(next, "AppPorts", "outbox");
|
|
1842
|
+
next = appendPickStringLiteralMember(next, "AppPorts", "outboxAdmin");
|
|
1759
1843
|
|
|
1760
1844
|
if (!/\bconst\s+outbox\s*=/.test(next)) {
|
|
1761
1845
|
const beforeOutboxConst = next;
|
|
@@ -1770,6 +1854,21 @@ async function updateOutboxDatabaseProvider(
|
|
|
1770
1854
|
}
|
|
1771
1855
|
}
|
|
1772
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
|
+
|
|
1773
1872
|
if (!/\boutbox\s*,/.test(next)) {
|
|
1774
1873
|
const withOutboxPort = next.replace(
|
|
1775
1874
|
/(\n\s*idempotency,\n)/,
|
|
@@ -1783,6 +1882,19 @@ async function updateOutboxDatabaseProvider(
|
|
|
1783
1882
|
next = withOutboxPort;
|
|
1784
1883
|
}
|
|
1785
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
|
+
|
|
1786
1898
|
if (!new RegExp(`outbox:\\s*${factoryName}\\(tx\\)`).test(next)) {
|
|
1787
1899
|
const withTransactionOutbox = next.replace(
|
|
1788
1900
|
/(\n\s*idempotency: createDrizzle[A-Za-z]+IdempotencyPort\(tx\),\n)/,
|
|
@@ -1882,6 +1994,12 @@ function drizzleOutboxPortFactoryName(database: DatabaseName): string {
|
|
|
1882
1994
|
return "createDrizzleSqliteOutboxPort";
|
|
1883
1995
|
}
|
|
1884
1996
|
|
|
1997
|
+
function drizzleOutboxAdminPortFactoryName(database: DatabaseName): string {
|
|
1998
|
+
if (database === "postgres") return "createDrizzlePostgresOutboxAdminPort";
|
|
1999
|
+
if (database === "mysql") return "createDrizzleMysqlOutboxAdminPort";
|
|
2000
|
+
return "createDrizzleSqliteOutboxAdminPort";
|
|
2001
|
+
}
|
|
2002
|
+
|
|
1885
2003
|
async function applyOutboxDrainWiring(
|
|
1886
2004
|
result: MakeResult,
|
|
1887
2005
|
targetDir: string,
|
|
@@ -1957,7 +2075,10 @@ export async function makeUpload(
|
|
|
1957
2075
|
force: Boolean(options.force),
|
|
1958
2076
|
dryRun: Boolean(options.dryRun),
|
|
1959
2077
|
name: `${names.feature.kebab}/${names.artifact.kebab}`,
|
|
1960
|
-
files:
|
|
2078
|
+
files: [
|
|
2079
|
+
...(await missingCapabilityBuilderFiles(targetDir, "uploads", config)),
|
|
2080
|
+
...uploadFiles(names, config),
|
|
2081
|
+
],
|
|
1961
2082
|
index: featureArtifactIndexFile("upload", names, config),
|
|
1962
2083
|
dependencies: {
|
|
1963
2084
|
"@beignet/core": beignetDependencyVersion,
|
|
@@ -2512,6 +2633,7 @@ function resourceFiles(
|
|
|
2512
2633
|
const infraDir = infrastructureDir(config);
|
|
2513
2634
|
const featureDir = resourceFeatureDir(names, config);
|
|
2514
2635
|
const files: GeneratedFile[] = [
|
|
2636
|
+
...testSupportTemplateFiles,
|
|
2515
2637
|
{
|
|
2516
2638
|
path: resourceContractFilePath(names, config),
|
|
2517
2639
|
content: contractFile(names, config, mode, options),
|
|
@@ -2668,6 +2790,7 @@ function testFiles(
|
|
|
2668
2790
|
config: ResolvedBeignetConfig,
|
|
2669
2791
|
): GeneratedFile[] {
|
|
2670
2792
|
return [
|
|
2793
|
+
...testSupportTemplateFiles,
|
|
2671
2794
|
{
|
|
2672
2795
|
path: useCaseTestFilePath(names, config),
|
|
2673
2796
|
content: useCaseTestFile(names, config),
|
|
@@ -3468,7 +3591,7 @@ function listUseCaseFile(
|
|
|
3468
3591
|
|
|
3469
3592
|
return `import "@beignet/core/server-only";
|
|
3470
3593
|
import { normalizeCursorPage } from "@beignet/core/pagination";
|
|
3471
|
-
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)}";
|
|
3472
3595
|
import {
|
|
3473
3596
|
decode${names.singularPascal}Cursor,
|
|
3474
3597
|
List${names.pluralPascal}InputSchema,
|
|
@@ -3480,7 +3603,7 @@ export const list${names.pluralPascal}UseCase = useCase
|
|
|
3480
3603
|
.input(List${names.pluralPascal}InputSchema)
|
|
3481
3604
|
.output(List${names.pluralPascal}OutputSchema)
|
|
3482
3605
|
.run(async ({ ctx, input }) => {
|
|
3483
|
-
${options.tenant ? `\t\tconst
|
|
3606
|
+
${options.tenant ? `\t\tconst scope = requireTenantScope(ctx);\n\n` : ""} const page = normalizeCursorPage(input, {
|
|
3484
3607
|
defaultLimit: 20,
|
|
3485
3608
|
maxLimit: 100,
|
|
3486
3609
|
});
|
|
@@ -3491,7 +3614,7 @@ ${options.tenant ? `\t\tconst tenantId = ctx.tenant?.id;\n\t\tif (!tenantId) {\n
|
|
|
3491
3614
|
name: input.name,
|
|
3492
3615
|
sortBy: input.sortBy,
|
|
3493
3616
|
sortDirection: input.sortDirection,
|
|
3494
|
-
${options.tenant ?
|
|
3617
|
+
}${options.tenant ? ", scope" : ""});
|
|
3495
3618
|
});
|
|
3496
3619
|
`;
|
|
3497
3620
|
}
|
|
@@ -3507,7 +3630,7 @@ function createUseCaseFile(
|
|
|
3507
3630
|
);
|
|
3508
3631
|
|
|
3509
3632
|
return `import "@beignet/core/server-only";
|
|
3510
|
-
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)}";
|
|
3511
3634
|
import {
|
|
3512
3635
|
Create${names.singularPascal}InputSchema,
|
|
3513
3636
|
${names.singularPascal}Schema,
|
|
@@ -3519,9 +3642,7 @@ export const create${names.singularPascal}UseCase = useCase
|
|
|
3519
3642
|
.input(Create${names.singularPascal}InputSchema)
|
|
3520
3643
|
.output(${names.singularPascal}Schema)
|
|
3521
3644
|
.run(async ({ ctx, input }) => {
|
|
3522
|
-
${options.tenant ? `\t\tconst
|
|
3523
|
-
...input,
|
|
3524
|
-
${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" : ""});
|
|
3525
3646
|
${options.events ? `\n\t\tawait ctx.ports.eventBus.publish(${names.singularPascal}Created, {\n\t\t\tid: ${names.singularCamel}.id,\n\t\t});\n` : ""}
|
|
3526
3647
|
return ${names.singularCamel};
|
|
3527
3648
|
});
|
|
@@ -3539,7 +3660,7 @@ function getUseCaseFile(
|
|
|
3539
3660
|
);
|
|
3540
3661
|
|
|
3541
3662
|
return `import "@beignet/core/server-only";
|
|
3542
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3663
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3543
3664
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3544
3665
|
import {
|
|
3545
3666
|
${names.singularPascal}IdInputSchema,
|
|
@@ -3551,7 +3672,7 @@ export const get${names.singularPascal}UseCase = useCase
|
|
|
3551
3672
|
.input(${names.singularPascal}IdInputSchema)
|
|
3552
3673
|
.output(${names.singularPascal}Schema)
|
|
3553
3674
|
.run(async ({ ctx, input }) => {
|
|
3554
|
-
${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" : ""});
|
|
3555
3676
|
if (!${names.singularCamel}) {
|
|
3556
3677
|
throw appError("${names.singularPascal}NotFound", {
|
|
3557
3678
|
details: { id: input.id },
|
|
@@ -3574,7 +3695,7 @@ function updateUseCaseFile(
|
|
|
3574
3695
|
);
|
|
3575
3696
|
|
|
3576
3697
|
return `import "@beignet/core/server-only";
|
|
3577
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3698
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3578
3699
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3579
3700
|
${options.events ? `import { ${names.singularPascal}Updated } from "../domain/events";\n` : ""}import {
|
|
3580
3701
|
Update${names.singularPascal}InputSchema,
|
|
@@ -3586,7 +3707,7 @@ export const update${names.singularPascal}UseCase = useCase
|
|
|
3586
3707
|
.input(Update${names.singularPascal}InputSchema)
|
|
3587
3708
|
.output(${names.singularPascal}Schema)
|
|
3588
3709
|
.run(async ({ ctx, input }) => {
|
|
3589
|
-
${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" : ""});
|
|
3590
3711
|
if (!existing) {
|
|
3591
3712
|
throw appError("${names.singularPascal}NotFound", {
|
|
3592
3713
|
details: { id: input.id },
|
|
@@ -3600,7 +3721,7 @@ ${options.auth ? `\t\tawait ctx.gate.authorize("${names.pluralCamel}.update", ex
|
|
|
3600
3721
|
|
|
3601
3722
|
const ${names.singularCamel} = await ctx.ports.${names.pluralCamel}.update({
|
|
3602
3723
|
...input,
|
|
3603
|
-
${options.tenant ?
|
|
3724
|
+
}${options.tenant ? ", scope" : ""});
|
|
3604
3725
|
if (!${names.singularCamel}) {
|
|
3605
3726
|
throw appError("${names.singularPascal}Conflict", {
|
|
3606
3727
|
details: { id: input.id, expectedVersion: existing.version },
|
|
@@ -3624,7 +3745,7 @@ function deleteUseCaseFile(
|
|
|
3624
3745
|
|
|
3625
3746
|
return `import "@beignet/core/server-only";
|
|
3626
3747
|
import { z } from "zod";
|
|
3627
|
-
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3748
|
+
${options.tenant ? `import { requireTenantScope } from "@beignet/core/tenancy";\n` : ""}import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
3628
3749
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
3629
3750
|
${options.events ? `import { ${names.singularPascal}Deleted } from "../domain/events";\n` : ""}import { ${names.singularPascal}IdInputSchema } from "../schemas";
|
|
3630
3751
|
|
|
@@ -3633,7 +3754,7 @@ export const delete${names.singularPascal}UseCase = useCase
|
|
|
3633
3754
|
.input(${names.singularPascal}IdInputSchema)
|
|
3634
3755
|
.output(z.void())
|
|
3635
3756
|
.run(async ({ ctx, input }) => {
|
|
3636
|
-
${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" : ""});
|
|
3637
3758
|
if (!deleted) {
|
|
3638
3759
|
throw appError("${names.singularPascal}NotFound", {
|
|
3639
3760
|
details: { id: input.id },
|
|
@@ -3673,7 +3794,7 @@ function repositoryPortFile(
|
|
|
3673
3794
|
PageResult,
|
|
3674
3795
|
SortDirection,
|
|
3675
3796
|
} from "@beignet/core/pagination";
|
|
3676
|
-
import type {
|
|
3797
|
+
${options.tenant ? `import type { TenantScope } from "@beignet/core/tenancy";\n` : ""}import type {
|
|
3677
3798
|
Create${names.singularPascal}Input,
|
|
3678
3799
|
${names.singularPascal}Cursor,
|
|
3679
3800
|
${names.singularPascal}SortBy,
|
|
@@ -3687,13 +3808,12 @@ export type List${names.pluralPascal}Query = {
|
|
|
3687
3808
|
name?: string;
|
|
3688
3809
|
sortBy: ${names.singularPascal}SortBy;
|
|
3689
3810
|
sortDirection: SortDirection;
|
|
3690
|
-
|
|
3691
|
-
${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
|
+
};
|
|
3692
3812
|
|
|
3693
3813
|
export interface ${names.singularPascal}Repository {
|
|
3694
|
-
list(query: List${names.pluralPascal}Query): Promise<List${names.pluralPascal}Result>;
|
|
3695
|
-
create(input: ${options.tenant ?
|
|
3696
|
-
${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` : ""}}
|
|
3697
3817
|
`;
|
|
3698
3818
|
}
|
|
3699
3819
|
|
|
@@ -3727,14 +3847,14 @@ function inMemoryRepositoryFile(
|
|
|
3727
3847
|
? " || existing.deletedAt !== null"
|
|
3728
3848
|
: "";
|
|
3729
3849
|
const deleteImplementation = options.softDelete
|
|
3730
|
-
? `${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();
|
|
3731
3851
|
${names.pluralCamel}.set(id, { ...existing, deletedAt: now, updatedAt: now });
|
|
3732
3852
|
return true;`
|
|
3733
|
-
: `${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);`;
|
|
3734
3854
|
|
|
3735
3855
|
return `import "@beignet/core/server-only";
|
|
3736
3856
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
3737
|
-
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)}";
|
|
3738
3858
|
import { encode${names.singularPascal}Cursor } from "${aliasModule(resourceSchemaFilePath(names, config))}";
|
|
3739
3859
|
import type {
|
|
3740
3860
|
Create${names.singularPascal}Input,
|
|
@@ -3802,10 +3922,10 @@ export function createInMemory${names.singularPascal}Repository(
|
|
|
3802
3922
|
);
|
|
3803
3923
|
|
|
3804
3924
|
return {
|
|
3805
|
-
async list(query) {
|
|
3925
|
+
async list(query${options.tenant ? ", scope" : ""}) {
|
|
3806
3926
|
const name = query.name?.toLocaleLowerCase();
|
|
3807
|
-
const all${names.pluralPascal} = Array.from(${names.pluralCamel}.values())
|
|
3808
|
-
${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))
|
|
3809
3929
|
.sort((left, right) => compare${names.pluralPascal}(left, right, query))
|
|
3810
3930
|
.filter((${names.singularCamel}) => isAfter${names.singularPascal}Cursor(${names.singularCamel}, query));
|
|
3811
3931
|
const pageItems = all${names.pluralPascal}.slice(0, query.page.limit);
|
|
@@ -3820,11 +3940,11 @@ ${activeFilter}${options.tenant ? `\t\t\t\t.filter((${names.singularCamel}) => $
|
|
|
3820
3940
|
nextCursor,
|
|
3821
3941
|
);
|
|
3822
3942
|
},
|
|
3823
|
-
async create(input) {
|
|
3943
|
+
async create(input${options.tenant ? ", scope" : ""}) {
|
|
3824
3944
|
const now = new Date().toISOString();
|
|
3825
|
-
const ${names.singularCamel}: ${names.singularPascal} = {
|
|
3945
|
+
${options.tenant ? `\t\t\tconst tenantId = tenantScopeId(scope);\n` : ""} const ${names.singularCamel}: ${names.singularPascal} = {
|
|
3826
3946
|
id: crypto.randomUUID(),
|
|
3827
|
-
${options.tenant ? `\t\t\t\ttenantId
|
|
3947
|
+
${options.tenant ? `\t\t\t\ttenantId,\n` : ""} name: input.name,
|
|
3828
3948
|
version: 1,
|
|
3829
3949
|
createdAt: now,
|
|
3830
3950
|
updatedAt: now,
|
|
@@ -3834,7 +3954,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
3834
3954
|
${newDeletedAtField} });
|
|
3835
3955
|
return ${names.singularCamel};
|
|
3836
3956
|
},
|
|
3837
|
-
${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` : ""}
|
|
3838
3958
|
};
|
|
3839
3959
|
}
|
|
3840
3960
|
`;
|
|
@@ -3958,20 +4078,20 @@ function drizzleRepositoryFile(
|
|
|
3958
4078
|
const findWhere = drizzleResourceWhere(names, options, [
|
|
3959
4079
|
`eq(schema.${names.pluralCamel}.id, id)`,
|
|
3960
4080
|
...(options.tenant
|
|
3961
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4081
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3962
4082
|
: []),
|
|
3963
4083
|
]);
|
|
3964
4084
|
const updateWhere = drizzleResourceWhere(names, options, [
|
|
3965
4085
|
`eq(schema.${names.pluralCamel}.id, input.id)`,
|
|
3966
4086
|
`eq(schema.${names.pluralCamel}.version, input.version)`,
|
|
3967
4087
|
...(options.tenant
|
|
3968
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4088
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3969
4089
|
: []),
|
|
3970
4090
|
]);
|
|
3971
4091
|
const deleteWhere = drizzleResourceWhere(names, options, [
|
|
3972
4092
|
`eq(schema.${names.pluralCamel}.id, id)`,
|
|
3973
4093
|
...(options.tenant
|
|
3974
|
-
? [`eq(schema.${names.pluralCamel}.tenantId,
|
|
4094
|
+
? [`eq(schema.${names.pluralCamel}.tenantId, tenantScopeId(scope))`]
|
|
3975
4095
|
: []),
|
|
3976
4096
|
]);
|
|
3977
4097
|
const drizzleImports = [
|
|
@@ -3989,7 +4109,7 @@ function drizzleRepositoryFile(
|
|
|
3989
4109
|
// After a version-guarded update succeeds, re-select by identity only —
|
|
3990
4110
|
// matching what `.returning()` yields on the dialects that support it.
|
|
3991
4111
|
const updateReselectWhere = options.tenant
|
|
3992
|
-
? `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)))`
|
|
3993
4113
|
: `eq(schema.${names.pluralCamel}.id, input.id)`;
|
|
3994
4114
|
// drizzle-orm/mysql2 has no `.returning(...)`; mutations resolve to a
|
|
3995
4115
|
// `[ResultSetHeader, FieldPacket[]]` tuple typed as `unknown` through the
|
|
@@ -4011,13 +4131,13 @@ function affectedRows(result: unknown): number {
|
|
|
4011
4131
|
`
|
|
4012
4132
|
: "";
|
|
4013
4133
|
const createMethod = dialect.supportsReturning
|
|
4014
|
-
? ` async create(input) {
|
|
4134
|
+
? ` async create(input${options.tenant ? ", scope" : ""}) {
|
|
4015
4135
|
const now = new Date().toISOString();
|
|
4016
4136
|
const [row] = await db
|
|
4017
4137
|
.insert(schema.${names.pluralCamel})
|
|
4018
4138
|
.values({
|
|
4019
4139
|
id: crypto.randomUUID(),
|
|
4020
|
-
${options.tenant ? `\t\t\t\t\ttenantId:
|
|
4140
|
+
${options.tenant ? `\t\t\t\t\ttenantId: tenantScopeId(scope),\n` : ""} name: input.name,
|
|
4021
4141
|
version: 1,
|
|
4022
4142
|
createdAt: now,
|
|
4023
4143
|
updatedAt: now,
|
|
@@ -4031,11 +4151,11 @@ ${options.tenant ? `\t\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input
|
|
|
4031
4151
|
return to${names.singularPascal}(row);
|
|
4032
4152
|
},
|
|
4033
4153
|
`
|
|
4034
|
-
: ` async create(input) {
|
|
4154
|
+
: ` async create(input${options.tenant ? ", scope" : ""}) {
|
|
4035
4155
|
const now = new Date().toISOString();
|
|
4036
4156
|
const ${names.singularCamel}: ${names.singularPascal} = {
|
|
4037
4157
|
id: crypto.randomUUID(),
|
|
4038
|
-
${options.tenant ? `\t\t\t\ttenantId:
|
|
4158
|
+
${options.tenant ? `\t\t\t\ttenantId: tenantScopeId(scope),\n` : ""} name: input.name,
|
|
4039
4159
|
version: 1,
|
|
4040
4160
|
createdAt: now,
|
|
4041
4161
|
updatedAt: now,
|
|
@@ -4049,7 +4169,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4049
4169
|
mode !== "resource"
|
|
4050
4170
|
? ""
|
|
4051
4171
|
: dialect.supportsReturning
|
|
4052
|
-
? ` async findById(id: string${options.tenant ? ",
|
|
4172
|
+
? ` async findById(id: string${options.tenant ? ", scope" : ""}) {
|
|
4053
4173
|
const [row] = await db
|
|
4054
4174
|
.select()
|
|
4055
4175
|
.from(schema.${names.pluralCamel})
|
|
@@ -4058,7 +4178,7 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4058
4178
|
|
|
4059
4179
|
return row ? to${names.singularPascal}(row) : null;
|
|
4060
4180
|
},
|
|
4061
|
-
async update(input) {
|
|
4181
|
+
async update(input${options.tenant ? ", scope" : ""}) {
|
|
4062
4182
|
const [row] = await db
|
|
4063
4183
|
.update(schema.${names.pluralCamel})
|
|
4064
4184
|
.set({
|
|
@@ -4071,12 +4191,12 @@ ${options.tenant ? `\t\t\t\ttenantId: input.tenantId,\n` : ""} name: input.na
|
|
|
4071
4191
|
|
|
4072
4192
|
return row ? to${names.singularPascal}(row) : null;
|
|
4073
4193
|
},
|
|
4074
|
-
async delete(id: string${options.tenant ? ",
|
|
4194
|
+
async delete(id: string${options.tenant ? ", scope" : ""}) {
|
|
4075
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`}
|
|
4076
4196
|
return rows.length > 0;
|
|
4077
4197
|
},
|
|
4078
4198
|
`
|
|
4079
|
-
: ` async findById(id: string${options.tenant ? ",
|
|
4199
|
+
: ` async findById(id: string${options.tenant ? ", scope" : ""}) {
|
|
4080
4200
|
const [row] = await db
|
|
4081
4201
|
.select()
|
|
4082
4202
|
.from(schema.${names.pluralCamel})
|
|
@@ -4085,7 +4205,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4085
4205
|
|
|
4086
4206
|
return row ? to${names.singularPascal}(row) : null;
|
|
4087
4207
|
},
|
|
4088
|
-
async update(input) {
|
|
4208
|
+
async update(input${options.tenant ? ", scope" : ""}) {
|
|
4089
4209
|
const result = await db
|
|
4090
4210
|
.update(schema.${names.pluralCamel})
|
|
4091
4211
|
.set({
|
|
@@ -4105,7 +4225,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4105
4225
|
|
|
4106
4226
|
return row ? to${names.singularPascal}(row) : null;
|
|
4107
4227
|
},
|
|
4108
|
-
async delete(id: string${options.tenant ? ",
|
|
4228
|
+
async delete(id: string${options.tenant ? ", scope" : ""}) {
|
|
4109
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`}
|
|
4110
4230
|
return affectedRows(result) > 0;
|
|
4111
4231
|
},
|
|
@@ -4113,7 +4233,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
4113
4233
|
|
|
4114
4234
|
return `import "@beignet/core/server-only";
|
|
4115
4235
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
4116
|
-
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}";
|
|
4117
4237
|
import { ${drizzleImports.join(", ")} } from "drizzle-orm";
|
|
4118
4238
|
import type { ${names.singularPascal}Repository } from "${aliasModule(repositoryPortPath)}";
|
|
4119
4239
|
import { encode${names.singularPascal}Cursor } from "${aliasModule(resourceSchemaFilePath(names, config))}";
|
|
@@ -4160,9 +4280,9 @@ function ${names.singularCamel}CursorFilter(
|
|
|
4160
4280
|
|
|
4161
4281
|
function ${names.singularCamel}ListWhere(
|
|
4162
4282
|
query: List${names.pluralPascal}Query,
|
|
4163
|
-
): SQL<unknown> | undefined {
|
|
4283
|
+
${options.tenant ? `\tscope: Parameters<${names.singularPascal}Repository["list"]>[1],\n` : ""}): SQL<unknown> | undefined {
|
|
4164
4284
|
const filters: SQL<unknown>[] = [
|
|
4165
|
-
${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` : ""} ];
|
|
4166
4286
|
const cursor = ${names.singularCamel}CursorFilter(query);
|
|
4167
4287
|
|
|
4168
4288
|
if (query.name) {
|
|
@@ -4196,8 +4316,8 @@ export function createDrizzle${names.singularPascal}Repository(
|
|
|
4196
4316
|
db: ${dialect.dbTypeName}<typeof schema>,
|
|
4197
4317
|
): ${names.singularPascal}Repository {
|
|
4198
4318
|
return {
|
|
4199
|
-
async list(query) {
|
|
4200
|
-
const where = ${names.singularCamel}ListWhere(query);
|
|
4319
|
+
async list(query${options.tenant ? ", scope" : ""}) {
|
|
4320
|
+
const where = ${names.singularCamel}ListWhere(query${options.tenant ? ", scope" : ""});
|
|
4201
4321
|
const rows = await db
|
|
4202
4322
|
.select()
|
|
4203
4323
|
.from(schema.${names.pluralCamel})
|
|
@@ -4377,7 +4497,7 @@ function useCaseTestFile(
|
|
|
4377
4497
|
): string {
|
|
4378
4498
|
const filePath = useCaseTestFilePath(names, config);
|
|
4379
4499
|
|
|
4380
|
-
return `import { describe, expect, it } from "
|
|
4500
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
4381
4501
|
import { createUseCaseTester } from "@beignet/core/application";
|
|
4382
4502
|
import { createInMemoryDevtools } from "@beignet/devtools";
|
|
4383
4503
|
import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
|
|
@@ -4549,7 +4669,7 @@ function resourcePolicyTestFile(
|
|
|
4549
4669
|
names: ResourceNames,
|
|
4550
4670
|
options: ResourceGenerationOptions,
|
|
4551
4671
|
): string {
|
|
4552
|
-
return `import { describe, expect, it } from "
|
|
4672
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
4553
4673
|
import { createPolicyTester } from "@beignet/core/ports/testing";
|
|
4554
4674
|
import { ${names.singularCamel}Policy } from "../policy";
|
|
4555
4675
|
import type { ${names.singularPascal} } from "../schemas";
|
|
@@ -4800,9 +4920,8 @@ ${names.timezone ? `\t\ttimezone: "${names.timezone}",\n` : ""}\t\tpayload: ${na
|
|
|
4800
4920
|
}
|
|
4801
4921
|
|
|
4802
4922
|
function uploadFile(names: UploadNames, config: ResolvedBeignetConfig): string {
|
|
4803
|
-
return `import {
|
|
4804
|
-
import {
|
|
4805
|
-
import type { AppContext } from "${aliasModule(config.paths.appContext)}";
|
|
4923
|
+
return `import { z } from "zod";
|
|
4924
|
+
import { defineUpload } from "${aliasModule(config.paths.uploadsBuilder)}";
|
|
4806
4925
|
|
|
4807
4926
|
export const ${names.metadataSchemaName} = z.object({
|
|
4808
4927
|
\tresourceId: z.string().min(1),
|
|
@@ -4810,12 +4929,7 @@ export const ${names.metadataSchemaName} = z.object({
|
|
|
4810
4929
|
|
|
4811
4930
|
export type ${names.metadataTypeName} = z.infer<typeof ${names.metadataSchemaName}>;
|
|
4812
4931
|
|
|
4813
|
-
export const ${names.uploadExportName} = defineUpload
|
|
4814
|
-
\t"${names.uploadName}",
|
|
4815
|
-
\ttypeof ${names.metadataSchemaName},
|
|
4816
|
-
\tAppContext,
|
|
4817
|
-
\t{ objectKeys: string[] }
|
|
4818
|
-
>("${names.uploadName}", {
|
|
4932
|
+
export const ${names.uploadExportName} = defineUpload("${names.uploadName}", {
|
|
4819
4933
|
\tmetadata: ${names.metadataSchemaName},
|
|
4820
4934
|
\tfile: {
|
|
4821
4935
|
\t\tcontentTypes: ["application/pdf", "text/plain"],
|
|
@@ -5173,7 +5287,7 @@ function testFile(
|
|
|
5173
5287
|
);
|
|
5174
5288
|
const requestTarget = "app";
|
|
5175
5289
|
|
|
5176
|
-
return `import { describe, expect, it } from "
|
|
5290
|
+
return `import { describe, expect, it } from "@/lib/beignet-test";
|
|
5177
5291
|
import { createUseCaseTester } from "@beignet/core/application";
|
|
5178
5292
|
${mode === "resource" ? `import { isAppError } from "@beignet/core/errors";\n` : ""}import { defineRoutes } from "@beignet/web";
|
|
5179
5293
|
import { createTestApp } from "@beignet/web/testing";
|