@beignet/cli 0.0.23 → 0.0.24
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 +15 -0
- package/README.md +10 -2
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +81 -0
- package/dist/inspect.js.map +1 -1
- package/dist/make.js +16 -15
- package/dist/make.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +3 -2
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/db/mysql.js +2 -2
- package/dist/templates/db/postgres.js +2 -2
- package/dist/templates/db/sqlite.js +2 -2
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +1 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +1 -0
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +90 -20
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/todos.js +5 -5
- package/package.json +2 -2
- package/src/inspect.ts +114 -0
- package/src/make.ts +16 -15
- package/src/templates/base.ts +3 -2
- package/src/templates/db/mysql.ts +2 -2
- package/src/templates/db/postgres.ts +2 -2
- package/src/templates/db/sqlite.ts +2 -2
- package/src/templates/index.ts +1 -0
- package/src/templates/server.ts +90 -20
- package/src/templates/todos.ts +5 -5
package/src/make.ts
CHANGED
|
@@ -7188,7 +7188,7 @@ function listUseCaseFile(
|
|
|
7188
7188
|
`list-${names.pluralKebab}.ts`,
|
|
7189
7189
|
);
|
|
7190
7190
|
|
|
7191
|
-
return `import
|
|
7191
|
+
return `import "@beignet/core/server-only";
|
|
7192
7192
|
import { normalizeCursorPage } from "@beignet/core/pagination";
|
|
7193
7193
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
7194
7194
|
import {
|
|
@@ -7228,7 +7228,7 @@ function createUseCaseFile(
|
|
|
7228
7228
|
`create-${names.singularKebab}.ts`,
|
|
7229
7229
|
);
|
|
7230
7230
|
|
|
7231
|
-
return `import
|
|
7231
|
+
return `import "@beignet/core/server-only";
|
|
7232
7232
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
7233
7233
|
import {
|
|
7234
7234
|
Create${names.singularPascal}InputSchema,
|
|
@@ -7260,7 +7260,7 @@ function getUseCaseFile(
|
|
|
7260
7260
|
`get-${names.singularKebab}.ts`,
|
|
7261
7261
|
);
|
|
7262
7262
|
|
|
7263
|
-
return `import
|
|
7263
|
+
return `import "@beignet/core/server-only";
|
|
7264
7264
|
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
7265
7265
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
7266
7266
|
import {
|
|
@@ -7295,7 +7295,7 @@ function updateUseCaseFile(
|
|
|
7295
7295
|
`update-${names.singularKebab}.ts`,
|
|
7296
7296
|
);
|
|
7297
7297
|
|
|
7298
|
-
return `import
|
|
7298
|
+
return `import "@beignet/core/server-only";
|
|
7299
7299
|
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
7300
7300
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
7301
7301
|
${options.events ? `import { ${names.singularPascal}Updated } from "../domain/events";\n` : ""}import {
|
|
@@ -7344,7 +7344,7 @@ function deleteUseCaseFile(
|
|
|
7344
7344
|
`delete-${names.singularKebab}.ts`,
|
|
7345
7345
|
);
|
|
7346
7346
|
|
|
7347
|
-
return `import
|
|
7347
|
+
return `import "@beignet/core/server-only";
|
|
7348
7348
|
import { z } from "zod";
|
|
7349
7349
|
import { appError } from "${relativeModule(filePath, resourceSharedErrorsPath(config))}";
|
|
7350
7350
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
@@ -7454,7 +7454,7 @@ function inMemoryRepositoryFile(
|
|
|
7454
7454
|
return true;`
|
|
7455
7455
|
: `${options.tenant ? `\t\t\tconst existing = ${names.pluralCamel}.get(id);\n\t\t\tif (existing?.tenantId !== filter.tenantId) return false;\n` : ""} return ${names.pluralCamel}.delete(id);`;
|
|
7456
7456
|
|
|
7457
|
-
return `import
|
|
7457
|
+
return `import "@beignet/core/server-only";
|
|
7458
7458
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
7459
7459
|
import type { ${names.singularPascal}Repository } from "${aliasModule(repositoryPortPath)}";
|
|
7460
7460
|
import { encode${names.singularPascal}Cursor } from "${aliasModule(resourceSchemaFilePath(names, config))}";
|
|
@@ -7833,7 +7833,7 @@ ${options.softDelete ? `\t\t\tconst now = new Date().toISOString();\n\t\t\tconst
|
|
|
7833
7833
|
},
|
|
7834
7834
|
`;
|
|
7835
7835
|
|
|
7836
|
-
return `import
|
|
7836
|
+
return `import "@beignet/core/server-only";
|
|
7837
7837
|
import { cursorPageResult } from "@beignet/core/pagination";
|
|
7838
7838
|
import type { ${dialect.dbTypeName} } from "@beignet/provider-db-drizzle/${dialect.subpath}";
|
|
7839
7839
|
import { ${drizzleImports.join(", ")} } from "drizzle-orm";
|
|
@@ -8068,7 +8068,7 @@ function standaloneUseCaseFile(
|
|
|
8068
8068
|
config: ResolvedBeignetConfig,
|
|
8069
8069
|
filePath: string,
|
|
8070
8070
|
): string {
|
|
8071
|
-
return `import
|
|
8071
|
+
return `import "@beignet/core/server-only";
|
|
8072
8072
|
import { z } from "zod";
|
|
8073
8073
|
import { useCase } from "${relativeModule(filePath, config.paths.useCaseBuilder)}";
|
|
8074
8074
|
|
|
@@ -10101,7 +10101,7 @@ function routeGroupFile(
|
|
|
10101
10101
|
const contractsPath = resourceContractFilePath(names, config);
|
|
10102
10102
|
const useCasesPath = resourceUseCaseIndexPath(names, config);
|
|
10103
10103
|
|
|
10104
|
-
return `import
|
|
10104
|
+
return `import "@beignet/core/server-only";
|
|
10105
10105
|
import { defineRouteGroup } from "@beignet/next";
|
|
10106
10106
|
import type { AppContext } from "${relativeModule(routeFilePath, config.paths.appContext)}";
|
|
10107
10107
|
import {
|
|
@@ -10138,12 +10138,12 @@ function testFile(
|
|
|
10138
10138
|
path.dirname(config.paths.server),
|
|
10139
10139
|
"context.ts",
|
|
10140
10140
|
);
|
|
10141
|
-
const requestTarget =
|
|
10141
|
+
const requestTarget = "app";
|
|
10142
10142
|
|
|
10143
10143
|
return `import { describe, expect, it } from "bun:test";
|
|
10144
10144
|
import { createUseCaseTester } from "@beignet/core/application";
|
|
10145
10145
|
${mode === "resource" ? `import { isAppError } from "@beignet/core/errors";\n` : ""}import { defineRoutes } from "@beignet/web";
|
|
10146
|
-
import { createTestApp
|
|
10146
|
+
import { createTestApp } from "@beignet/web/testing";
|
|
10147
10147
|
import { createInMemoryDevtools } from "@beignet/devtools";
|
|
10148
10148
|
import { createTestContextFactory, createTestPorts } from "@beignet/core/testing";
|
|
10149
10149
|
import { ${options.auth ? "createTestUserActor" : "createTestAnonymousActor"}${options.tenant ? ", createTestTenant" : ""} } from "@beignet/core/ports/testing";
|
|
@@ -10192,13 +10192,15 @@ ${options.tenant ? `\t\t\t{\n\t\t\t\tid: "00000000-0000-4000-8000-000000000104",
|
|
|
10192
10192
|
overrides: {
|
|
10193
10193
|
${names.pluralCamel},
|
|
10194
10194
|
${
|
|
10195
|
-
options.auth
|
|
10195
|
+
options.auth || options.tenant
|
|
10196
10196
|
? `\t\t\t\tauth: {
|
|
10197
10197
|
getSession: async () => ({
|
|
10198
|
-
user: { id: "user_test", name: "Test User" },
|
|
10198
|
+
user: { id: "user_test", name: "Test User"${options.tenant ? `, tenantId: "tenant_test"` : ""} },
|
|
10199
|
+
${options.tenant ? `\t\t\t\t\t\tsession: { tenantId: "tenant_test" },\n` : ""}
|
|
10199
10200
|
}),
|
|
10200
10201
|
},
|
|
10201
|
-
|
|
10202
|
+
${options.auth ? `\t\t\t\tgate: appPorts.gate,\n` : ""}
|
|
10203
|
+
`
|
|
10202
10204
|
: `\t\t\t\tauth: {
|
|
10203
10205
|
getSession: async () => null,
|
|
10204
10206
|
},\n`
|
|
@@ -10348,7 +10350,6 @@ ${
|
|
|
10348
10350
|
routes: defineRoutes<AppContext>([${names.singularCamel}Routes]),
|
|
10349
10351
|
context: appContext,
|
|
10350
10352
|
});
|
|
10351
|
-
${options.tenant ? `\t\tconst requester = createTestRequester(app, {\n\t\t\theaders: { "x-tenant-id": "tenant_test" },\n\t\t});\n` : ""}
|
|
10352
10353
|
const createdViaRoute = await ${requestTarget}.request(create${names.singularPascal}, {
|
|
10353
10354
|
body: { name: "Second ${names.singularPascal}" },
|
|
10354
10355
|
});
|
package/src/templates/base.ts
CHANGED
|
@@ -419,7 +419,7 @@ export function envExample(ctx: TemplateContext): string {
|
|
|
419
419
|
"# Copy to .env.local and fill in values for your app.",
|
|
420
420
|
"APP_URL=http://localhost:3000",
|
|
421
421
|
"",
|
|
422
|
-
"# Dev-only default; remove in production.",
|
|
422
|
+
"# Dev-only default; remove or protect in production.",
|
|
423
423
|
"DEVTOOLS_ENABLED=true",
|
|
424
424
|
"",
|
|
425
425
|
"LOG_LEVEL=info",
|
|
@@ -428,7 +428,8 @@ export function envExample(ctx: TemplateContext): string {
|
|
|
428
428
|
"",
|
|
429
429
|
...databaseLines,
|
|
430
430
|
"",
|
|
431
|
-
"
|
|
431
|
+
"# Optional for local development; required in production.",
|
|
432
|
+
"# BETTER_AUTH_SECRET=replace-with-a-random-32-character-secret",
|
|
432
433
|
"BETTER_AUTH_URL=http://localhost:3000",
|
|
433
434
|
"# BETTER_AUTH_TRUSTED_ORIGINS=http://localhost:3000",
|
|
434
435
|
"",
|
|
@@ -115,7 +115,7 @@ export const todos = mysqlTable(
|
|
|
115
115
|
}),
|
|
116
116
|
);
|
|
117
117
|
`,
|
|
118
|
-
drizzleTodoRepository: `import
|
|
118
|
+
drizzleTodoRepository: `import "@beignet/core/server-only";
|
|
119
119
|
import { offsetPageResult } from "@beignet/core/pagination";
|
|
120
120
|
import type { DrizzleMysqlDatabase } from "@beignet/provider-db-drizzle/mysql";
|
|
121
121
|
import { count, desc, eq } from "drizzle-orm";
|
|
@@ -255,7 +255,7 @@ async function prepare(pool: Pool): Promise<void> {
|
|
|
255
255
|
await migrate(drizzle(pool), { migrationsFolder: "drizzle" });
|
|
256
256
|
}
|
|
257
257
|
`,
|
|
258
|
-
dbProvider: `import
|
|
258
|
+
dbProvider: `import "@beignet/core/server-only";
|
|
259
259
|
import { createProvider } from "@beignet/core/providers";
|
|
260
260
|
import {
|
|
261
261
|
createDrizzleMysqlIdempotencyPort,
|
|
@@ -105,7 +105,7 @@ export const todos = pgTable(
|
|
|
105
105
|
}),
|
|
106
106
|
);
|
|
107
107
|
`,
|
|
108
|
-
drizzleTodoRepository: `import
|
|
108
|
+
drizzleTodoRepository: `import "@beignet/core/server-only";
|
|
109
109
|
import { offsetPageResult } from "@beignet/core/pagination";
|
|
110
110
|
import type { DrizzlePostgresDatabase } from "@beignet/provider-db-drizzle/postgres";
|
|
111
111
|
import { count, desc, eq } from "drizzle-orm";
|
|
@@ -243,7 +243,7 @@ async function prepare(pool: Pool): Promise<void> {
|
|
|
243
243
|
await migrate(drizzle(pool), { migrationsFolder: "drizzle" });
|
|
244
244
|
}
|
|
245
245
|
`,
|
|
246
|
-
dbProvider: `import
|
|
246
|
+
dbProvider: `import "@beignet/core/server-only";
|
|
247
247
|
import { createProvider } from "@beignet/core/providers";
|
|
248
248
|
import {
|
|
249
249
|
createDrizzlePostgresIdempotencyPort,
|
|
@@ -101,7 +101,7 @@ export const todos = sqliteTable(
|
|
|
101
101
|
}),
|
|
102
102
|
);
|
|
103
103
|
`,
|
|
104
|
-
drizzleTodoRepository: `import
|
|
104
|
+
drizzleTodoRepository: `import "@beignet/core/server-only";
|
|
105
105
|
import { offsetPageResult } from "@beignet/core/pagination";
|
|
106
106
|
import type { DrizzleSqliteDatabase } from "@beignet/provider-db-drizzle/sqlite";
|
|
107
107
|
import { count, desc, eq } from "drizzle-orm";
|
|
@@ -242,7 +242,7 @@ async function prepare(client: Client): Promise<void> {
|
|
|
242
242
|
await migrate(drizzle(client), { migrationsFolder: "drizzle" });
|
|
243
243
|
}
|
|
244
244
|
`,
|
|
245
|
-
dbProvider: `import
|
|
245
|
+
dbProvider: `import "@beignet/core/server-only";
|
|
246
246
|
import { createProvider } from "@beignet/core/providers";
|
|
247
247
|
import {
|
|
248
248
|
createDrizzleSqliteIdempotencyPort,
|
package/src/templates/index.ts
CHANGED
|
@@ -132,6 +132,7 @@ export function getTemplateFiles(ctx: TemplateContext): TemplateFile[] {
|
|
|
132
132
|
{ path: "lib/env.ts", content: env(ctx) },
|
|
133
133
|
{ path: "lib/auth.ts", content: serverFiles.authHelpers },
|
|
134
134
|
{ path: "lib/better-auth.ts", content: betterAuth(ctx) },
|
|
135
|
+
{ path: "lib/tenant.ts", content: serverFiles.tenantHelper },
|
|
135
136
|
{ path: "lib/use-case.ts", content: serverFiles.useCaseBuilder },
|
|
136
137
|
{ path: "server/index.ts", content: server(ctx) },
|
|
137
138
|
{ path: "server/context.ts", content: serverFiles.serverContext },
|
package/src/templates/server.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
export function serverProviders(ctx: TemplateContext): string {
|
|
9
9
|
const db = databaseDescriptor(ctx);
|
|
10
10
|
const imports = [
|
|
11
|
-
'import
|
|
11
|
+
'import "@beignet/core/server-only";',
|
|
12
12
|
'import { createDevtoolsProvider } from "@beignet/devtools";',
|
|
13
13
|
'import { createAuthBetterAuthProvider } from "@beignet/provider-auth-better-auth";',
|
|
14
14
|
`import { ${db.providerFactory} } from "@beignet/provider-db-drizzle/${db.subpath}";`,
|
|
@@ -155,6 +155,19 @@ export function env(ctx: TemplateContext): string {
|
|
|
155
155
|
import { z } from "zod";
|
|
156
156
|
|
|
157
157
|
const BooleanEnv = z.enum(["true", "false"]).transform((value) => value === "true");
|
|
158
|
+
const LOCAL_BETTER_AUTH_SECRET = "local-dev-better-auth-secret-change-me";
|
|
159
|
+
const isProductionRuntime =
|
|
160
|
+
process.env.NODE_ENV === "production" &&
|
|
161
|
+
process.env.NEXT_PHASE !== "phase-production-build";
|
|
162
|
+
const BetterAuthSecret = z.string().min(32).superRefine((value, ctx) => {
|
|
163
|
+
if (isProductionRuntime && value === LOCAL_BETTER_AUTH_SECRET) {
|
|
164
|
+
ctx.addIssue({
|
|
165
|
+
code: "custom",
|
|
166
|
+
message:
|
|
167
|
+
"BETTER_AUTH_SECRET must be unique in production. Generate a strong secret instead of using the local development placeholder.",
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
});
|
|
158
171
|
|
|
159
172
|
export const env = createEnv({
|
|
160
173
|
server: {
|
|
@@ -163,10 +176,9 @@ export const env = createEnv({
|
|
|
163
176
|
.default("development"),
|
|
164
177
|
APP_URL: z.string().url().default("http://localhost:3000"),
|
|
165
178
|
DEVTOOLS_ENABLED: BooleanEnv.optional(),
|
|
166
|
-
BETTER_AUTH_SECRET:
|
|
167
|
-
|
|
168
|
-
.
|
|
169
|
-
.default("local-dev-better-auth-secret-change-me"),
|
|
179
|
+
BETTER_AUTH_SECRET: isProductionRuntime
|
|
180
|
+
? BetterAuthSecret
|
|
181
|
+
: BetterAuthSecret.default(LOCAL_BETTER_AUTH_SECRET),
|
|
170
182
|
BETTER_AUTH_URL: z.string().url().default("http://localhost:3000"),
|
|
171
183
|
BETTER_AUTH_TRUSTED_ORIGINS: z.string().optional(),
|
|
172
184
|
${databaseEnvLines}
|
|
@@ -283,7 +295,7 @@ export function server(ctx: TemplateContext): string {
|
|
|
283
295
|
\t\t\tDB_URL: env.${db.urlEnvVar},
|
|
284
296
|
\t\t},`;
|
|
285
297
|
|
|
286
|
-
return `import
|
|
298
|
+
return `import "@beignet/core/server-only";
|
|
287
299
|
import { createNextServer } from "@beignet/next";
|
|
288
300
|
import { createErrorReportingHooks, createIdempotencyHooks } from "@beignet/core/server";
|
|
289
301
|
import type { AppContext } from "@/app-context";
|
|
@@ -369,7 +381,7 @@ export const errors = defineErrors({
|
|
|
369
381
|
|
|
370
382
|
export const appError = createAppError(errors);
|
|
371
383
|
`,
|
|
372
|
-
useCaseBuilder: `import
|
|
384
|
+
useCaseBuilder: `import "@beignet/core/server-only";
|
|
373
385
|
import { createUseCase } from "@beignet/core/application";
|
|
374
386
|
import type { AppContext } from "@/app-context";
|
|
375
387
|
|
|
@@ -403,10 +415,14 @@ export type AuthUser = {
|
|
|
403
415
|
id: string;
|
|
404
416
|
email?: string;
|
|
405
417
|
name?: string;
|
|
418
|
+
tenantId?: string;
|
|
419
|
+
organizationId?: string;
|
|
406
420
|
};
|
|
407
421
|
|
|
408
422
|
export type AuthSessionMetadata = {
|
|
409
423
|
id?: string;
|
|
424
|
+
tenantId?: string;
|
|
425
|
+
organizationId?: string;
|
|
410
426
|
};
|
|
411
427
|
|
|
412
428
|
export type AuthSession = BeignetAuthSession<
|
|
@@ -420,7 +436,7 @@ export type AuthPort = BeignetAuthPort<
|
|
|
420
436
|
AuthRequest
|
|
421
437
|
>;
|
|
422
438
|
`,
|
|
423
|
-
serverRoutes: `import
|
|
439
|
+
serverRoutes: `import "@beignet/core/server-only";
|
|
424
440
|
import { contractsFromRoutes, defineRoutes } from "@beignet/next";
|
|
425
441
|
import type { AppContext } from "@/app-context";
|
|
426
442
|
import { todoRoutes } from "@/features/todos/routes";
|
|
@@ -430,17 +446,17 @@ export const routes = defineRoutes<AppContext>([
|
|
|
430
446
|
]);
|
|
431
447
|
export const contracts = contractsFromRoutes(routes);
|
|
432
448
|
`,
|
|
433
|
-
serverContext: `import
|
|
449
|
+
serverContext: `import "@beignet/core/server-only";
|
|
434
450
|
import {
|
|
435
451
|
type ActivityActor,
|
|
436
452
|
createAnonymousActor,
|
|
437
453
|
createServiceActor,
|
|
438
|
-
createTenant,
|
|
439
454
|
createUserActor,
|
|
440
455
|
} from "@beignet/core/ports";
|
|
441
456
|
import { defineServerContext } from "@beignet/core/server";
|
|
442
457
|
import type { TraceContext } from "@beignet/core/tracing";
|
|
443
458
|
import type { AppContext, AppRuntimePorts } from "@/app-context";
|
|
459
|
+
import { resolveRequestTenant, resolveServiceTenant } from "@/lib/tenant";
|
|
444
460
|
|
|
445
461
|
export type AppServiceContextInput =
|
|
446
462
|
| {
|
|
@@ -456,7 +472,7 @@ export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
|
|
|
456
472
|
gate: (ports) => ports.gate,
|
|
457
473
|
request: async ({ req, ports, requestId, trace }) => {
|
|
458
474
|
const auth = await ports.auth.getSession(req);
|
|
459
|
-
const
|
|
475
|
+
const tenant = resolveRequestTenant({ auth });
|
|
460
476
|
|
|
461
477
|
return {
|
|
462
478
|
requestId,
|
|
@@ -466,7 +482,7 @@ export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
|
|
|
466
482
|
auth,
|
|
467
483
|
...trace,
|
|
468
484
|
ports,
|
|
469
|
-
tenant
|
|
485
|
+
...(tenant ? { tenant } : {}),
|
|
470
486
|
};
|
|
471
487
|
},
|
|
472
488
|
service: ({
|
|
@@ -479,15 +495,67 @@ export const appContext = defineServerContext<AppContext, AppRuntimePorts>()({
|
|
|
479
495
|
input: AppServiceContextInput;
|
|
480
496
|
requestId: string;
|
|
481
497
|
trace: TraceContext;
|
|
482
|
-
}) =>
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
498
|
+
}) => {
|
|
499
|
+
const tenant = resolveServiceTenant(input?.tenantId);
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
requestId,
|
|
503
|
+
actor: input?.actor ?? createServiceActor("beignet-service"),
|
|
504
|
+
auth: null,
|
|
505
|
+
...trace,
|
|
506
|
+
ports,
|
|
507
|
+
...(tenant ? { tenant } : {}),
|
|
508
|
+
};
|
|
509
|
+
},
|
|
490
510
|
});
|
|
511
|
+
`,
|
|
512
|
+
tenantHelper: `import { type ActivityTenant, createTenant } from "@beignet/core/ports";
|
|
513
|
+
import type { AuthSession } from "@/ports/auth";
|
|
514
|
+
|
|
515
|
+
export type TenantResolutionInput = {
|
|
516
|
+
auth: AuthSession | null;
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
export function resolveRequestTenant({
|
|
520
|
+
auth,
|
|
521
|
+
}: TenantResolutionInput): ActivityTenant | undefined {
|
|
522
|
+
const tenantId = auth ? tenantIdFromAuth(auth) : undefined;
|
|
523
|
+
|
|
524
|
+
return tenantId ? createTenant(tenantId) : undefined;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function resolveServiceTenant(
|
|
528
|
+
tenantId: string | undefined,
|
|
529
|
+
): ActivityTenant | undefined {
|
|
530
|
+
const normalizedTenantId = normalizeTenantId(tenantId);
|
|
531
|
+
|
|
532
|
+
return normalizedTenantId ? createTenant(normalizedTenantId) : undefined;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function tenantIdFromAuth(auth: AuthSession) {
|
|
536
|
+
return (
|
|
537
|
+
stringProperty(auth.session, "tenantId") ??
|
|
538
|
+
stringProperty(auth.session, "organizationId") ??
|
|
539
|
+
stringProperty(auth.user, "tenantId") ??
|
|
540
|
+
stringProperty(auth.user, "organizationId")
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function stringProperty(value: unknown, key: string) {
|
|
545
|
+
if (!isRecord(value)) return undefined;
|
|
546
|
+
|
|
547
|
+
return normalizeTenantId(value[key]);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function normalizeTenantId(value: unknown) {
|
|
551
|
+
return typeof value === "string" && value.trim().length > 0
|
|
552
|
+
? value.trim()
|
|
553
|
+
: undefined;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
557
|
+
return typeof value === "object" && value !== null;
|
|
558
|
+
}
|
|
491
559
|
`,
|
|
492
560
|
apiCatchAllRoute: `import { server } from "@/server";
|
|
493
561
|
|
|
@@ -553,11 +621,13 @@ export const { GET, POST } = createDevtoolsRoute(server.ports.devtools, {
|
|
|
553
621
|
});
|
|
554
622
|
`,
|
|
555
623
|
openApiRoute: `import { createOpenAPIHandler } from "@beignet/next";
|
|
624
|
+
import { env } from "@/lib/env";
|
|
556
625
|
import { server } from "@/server";
|
|
557
626
|
|
|
558
627
|
export const GET = createOpenAPIHandler(server.contracts, {
|
|
559
628
|
title: "Beignet starter API",
|
|
560
629
|
version: "0.1.0",
|
|
630
|
+
servers: [{ url: env.APP_URL }],
|
|
561
631
|
});
|
|
562
632
|
`,
|
|
563
633
|
authRoute: `import { toNextJsHandler } from "better-auth/next-js";
|
package/src/templates/todos.ts
CHANGED
|
@@ -174,7 +174,7 @@ export const todoPolicy = definePolicy({
|
|
|
174
174
|
authorizeOwner(ctx, todo, "delete"),
|
|
175
175
|
});
|
|
176
176
|
`,
|
|
177
|
-
routes: `import
|
|
177
|
+
routes: `import "@beignet/core/server-only";
|
|
178
178
|
import { defineRouteGroup } from "@beignet/next";
|
|
179
179
|
import type { AppContext } from "@/app-context";
|
|
180
180
|
import {
|
|
@@ -200,7 +200,7 @@ export const todoRoutes = defineRouteGroup<AppContext>()({
|
|
|
200
200
|
],
|
|
201
201
|
});
|
|
202
202
|
`,
|
|
203
|
-
listTodosUseCase: `import
|
|
203
|
+
listTodosUseCase: `import "@beignet/core/server-only";
|
|
204
204
|
import { normalizeOffsetPage } from "@beignet/core/pagination";
|
|
205
205
|
import { requireUser } from "@/lib/auth";
|
|
206
206
|
import { useCase } from "@/lib/use-case";
|
|
@@ -220,7 +220,7 @@ export const listTodosUseCase = useCase
|
|
|
220
220
|
return ctx.ports.todos.list(user.id, page);
|
|
221
221
|
});
|
|
222
222
|
`,
|
|
223
|
-
createTodoUseCase: `import
|
|
223
|
+
createTodoUseCase: `import "@beignet/core/server-only";
|
|
224
224
|
import { requireUser } from "@/lib/auth";
|
|
225
225
|
import { useCase } from "@/lib/use-case";
|
|
226
226
|
import { CreateTodoInputSchema, TodoSchema } from "../schemas";
|
|
@@ -238,7 +238,7 @@ export const createTodoUseCase = useCase
|
|
|
238
238
|
);
|
|
239
239
|
});
|
|
240
240
|
`,
|
|
241
|
-
updateTodoUseCase: `import
|
|
241
|
+
updateTodoUseCase: `import "@beignet/core/server-only";
|
|
242
242
|
import { appError } from "@/features/shared/errors";
|
|
243
243
|
import { requireUser } from "@/lib/auth";
|
|
244
244
|
import { useCase } from "@/lib/use-case";
|
|
@@ -263,7 +263,7 @@ export const updateTodoUseCase = useCase
|
|
|
263
263
|
});
|
|
264
264
|
});
|
|
265
265
|
`,
|
|
266
|
-
deleteTodoUseCase: `import
|
|
266
|
+
deleteTodoUseCase: `import "@beignet/core/server-only";
|
|
267
267
|
import { appError } from "@/features/shared/errors";
|
|
268
268
|
import { requireUser } from "@/lib/auth";
|
|
269
269
|
import { useCase } from "@/lib/use-case";
|