betterstart-cli 0.0.102 → 0.0.104

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.
Files changed (147) hide show
  1. package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/index.ts +0 -5
  2. package/dist/assets/adapters/next/templates/init/lib/actions/webhooks/send-test-webhook.ts +1 -1
  3. package/dist/assets/adapters/next/templates/renderers/commands/init/banner-art.ts +7 -0
  4. package/dist/assets/adapters/next/templates/renderers/commands/init/verify-database-reachable.ts +30 -0
  5. package/dist/assets/adapters/next/templates/renderers/commands/seed/build-seed-script.ts +171 -0
  6. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-alias-block.ts +4 -0
  7. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-authorship-data-fields.ts +4 -0
  8. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-format-action-date-source.ts +18 -0
  9. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-read-select-option-type.ts +6 -0
  10. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-relationship-data-types.ts +25 -0
  11. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/build-result-mapping.ts +62 -0
  12. package/dist/assets/adapters/next/templates/renderers/generators/actions/action-helpers/generate-populate-list-rels-function.ts +50 -0
  13. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-restore-many-to-many-statements.ts +27 -0
  14. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/build-select-option-types.ts +37 -0
  15. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-bulk-sort-order-content.ts +93 -0
  16. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-content.ts +157 -0
  17. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-create-select-option-content.ts +134 -0
  18. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-bulk-content.ts +85 -0
  19. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-delete-content.ts +66 -0
  20. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-distinct-files.ts +45 -0
  21. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-id-content.ts +55 -0
  22. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-by-slug-content.ts +55 -0
  23. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-plural-content.ts +106 -0
  24. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-select-options-content.ts +55 -0
  25. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-get-versions-content.ts +19 -0
  26. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-m2m-files.ts +151 -0
  27. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-restore-content.ts +123 -0
  28. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/gen-update-content.ts +187 -0
  29. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-resolve-media-function.ts +40 -0
  30. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-slug-helpers.ts +73 -0
  31. package/dist/assets/adapters/next/templates/renderers/generators/actions/entity-file-contents/generate-version-snapshot-helpers.ts +104 -0
  32. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-context.ts +22 -0
  33. package/dist/assets/adapters/next/templates/renderers/generators/actions/lifecycle-hook-emissions/lifecycle-hook-import.ts +4 -0
  34. package/dist/assets/adapters/next/templates/renderers/generators/actions/markdown-renderer.ts +411 -0
  35. package/dist/assets/adapters/next/templates/renderers/generators/actions/select-fields-resolver.ts +99 -0
  36. package/dist/assets/adapters/next/templates/renderers/generators/actions/single-actions/generate-single-actions.ts +292 -0
  37. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-authorship-value.ts +8 -0
  38. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-column-def.ts +297 -0
  39. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-first-column-def.ts +483 -0
  40. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-cell-media.ts +38 -0
  41. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-image-element.ts +22 -0
  42. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-relationship-label.ts +26 -0
  43. package/dist/assets/adapters/next/templates/renderers/generators/columns/column-defs/generate-select-label.ts +17 -0
  44. package/dist/assets/adapters/next/templates/renderers/generators/columns/custom-cell.ts +24 -0
  45. package/dist/assets/adapters/next/templates/renderers/generators/columns/generate-columns.ts +55 -0
  46. package/dist/assets/adapters/next/templates/renderers/generators/create-page.ts +16 -0
  47. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-database.ts +5 -0
  48. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-junction-table.ts +27 -0
  49. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-select-options-table.ts +35 -0
  50. package/dist/assets/adapters/next/templates/renderers/generators/database/generate-table-definition.ts +25 -0
  51. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-entity-dev-mode-snippets.ts +180 -0
  52. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-form-dev-mode-snippets.ts +97 -0
  53. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/build-single-dev-mode-snippets.ts +60 -0
  54. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/create-dev-mode-integration-artifact.ts +89 -0
  55. package/dist/assets/adapters/next/templates/renderers/generators/dev-mode-integration/lifecycle-hook-code.ts +55 -0
  56. package/dist/assets/adapters/next/templates/renderers/generators/edit-page.ts +33 -0
  57. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-email-template.ts +248 -0
  58. package/dist/assets/adapters/next/templates/renderers/generators/email-template/generate-field-value.ts +31 -0
  59. package/dist/assets/adapters/next/templates/renderers/generators/emitted-helpers.ts +16 -0
  60. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/generate-icon-postfix.ts +27 -0
  61. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-boolean-field.ts +47 -0
  62. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-creatable-select-field.ts +122 -0
  63. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-date-field.ts +47 -0
  64. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-default-field.ts +53 -0
  65. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-gallery-field.ts +46 -0
  66. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-group-field.ts +16 -0
  67. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-icon-field.ts +31 -0
  68. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-markdown-field.ts +50 -0
  69. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-media-field.ts +47 -0
  70. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-relationship-field.ts +91 -0
  71. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-multi-select-field.ts +219 -0
  72. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-number-field.ts +48 -0
  73. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-richtext-field.ts +50 -0
  74. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-select-field.ts +61 -0
  75. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-separator-field.ts +16 -0
  76. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-single-relationship-field.ts +93 -0
  77. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx/render-textarea-field.ts +53 -0
  78. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-list-field.ts +163 -0
  79. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-field.ts +164 -0
  80. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-nested-object-list-field.ts +59 -0
  81. package/dist/assets/adapters/next/templates/renderers/generators/form/field-jsx-nested/render-text-input-field.ts +27 -0
  82. package/dist/assets/adapters/next/templates/renderers/generators/form/form-entity/generate-form.ts +822 -0
  83. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-default-value-from-sync-functions.ts +41 -0
  84. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/build-ui-imports.ts +58 -0
  85. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-default-value.ts +67 -0
  86. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/generate-nested-list-default-value.ts +13 -0
  87. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/label-with-description.ts +14 -0
  88. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/render-tabs-trigger.ts +18 -0
  89. package/dist/assets/adapters/next/templates/renderers/generators/form/form-shared/select-read-values-expression.ts +21 -0
  90. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-handlers.ts +67 -0
  91. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-creatable-select-state.ts +29 -0
  92. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-field-array-hooks.ts +13 -0
  93. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/build-group-fields-jsx.ts +30 -0
  94. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-card-component.ts +129 -0
  95. package/dist/assets/adapters/next/templates/renderers/generators/form/form-single/generate-single-form.ts +66 -0
  96. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-column-def.ts +72 -0
  97. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-columns/generate-columns.ts +89 -0
  98. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page/generate-page.ts +23 -0
  99. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-page-content.ts +246 -0
  100. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-submission-drawer.ts +216 -0
  101. package/dist/assets/adapters/next/templates/renderers/generators/form-admin/admin-table.ts +273 -0
  102. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/exclusive-date-upper-bound-helper.ts +11 -0
  103. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-actions/generate-form-actions.ts +505 -0
  104. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-multistep/build-component-source.ts +197 -0
  105. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/build-field-error-helper.ts +16 -0
  106. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/error-message-line.ts +13 -0
  107. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-field-jsx.ts +226 -0
  108. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-file-upload-field-jsx.ts +41 -0
  109. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-list-field-jsx.ts +160 -0
  110. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-multiselect-field-jsx.ts +40 -0
  111. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-number-field-jsx.ts +41 -0
  112. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/generate-text-field-jsx.ts +39 -0
  113. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-shared/label-with-description-line.ts +14 -0
  114. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-component-single/build-single-step-form-component-source.ts +100 -0
  115. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-database.ts +29 -0
  116. package/dist/assets/adapters/next/templates/renderers/generators/form-pipeline/form-hook.ts +142 -0
  117. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-hook.ts +169 -0
  118. package/dist/assets/adapters/next/templates/renderers/generators/hook/generate-single-hook.ts +30 -0
  119. package/dist/assets/adapters/next/templates/renderers/generators/navigation-extract.ts +19 -0
  120. package/dist/assets/adapters/next/templates/renderers/generators/page-content/generate-page-content.ts +419 -0
  121. package/dist/assets/adapters/next/templates/renderers/generators/page.ts +24 -0
  122. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-delete-dialog-source.ts +108 -0
  123. package/dist/assets/adapters/next/templates/renderers/generators/row-actions-emitters/build-row-actions-source.ts +61 -0
  124. package/dist/assets/adapters/next/templates/renderers/generators/single-page.ts +59 -0
  125. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sort-label-expr.ts +9 -0
  126. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-button-jsx.ts +20 -0
  127. package/dist/assets/adapters/next/templates/renderers/generators/sortable-header/build-sortable-header-def.ts +14 -0
  128. package/dist/assets/adapters/next/templates/renderers/generators/table.ts +284 -0
  129. package/dist/assets/adapters/next/templates/renderers/generators/thankyou-email-template.ts +110 -0
  130. package/dist/assets/adapters/next/templates/renderers/generators/webhook-events/render-extract.ts +14 -0
  131. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/next-config/create-next-config-template.ts +48 -0
  132. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-shadow-input-utility.ts +9 -0
  133. package/dist/assets/adapters/next/templates/renderers/init/scaffolders/tailwind/admin-theme-block.ts +42 -0
  134. package/dist/assets/adapters/next/templates/renderers/init/templates/app/admin.ts +12 -0
  135. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-email-provider.ts +27 -0
  136. package/dist/assets/adapters/next/templates/renderers/integration-runtime/render-core-storage-provider.ts +27 -0
  137. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-db-barrel.ts +8 -0
  138. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-navigation-barrel.ts +63 -0
  139. package/dist/assets/adapters/next/templates/renderers/snapshots/barrels/render-webhook-events-barrel.ts +16 -0
  140. package/dist/{chunk-TIJEHWRF.js → chunk-OYU73Q2W.js} +1 -50
  141. package/dist/chunk-OYU73Q2W.js.map +1 -0
  142. package/dist/cli.js +8903 -4025
  143. package/dist/cli.js.map +1 -1
  144. package/dist/{read-namespaced-template-UXU7OCY3.js → read-namespaced-template-EIH2NPDJ.js} +2 -2
  145. package/package.json +1 -1
  146. package/dist/chunk-TIJEHWRF.js.map +0 -1
  147. /package/dist/{read-namespaced-template-UXU7OCY3.js.map → read-namespaced-template-EIH2NPDJ.js.map} +0 -0
@@ -1,8 +1,3 @@
1
- // Client-safe barrel: only types and server actions. Server-only modules are
2
- // imported by path — emitWebhookEvent from './dispatch' and
3
- // registerWebhookHooks from './register' — so client components can import
4
- // this barrel without pulling node:crypto into the bundle.
5
-
6
1
  export { createWebhook } from './create-webhook'
7
2
  export { deleteWebhook } from './delete-webhook'
8
3
  export { getWebhookDeliveries } from './get-webhook-deliveries'
@@ -1,6 +1,7 @@
1
1
  'use server'
2
2
 
3
3
  import type { SendTestWebhookResult } from './types'
4
+ import { recordWebhookDelivery } from '@admin/actions/webhooks/record-webhook-delivery'
4
5
  import { requireRole, UserRole } from '@admin/auth/middleware'
5
6
  import db from '@admin/db'
6
7
  import { webhooks } from '@admin/db/schema'
@@ -8,7 +9,6 @@ import { toActionError } from '@admin/utils/error/to-action-error'
8
9
  import { createWebhookEventId } from '@admin/utils/webhook/create-webhook-event-id'
9
10
  import { signWebhookPayload } from '@admin/utils/webhook/sign-webhook-payload'
10
11
  import { eq } from 'drizzle-orm'
11
- import { recordWebhookDelivery } from './dispatch'
12
12
 
13
13
  const TEST_REQUEST_TIMEOUT_MS = 10_000
14
14
 
@@ -0,0 +1,7 @@
1
+ export function renderInitBannerArt(): string {
2
+ return `
3
+ ▄ ▗ ▗ ▄▖▗ ▗
4
+ ▙▘█▌▜▘▜▘█▌▛▘▚ ▜▘▀▌▛▘▜▘
5
+ ▙▘▙▖▐▖▐▖▙▖▌ ▄▌▐▖█▌▌ ▐▖
6
+ `
7
+ }
@@ -0,0 +1,30 @@
1
+ export function renderVerifyDatabaseReachableRequireFromProjectTemplate(): string {
2
+ return `
3
+ const { createRequire } = require('node:module')
4
+ const requireFromProject = createRequire(process.cwd() + '/package.json')
5
+ const { Client } = requireFromProject('pg')
6
+ const client = new Client({
7
+ connectionString: process.env.DATABASE_URL,
8
+ connectionTimeoutMillis: 10000
9
+ })
10
+
11
+ async function main() {
12
+ try {
13
+ await client.connect()
14
+ await client.query('select 1')
15
+ } catch (error) {
16
+ const message = error instanceof Error ? error.message : String(error)
17
+ console.error('Database connection failed: ' + message)
18
+ process.exitCode = 1
19
+ } finally {
20
+ await client.end().catch(() => {})
21
+ }
22
+ }
23
+
24
+ main().catch((error) => {
25
+ const message = error instanceof Error ? error.message : String(error)
26
+ console.error('Database connection failed: ' + message)
27
+ process.exitCode = 1
28
+ })
29
+ `
30
+ }
@@ -0,0 +1,171 @@
1
+ export function renderBuildSeedScriptEnvFileTemplate({
2
+ authBasePath
3
+ }: {
4
+ authBasePath: string
5
+ }): string {
6
+ return `/**
7
+ * AUTO-GENERATED by BetterStart — safe to delete after running.
8
+ */
9
+
10
+ import { readFileSync } from 'node:fs'
11
+ import { resolve } from 'node:path'
12
+
13
+ // Parsed by hand to avoid a @next/env dependency in the generated project.
14
+ for (const envFile of ['.env.local', '.env']) {
15
+ try {
16
+ const content = readFileSync(resolve(process.cwd(), envFile), 'utf-8')
17
+ for (const line of content.split('\\n')) {
18
+ const trimmed = line.trim()
19
+ if (!trimmed || trimmed.startsWith('#')) continue
20
+ const eqIdx = trimmed.indexOf('=')
21
+ if (eqIdx === -1) continue
22
+ const key = trimmed.slice(0, eqIdx).trim()
23
+ const val = trimmed.slice(eqIdx + 1).trim().replace(/^['"]|['"]$/g, '')
24
+ if (!process.env[key]) process.env[key] = val
25
+ }
26
+ } catch {}
27
+ }
28
+
29
+ import { drizzle } from 'drizzle-orm/postgres-js'
30
+ import { eq } from 'drizzle-orm'
31
+ import postgres from 'postgres'
32
+ import * as schema from '../lib/db/schema'
33
+ import { betterAuth } from 'better-auth'
34
+ import { drizzleAdapter } from 'better-auth/adapters/drizzle'
35
+
36
+ // Inline DB connection (mirrors generated lib/db/client.ts)
37
+ const databaseUrl = process.env.DATABASE_URL
38
+ if (!databaseUrl) {
39
+ throw new Error('DATABASE_URL is required')
40
+ }
41
+
42
+ const client = postgres(databaseUrl)
43
+ const db = drizzle({ client, schema })
44
+
45
+ // Inline auth setup (mirrors generated lib/actions/auth/server.ts)
46
+ const auth = betterAuth({
47
+ secret: process.env.BETTERSTART_AUTH_SECRET,
48
+ baseURL: process.env.BETTERSTART_AUTH_URL,
49
+ basePath: process.env.BETTERSTART_AUTH_BASE_PATH || '${authBasePath}',
50
+ database: drizzleAdapter(db, {
51
+ provider: 'pg',
52
+ schema: {
53
+ user: schema.user,
54
+ session: schema.session,
55
+ account: schema.account,
56
+ verification: schema.verification,
57
+ },
58
+ }),
59
+ emailAndPassword: { enabled: true, minPasswordLength: 8 },
60
+ user: {
61
+ additionalFields: {
62
+ role: { type: 'string', required: false, defaultValue: 'member', input: false },
63
+ },
64
+ },
65
+ })
66
+
67
+ const CHECK_ADMIN_ONLY = process.env.SEED_CHECK_ADMIN === 'true'
68
+ const OVERWRITE_MODE = process.env.SEED_OVERWRITE_MODE
69
+
70
+ async function clearAuthorshipReferences(userId: string) {
71
+ for (const table of Object.values(schema)) {
72
+ if (!table || typeof table !== 'object') continue
73
+ const tableRef = table as unknown as Record<string, unknown>
74
+
75
+ if (tableRef.createdBy) {
76
+ await db
77
+ .update(table as never)
78
+ .set({ createdBy: null })
79
+ .where(eq(tableRef.createdBy as never, userId))
80
+ }
81
+
82
+ if (tableRef.updatedBy) {
83
+ await db
84
+ .update(table as never)
85
+ .set({ updatedBy: null })
86
+ .where(eq(tableRef.updatedBy as never, userId))
87
+ }
88
+ }
89
+ }
90
+
91
+ async function main() {
92
+ const existingAdmin = await db
93
+ .select({ id: schema.user.id, email: schema.user.email, name: schema.user.name })
94
+ .from(schema.user)
95
+ .where(eq(schema.user.role, 'admin'))
96
+ .then((rows: { id: string; email: string; name: string }[]) => rows[0])
97
+
98
+ if (CHECK_ADMIN_ONLY) {
99
+ if (existingAdmin) {
100
+ console.log(\`EXISTING_ADMIN:\${JSON.stringify(existingAdmin)}\`)
101
+ process.exit(3)
102
+ }
103
+ process.exit(0)
104
+ }
105
+
106
+ const EMAIL = process.env.SEED_EMAIL
107
+ const PASSWORD = process.env.SEED_PASSWORD
108
+ const NAME = process.env.SEED_NAME || 'Admin'
109
+
110
+ if (!EMAIL || !PASSWORD) {
111
+ console.error(' SEED_EMAIL and SEED_PASSWORD are required.')
112
+ process.exit(1)
113
+ }
114
+
115
+ const existingUser = await db
116
+ .select({ id: schema.user.id, name: schema.user.name })
117
+ .from(schema.user)
118
+ .where(eq(schema.user.email, EMAIL))
119
+ .then((rows: { id: string; name: string }[]) => rows[0])
120
+
121
+ const replaceTarget =
122
+ OVERWRITE_MODE === 'admin' ? existingAdmin : OVERWRITE_MODE === 'email' ? existingUser : undefined
123
+
124
+ if (existingUser && !replaceTarget) {
125
+ // Exit code 2 signals "user exists" to the CLI
126
+ console.log(\`EXISTING_USER:\${existingUser.name}\`)
127
+ process.exit(2)
128
+ }
129
+
130
+ if (replaceTarget) {
131
+ console.log(
132
+ OVERWRITE_MODE === 'admin'
133
+ ? '\\n Replacing existing admin user...'
134
+ : '\\n Replacing existing account...'
135
+ )
136
+ await clearAuthorshipReferences(replaceTarget.id)
137
+ // Remove existing account + session rows first (foreign key refs)
138
+ await db.delete(schema.session).where(eq(schema.session.userId, replaceTarget.id))
139
+ await db.delete(schema.account).where(eq(schema.account.userId, replaceTarget.id))
140
+ await db.delete(schema.user).where(eq(schema.user.id, replaceTarget.id))
141
+ } else {
142
+ console.log('\\n Creating admin user...')
143
+ }
144
+
145
+ console.log(\` Email: \${EMAIL}\\n\`)
146
+
147
+ const result = await auth.api.signUpEmail({
148
+ body: { email: EMAIL, password: PASSWORD, name: NAME },
149
+ })
150
+
151
+ if (!result?.user) {
152
+ console.error(' Failed to create user.')
153
+ process.exit(1)
154
+ }
155
+
156
+ await db
157
+ .update(schema.user)
158
+ .set({ role: 'admin' })
159
+ .where(eq(schema.user.id, result.user.id))
160
+
161
+ console.log(\` Admin user \${replaceTarget ? 'replaced' : 'created'}: \${EMAIL}\`)
162
+ console.log(' Role: admin\\n')
163
+ process.exit(0)
164
+ }
165
+
166
+ main().catch((err) => {
167
+ console.error(' Seed failed:', err.message || err)
168
+ process.exit(1)
169
+ })
170
+ `
171
+ }
@@ -0,0 +1,4 @@
1
+ export function renderBuildAuthorshipAliasBlockCreatedByUserTemplate(): string {
2
+ return `const createdByUser = alias(user, 'created_by_user')
3
+ const updatedByUser = alias(user, 'updated_by_user')`
4
+ }
@@ -0,0 +1,4 @@
1
+ export function renderBuildAuthorshipDataFieldsContentTemplate(): string {
2
+ return ` createdByUser: AdminUserSummary | null
3
+ updatedByUser: AdminUserSummary | null`
4
+ }
@@ -0,0 +1,18 @@
1
+ export function renderBuildFormatActionDateSourceDateTemplate({
2
+ optionsExportedExport
3
+ }: {
4
+ optionsExportedExport: string
5
+ }): string {
6
+ return `${optionsExportedExport}function formatActionDate(value: unknown): string {
7
+ if (typeof value !== 'string') return ''
8
+ const date = new Date(value)
9
+ if (Number.isNaN(date.getTime())) return ''
10
+
11
+ return date.toLocaleDateString('en-US', {
12
+ month: 'short',
13
+ day: '2-digit',
14
+ year: 'numeric',
15
+ timeZone: 'UTC'
16
+ })
17
+ }`
18
+ }
@@ -0,0 +1,6 @@
1
+ export function renderBuildReadSelectOptionTypeComponentTemplate(): string {
2
+ return `export interface SelectOptionValue {
3
+ label: string
4
+ value: string
5
+ }`
6
+ }
@@ -0,0 +1,25 @@
1
+ export function renderBuildRelationshipDataTypesComponentTemplate({
2
+ typeName
3
+ }: {
4
+ typeName: string
5
+ }): string {
6
+ return `export interface ${typeName} {
7
+ id: string
8
+ name?: string | null
9
+ email?: string | null
10
+ }`
11
+ }
12
+
13
+ export function renderBuildRelationshipDataTypesComponentDetailsTemplate({
14
+ typeName
15
+ }: {
16
+ typeName: string
17
+ }): string {
18
+ return `export interface ${typeName} {
19
+ id: string
20
+ name?: string | null
21
+ title?: string | null
22
+ slug?: string | null
23
+ label?: string | null
24
+ }`
25
+ }
@@ -0,0 +1,62 @@
1
+ export function renderBuildResultMappingResultTemplate({
2
+ plural,
3
+ rowDeclarations,
4
+ rowMappings
5
+ }: {
6
+ plural: string
7
+ rowDeclarations: string
8
+ rowMappings: string
9
+ }): string {
10
+ return `
11
+
12
+ const mapped${plural} = results.map((rawRow) => {
13
+ const row = rawRow as Record<string, unknown>
14
+ ${rowDeclarations}
15
+
16
+ return {
17
+ ${rowMappings}
18
+ }
19
+ })`
20
+ }
21
+
22
+ export function renderBuildResultMappingResultDetailsTemplate({
23
+ mapBlock,
24
+ plural,
25
+ singular,
26
+ camelPlural
27
+ }: {
28
+ mapBlock: string
29
+ plural: string
30
+ singular: string
31
+ camelPlural: string
32
+ }): string {
33
+ return `${mapBlock}
34
+
35
+ const populated${plural} = await Promise.all(
36
+ mapped${plural}.map(record => populate${singular}ListRelationships(record as ${singular}))
37
+ )
38
+
39
+ return {
40
+ ${camelPlural}: populated${plural},
41
+ total
42
+ }`
43
+ }
44
+
45
+ export function renderBuildResultMappingResultDetailsBranchTemplate({
46
+ mapBlock,
47
+ camelPlural,
48
+ plural,
49
+ singular
50
+ }: {
51
+ mapBlock: string
52
+ camelPlural: string
53
+ plural: string
54
+ singular: string
55
+ }): string {
56
+ return `${mapBlock}
57
+
58
+ return {
59
+ ${camelPlural}: mapped${plural} as unknown as ${singular}[],
60
+ total
61
+ }`
62
+ }
@@ -0,0 +1,50 @@
1
+ export function renderPopulateListRelsFunctionResultTemplate({
2
+ v,
3
+ qListFieldName,
4
+ qRelFieldName,
5
+ qRelTable
6
+ }: {
7
+ v: string
8
+ qListFieldName: string
9
+ qRelFieldName: string
10
+ qRelTable: string
11
+ }): string {
12
+ return ` const ${v}Ids = record.${qListFieldName} && Array.isArray(record.${qListFieldName})
13
+ ? (record.${qListFieldName} as Record<string, unknown>[])
14
+ .map((item) => item.${qRelFieldName})
15
+ .filter((id) => id != null && id !== '')
16
+ .map((id) => String(id))
17
+ : []
18
+
19
+ const ${v}Results = ${v}Ids.length > 0
20
+ ? await db.select().from(${qRelTable}).where(inArray(${qRelTable}.id, ${v}Ids))
21
+ : []
22
+
23
+ const ${v}Map = new Map(${v}Results.map((r) => [r.id, r]))
24
+
25
+ if (record.${qListFieldName} && Array.isArray(record.${qListFieldName})) {
26
+ record.${qListFieldName} = (record.${qListFieldName} as Record<string, unknown>[]).map((item) => {
27
+ const relId = item.${qRelFieldName} ? String(item.${qRelFieldName}) : null
28
+ return {
29
+ ...item,
30
+ ${qRelFieldName}: relId ? ${v}Map.get(relId) || null : null
31
+ }
32
+ })
33
+ }`
34
+ }
35
+
36
+ export function renderPopulateListRelsFunctionContentTemplate({
37
+ singular,
38
+ join
39
+ }: {
40
+ singular: string
41
+ join: string
42
+ }): string {
43
+ return `
44
+ async function populate${singular}ListRelationships(record: ${singular}): Promise<${singular}> {
45
+ ${join}
46
+
47
+ return record
48
+ }
49
+ `
50
+ }
@@ -0,0 +1,27 @@
1
+ export function renderBuildRestoreManyToManyStatementsValueTemplate({
2
+ fieldName,
3
+ junctionVar,
4
+ entityIdCol,
5
+ relSingular,
6
+ relIdCol
7
+ }: {
8
+ fieldName: string
9
+ junctionVar: string
10
+ entityIdCol: string
11
+ relSingular: string
12
+ relIdCol: string
13
+ }): string {
14
+ return ` const ${fieldName}Value = versionData.${fieldName}
15
+ const ${fieldName}Ids = Array.isArray(${fieldName}Value)
16
+ ? ${fieldName}Value.filter((id): id is string => typeof id === 'string')
17
+ : []
18
+ await tx.delete(${junctionVar}).where(eq(${junctionVar}.${entityIdCol}, versionRow.entityId))
19
+ if (${fieldName}Ids.length > 0) {
20
+ await tx.insert(${junctionVar}).values(
21
+ ${fieldName}Ids.map((${relSingular}Id) => ({
22
+ ${entityIdCol}: versionRow.entityId,
23
+ ${relIdCol}: ${relSingular}Id
24
+ }))
25
+ )
26
+ }`
27
+ }
@@ -0,0 +1,37 @@
1
+ export function renderBuildSelectOptionTypesComponentTemplate({
2
+ ctxSingular,
3
+ fieldNames,
4
+ ctxCamelSingular,
5
+ ctxPlural
6
+ }: {
7
+ ctxSingular: string
8
+ fieldNames: string
9
+ ctxCamelSingular: string
10
+ ctxPlural: string
11
+ }): string {
12
+ return `export type ${ctxSingular}SelectOptionFieldName = ${fieldNames}
13
+
14
+ export interface ${ctxSingular}SelectOption {
15
+ id: string
16
+ fieldName: ${ctxSingular}SelectOptionFieldName
17
+ label: string
18
+ value: string
19
+ createdAt: string
20
+ updatedAt: string
21
+ }
22
+
23
+ export interface Create${ctxSingular}SelectOptionInput {
24
+ fieldName: ${ctxSingular}SelectOptionFieldName
25
+ label: string
26
+ }
27
+
28
+ export interface Create${ctxSingular}SelectOptionResult {
29
+ success: boolean
30
+ error?: string
31
+ option?: ${ctxSingular}SelectOption
32
+ }
33
+
34
+ export const ${ctxCamelSingular}SelectOptionsCacheTags = {
35
+ byField: (fieldName: ${ctxSingular}SelectOptionFieldName) => \`${ctxPlural}:select-options:\${fieldName}\`
36
+ } as const`
37
+ }
@@ -0,0 +1,93 @@
1
+ export function renderGenBulkSortOrderContentServerActionTemplate({
2
+ ctxTableVar,
3
+ ctxCacheInvalidationFn,
4
+ ctxCamelPlural,
5
+ lifecycleHookImport,
6
+ ctxPlural,
7
+ reorderContext,
8
+ beforeHook,
9
+ afterHook
10
+ }: {
11
+ ctxTableVar: string
12
+ ctxCacheInvalidationFn: string
13
+ ctxCamelPlural: string
14
+ lifecycleHookImport: string
15
+ ctxPlural: string
16
+ reorderContext: string
17
+ beforeHook: string
18
+ afterHook: string
19
+ }): string {
20
+ return `'use server'
21
+
22
+ import db from '@admin/db'
23
+ import { requireRole, UserRole } from '@admin/auth/middleware'
24
+ import { ${ctxTableVar} } from '@admin/db/schema'
25
+ import { asc, eq, notInArray } from 'drizzle-orm'
26
+ import { ${ctxCacheInvalidationFn} } from 'next/cache'
27
+ import { ${ctxCamelPlural}CacheTags } from './types'
28
+ ${lifecycleHookImport}
29
+
30
+ export async function bulkUpdate${ctxPlural}SortOrder(
31
+ updates: Array<{ id: string; sortOrder: number }>
32
+ ): Promise<{ success: boolean; error?: string }> {
33
+ const actor = await requireRole([UserRole.ADMIN, UserRole.EDITOR])
34
+ if (updates.length === 0) return { success: true }
35
+
36
+ const now = new Date().toISOString()
37
+ const auditRequestMetadata = await getAuditRequestMetadata()
38
+ const lifecycleContext = ${reorderContext}
39
+ ${beforeHook}
40
+
41
+ try {
42
+ const sorted = [...updates].sort((a, b) => a.sortOrder - b.sortOrder)
43
+ const updatedIds = sorted.map((u) => u.id)
44
+
45
+ const remaining = await db
46
+ .select({ id: ${ctxTableVar}.id })
47
+ .from(${ctxTableVar})
48
+ .where(updatedIds.length > 0 ? notInArray(${ctxTableVar}.id, updatedIds) : undefined)
49
+ .orderBy(asc(${ctxTableVar}.sortOrder))
50
+
51
+ const maxUpdatedPosition = Math.max(...sorted.map((u) => u.sortOrder))
52
+ const allIds: string[] = new Array(maxUpdatedPosition + 1 + remaining.length)
53
+
54
+ for (const u of sorted) {
55
+ allIds[u.sortOrder] = u.id
56
+ }
57
+
58
+ let fillIndex = 0
59
+ let remainingIndex = 0
60
+ while (fillIndex <= maxUpdatedPosition && remainingIndex < remaining.length) {
61
+ if (allIds[fillIndex] === undefined) {
62
+ allIds[fillIndex] = remaining[remainingIndex].id as string
63
+ remainingIndex++
64
+ }
65
+ fillIndex++
66
+ }
67
+ let appendIndex = maxUpdatedPosition + 1
68
+ while (remainingIndex < remaining.length) {
69
+ allIds[appendIndex] = remaining[remainingIndex].id as string
70
+ remainingIndex++
71
+ appendIndex++
72
+ }
73
+
74
+ const appliedOrder = allIds
75
+ .filter(Boolean)
76
+ .map((id, index) => ({ id, sortOrder: index }))
77
+
78
+ await Promise.all(
79
+ appliedOrder.map(({ id, sortOrder }) =>
80
+ db.update(${ctxTableVar}).set({ sortOrder }).where(eq(${ctxTableVar}.id, id))
81
+ )
82
+ )
83
+
84
+ ${ctxCacheInvalidationFn}(${ctxCamelPlural}CacheTags.all)
85
+ ${afterHook}
86
+ return { success: true }
87
+ } catch (error) {
88
+ console.error('Error bulk updating sort order:', error)
89
+ return { success: false, error: error instanceof Error ? error.message : 'Failed to bulk update sort order' }
90
+ }
91
+ }
92
+ `
93
+ }