@effect-auth/core 0.1.0-alpha.18 → 0.1.0-alpha.19

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 (118) hide show
  1. package/README.md +4 -5
  2. package/dist/DevEmail.d.ts +44 -0
  3. package/dist/DevEmail.d.ts.map +1 -0
  4. package/dist/DevEmail.js +115 -0
  5. package/dist/DevEmail.js.map +1 -0
  6. package/dist/DevelopmentSeed.d.ts +47 -0
  7. package/dist/DevelopmentSeed.d.ts.map +1 -0
  8. package/dist/DevelopmentSeed.js +337 -0
  9. package/dist/DevelopmentSeed.js.map +1 -0
  10. package/dist/DrizzleBunSqliteStorage.d.ts +1 -1
  11. package/dist/DrizzleBunSqliteStorage.d.ts.map +1 -1
  12. package/dist/DrizzleD1SqliteStorage.d.ts +1 -1
  13. package/dist/DrizzleD1SqliteStorage.d.ts.map +1 -1
  14. package/dist/DrizzleEffectSqliteStorage.d.ts +1 -1
  15. package/dist/DrizzleEffectSqliteStorage.d.ts.map +1 -1
  16. package/dist/DrizzleNodeSqliteStorage.d.ts +1 -1
  17. package/dist/DrizzleNodeSqliteStorage.d.ts.map +1 -1
  18. package/dist/DrizzlePostgresStorage.d.ts +26 -0
  19. package/dist/DrizzlePostgresStorage.d.ts.map +1 -0
  20. package/dist/DrizzlePostgresStorage.js +14 -0
  21. package/dist/DrizzlePostgresStorage.js.map +1 -0
  22. package/dist/EffectQbPostgresStorage.d.ts +8 -1
  23. package/dist/EffectQbPostgresStorage.d.ts.map +1 -1
  24. package/dist/EffectQbPostgresStorage.js +359 -2
  25. package/dist/EffectQbPostgresStorage.js.map +1 -1
  26. package/dist/EffectQbSqliteStorage.d.ts +42 -2
  27. package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
  28. package/dist/EffectQbSqliteStorage.js +337 -1
  29. package/dist/EffectQbSqliteStorage.js.map +1 -1
  30. package/dist/HttpApi/Api.d.ts +151 -98
  31. package/dist/HttpApi/Api.d.ts.map +1 -1
  32. package/dist/HttpApi/Api.js +122 -72
  33. package/dist/HttpApi/Api.js.map +1 -1
  34. package/dist/HttpApi/ClientApi.d.ts +136 -136
  35. package/dist/HttpApi/Endpoints.d.ts +33 -1
  36. package/dist/HttpApi/Endpoints.d.ts.map +1 -1
  37. package/dist/HttpApi/Endpoints.js +31 -2
  38. package/dist/HttpApi/Endpoints.js.map +1 -1
  39. package/dist/HttpApi/Errors.d.ts +11 -3
  40. package/dist/HttpApi/Errors.d.ts.map +1 -1
  41. package/dist/HttpApi/Errors.js +16 -0
  42. package/dist/HttpApi/Errors.js.map +1 -1
  43. package/dist/HttpApi/Middleware.d.ts +12 -0
  44. package/dist/HttpApi/Middleware.d.ts.map +1 -1
  45. package/dist/HttpApi/Middleware.js +8 -0
  46. package/dist/HttpApi/Middleware.js.map +1 -1
  47. package/dist/HttpApi/OAuthDeviceAuthorization.d.ts +106 -0
  48. package/dist/HttpApi/OAuthDeviceAuthorization.d.ts.map +1 -0
  49. package/dist/HttpApi/OAuthDeviceAuthorization.js +415 -0
  50. package/dist/HttpApi/OAuthDeviceAuthorization.js.map +1 -0
  51. package/dist/HttpApi/OAuthToken.d.ts +1 -1
  52. package/dist/HttpApi/OAuthToken.d.ts.map +1 -1
  53. package/dist/HttpApi/OAuthToken.js +1 -1
  54. package/dist/HttpApi/OAuthToken.js.map +1 -1
  55. package/dist/HttpApi/Operations/OAuthDeviceAuthorization.d.ts +23 -0
  56. package/dist/HttpApi/Operations/OAuthDeviceAuthorization.d.ts.map +1 -0
  57. package/dist/HttpApi/Operations/OAuthDeviceAuthorization.js +7 -0
  58. package/dist/HttpApi/Operations/OAuthDeviceAuthorization.js.map +1 -0
  59. package/dist/HttpApi/Operations/OAuthToken.d.ts +10 -0
  60. package/dist/HttpApi/Operations/OAuthToken.d.ts.map +1 -1
  61. package/dist/HttpApi/Operations/OAuthToken.js +4 -0
  62. package/dist/HttpApi/Operations/OAuthToken.js.map +1 -1
  63. package/dist/HttpApi/Operations/index.d.ts +1 -0
  64. package/dist/HttpApi/Operations/index.d.ts.map +1 -1
  65. package/dist/HttpApi/Operations/index.js +1 -0
  66. package/dist/HttpApi/Operations/index.js.map +1 -1
  67. package/dist/HttpApi/PasswordContract.d.ts +9 -9
  68. package/dist/HttpApi/Schemas.d.ts +38 -0
  69. package/dist/HttpApi/Schemas.d.ts.map +1 -1
  70. package/dist/HttpApi/Schemas.js +36 -1
  71. package/dist/HttpApi/Schemas.js.map +1 -1
  72. package/dist/HttpApi/index.d.ts +3 -1
  73. package/dist/HttpApi/index.d.ts.map +1 -1
  74. package/dist/HttpApi/index.js +3 -1
  75. package/dist/HttpApi/index.js.map +1 -1
  76. package/dist/OAuth.d.ts +46 -1
  77. package/dist/OAuth.d.ts.map +1 -1
  78. package/dist/OAuth.js +118 -4
  79. package/dist/OAuth.js.map +1 -1
  80. package/dist/OAuthDeviceAuthorization.d.ts +378 -0
  81. package/dist/OAuthDeviceAuthorization.d.ts.map +1 -0
  82. package/dist/OAuthDeviceAuthorization.js +860 -0
  83. package/dist/OAuthDeviceAuthorization.js.map +1 -0
  84. package/dist/OAuthDeviceClient.d.ts +76 -0
  85. package/dist/OAuthDeviceClient.d.ts.map +1 -0
  86. package/dist/OAuthDeviceClient.js +362 -0
  87. package/dist/OAuthDeviceClient.js.map +1 -0
  88. package/dist/PostgresStorageMigrations.d.ts +2 -1
  89. package/dist/PostgresStorageMigrations.d.ts.map +1 -1
  90. package/dist/PostgresStorageMigrations.js +54 -0
  91. package/dist/PostgresStorageMigrations.js.map +1 -1
  92. package/dist/Retention.d.ts +6 -1
  93. package/dist/Retention.d.ts.map +1 -1
  94. package/dist/Retention.js +49 -12
  95. package/dist/Retention.js.map +1 -1
  96. package/dist/SqliteStorageSchemaData.d.ts +2 -0
  97. package/dist/SqliteStorageSchemaData.d.ts.map +1 -0
  98. package/dist/SqliteStorageSchemaData.js +508 -0
  99. package/dist/SqliteStorageSchemaData.js.map +1 -0
  100. package/dist/StorageMigrations.d.ts +2 -1
  101. package/dist/StorageMigrations.d.ts.map +1 -1
  102. package/dist/StorageMigrations.js +32 -0
  103. package/dist/StorageMigrations.js.map +1 -1
  104. package/dist/StorageSchemaGenerator.d.ts +70 -12
  105. package/dist/StorageSchemaGenerator.d.ts.map +1 -1
  106. package/dist/StorageSchemaGenerator.js +478 -82
  107. package/dist/StorageSchemaGenerator.js.map +1 -1
  108. package/migrations/0029_auth_oauth_device_authorization.sql +28 -0
  109. package/migrations/postgres/0010_auth_oauth_device_authorization.sql +50 -0
  110. package/package.json +24 -16
  111. package/dist/DrizzleDurableObjectSqliteStorage.d.ts +0 -19
  112. package/dist/DrizzleDurableObjectSqliteStorage.d.ts.map +0 -1
  113. package/dist/DrizzleDurableObjectSqliteStorage.js +0 -14
  114. package/dist/DrizzleDurableObjectSqliteStorage.js.map +0 -1
  115. package/dist/DrizzleWasmSqliteStorage.d.ts +0 -23
  116. package/dist/DrizzleWasmSqliteStorage.d.ts.map +0 -1
  117. package/dist/DrizzleWasmSqliteStorage.js +0 -15
  118. package/dist/DrizzleWasmSqliteStorage.js.map +0 -1
@@ -1,10 +1,40 @@
1
1
  import { postgresAuthStorageMigrations } from "./PostgresStorageMigrations.js";
2
+ import { sqliteAuthStorageSchema } from "./SqliteStorageSchemaData.js";
2
3
  export const storageSchemaGeneratorApiVersion = "effect-auth.storage-schema-generator/v1";
3
- export const storageSchemaGeneratorVersion = "1.1.0";
4
- export const storageSchemaVersion = "0009_auth_permissions";
5
- export const supportedStorageDatabases = ["postgres"];
4
+ export const storageSchemaGeneratorVersion = "1.5.0";
5
+ export const storageSchemaVersion = "0010_auth_oauth_device_authorization";
6
+ export const storageSchemaVersions = {
7
+ postgres: storageSchemaVersion,
8
+ sqlite: "0029_auth_oauth_device_authorization",
9
+ };
10
+ export const supportedStorageDatabases = ["postgres", "sqlite"];
6
11
  export const supportedStorageAdapters = ["effect-qb", "drizzle"];
7
12
  export const supportedStorageGeneratorModes = ["fresh"];
13
+ export const supportedDevelopmentSeedTargets = [
14
+ "alchemy-d1",
15
+ "bun-sqlite",
16
+ "node-sqlite",
17
+ "postgres",
18
+ ];
19
+ export const supportedDevelopmentSeedComplexities = ["medium", "low"];
20
+ export const storageDatabaseAdapters = {
21
+ postgres: supportedStorageAdapters,
22
+ sqlite: supportedStorageAdapters,
23
+ };
24
+ export const developmentSeedTargetsByDatabase = {
25
+ postgres: ["postgres"],
26
+ sqlite: ["alchemy-d1", "bun-sqlite", "node-sqlite"],
27
+ };
28
+ export const developmentSeedTargetsByDatabaseAdapter = {
29
+ postgres: {
30
+ "effect-qb": ["postgres"],
31
+ drizzle: ["postgres"],
32
+ },
33
+ sqlite: {
34
+ "effect-qb": ["alchemy-d1"],
35
+ drizzle: ["alchemy-d1", "bun-sqlite", "node-sqlite"],
36
+ },
37
+ };
8
38
  export const storageModuleCatalog = [
9
39
  {
10
40
  id: "core",
@@ -122,6 +152,7 @@ export const storageModuleCatalog = [
122
152
  "auth_oauth_client",
123
153
  "auth_oauth_consent",
124
154
  "auth_oauth_authorization_code",
155
+ "auth_oauth_device_authorization",
125
156
  "auth_oauth_client_secret",
126
157
  "auth_oauth_provider_mode_token",
127
158
  ],
@@ -261,16 +292,22 @@ export const storageSchemaGeneratorCatalog = {
261
292
  apiVersion: storageSchemaGeneratorApiVersion,
262
293
  generatorVersion: storageSchemaGeneratorVersion,
263
294
  schemaVersion: storageSchemaVersion,
295
+ schemaVersions: storageSchemaVersions,
264
296
  databases: supportedStorageDatabases,
265
297
  adapters: supportedStorageAdapters,
298
+ databaseAdapters: storageDatabaseAdapters,
266
299
  adapterOutputs: {
267
- "effect-qb": ["migration-sql"],
268
- drizzle: ["migration-sql", "drizzle-schema"],
300
+ "effect-qb": ["migration-sql", "development-seed"],
301
+ drizzle: ["migration-sql", "drizzle-schema", "development-seed"],
269
302
  },
270
303
  modes: supportedStorageGeneratorModes,
271
- outputKinds: ["migration-sql", "drizzle-schema"],
304
+ outputKinds: ["migration-sql", "drizzle-schema", "development-seed"],
272
305
  sqlLayouts: ["single", "module"],
273
306
  drizzleLayouts: ["single", "module", "table"],
307
+ developmentSeedTargets: supportedDevelopmentSeedTargets,
308
+ developmentSeedTargetsByDatabase,
309
+ developmentSeedTargetsByDatabaseAdapter,
310
+ developmentSeedComplexities: supportedDevelopmentSeedComplexities,
274
311
  features: storageFeatureCatalog,
275
312
  modules: storageModuleCatalog,
276
313
  };
@@ -343,20 +380,20 @@ const splitSqlStatements = (sql) => {
343
380
  }
344
381
  return statements;
345
382
  };
346
- const canonicalTables = (() => {
383
+ const buildCanonicalTables = (database, sqlSources) => {
347
384
  const creates = new Map();
348
385
  const indexes = new Map();
349
386
  let order = 0;
350
- for (const migration of postgresAuthStorageMigrations) {
351
- for (const statement of splitSqlStatements(migration.sql)) {
352
- const createMatch = /^create table ([a-z0-9_]+)\s*\(/i.exec(statement);
387
+ for (const sql of sqlSources) {
388
+ for (const statement of splitSqlStatements(sql)) {
389
+ const createMatch = /^create table (?:if not exists )?([a-z0-9_]+)\s*\(/i.exec(statement);
353
390
  if (createMatch?.[1] !== undefined) {
354
391
  creates.set(createMatch[1], { sql: statement, order });
355
392
  indexes.set(createMatch[1], []);
356
393
  order += 1;
357
394
  continue;
358
395
  }
359
- const indexMatch = /^create (?:unique )?index [a-z0-9_]+\s+on ([a-z0-9_]+)\s*\(/i.exec(statement);
396
+ const indexMatch = /^create (?:unique )?index (?:if not exists )?[a-z0-9_]+\s+on ([a-z0-9_]+)\s*\(/i.exec(statement);
360
397
  if (indexMatch?.[1] !== undefined) {
361
398
  const tableIndexes = indexes.get(indexMatch[1]);
362
399
  if (tableIndexes === undefined) {
@@ -366,6 +403,16 @@ const canonicalTables = (() => {
366
403
  }
367
404
  }
368
405
  }
406
+ const oauthClient = creates.get("auth_oauth_client");
407
+ if (database === "postgres" && oauthClient !== undefined) {
408
+ const previous = '@ != "client_credentials"))\'))';
409
+ const replacement = '@ != "client_credentials" && @ != "urn:ietf:params:oauth:grant-type:device_code"))\'))';
410
+ const sql = oauthClient.sql.replace(previous, replacement);
411
+ if (sql === oauthClient.sql) {
412
+ throw new Error("Unable to apply the OAuth device-code grant constraint");
413
+ }
414
+ creates.set("auth_oauth_client", { ...oauthClient, sql });
415
+ }
369
416
  const result = [];
370
417
  for (const [name, create] of creates) {
371
418
  const moduleId = tableModule.get(name);
@@ -387,7 +434,12 @@ const canonicalTables = (() => {
387
434
  }
388
435
  // oxlint-disable-next-line unicorn/no-array-sort -- ES2023 toSorted is outside the supported target.
389
436
  return result.sort((left, right) => left.order - right.order);
390
- })();
437
+ };
438
+ const canonicalTablesByDatabase = {
439
+ postgres: buildCanonicalTables("postgres", postgresAuthStorageMigrations.map((migration) => migration.sql)),
440
+ sqlite: buildCanonicalTables("sqlite", [sqliteAuthStorageSchema]),
441
+ };
442
+ const canonicalTables = canonicalTablesByDatabase.postgres;
391
443
  const splitTopLevel = (value) => {
392
444
  const parts = [];
393
445
  let depth = 0;
@@ -429,8 +481,9 @@ const splitTopLevel = (value) => {
429
481
  parts.push(value.slice(start).trim());
430
482
  return parts.filter((part) => part.length > 0);
431
483
  };
484
+ // oxlint-disable-next-line eslint/complexity -- one pass handles both dialects' column and constraint syntax
432
485
  const parseCanonicalTable = (canonical) => {
433
- const tableMatch = /^create table [a-z0-9_]+\s*\(([\s\S]*)\);$/i.exec(canonical.createTable);
486
+ const tableMatch = /^create table (?:if not exists )?[a-z0-9_]+\s*\(([\s\S]*)\);$/i.exec(canonical.createTable);
434
487
  if (tableMatch?.[1] === undefined) {
435
488
  throw new Error(`Unable to parse canonical table ${canonical.name}`);
436
489
  }
@@ -453,6 +506,24 @@ const parseCanonicalTable = (canonical) => {
453
506
  });
454
507
  continue;
455
508
  }
509
+ const primaryKeyMatch = /^primary key\s*\(([\s\S]*)\)$/i.exec(definition);
510
+ if (primaryKeyMatch?.[1] !== undefined) {
511
+ constraints.push({
512
+ name: `${canonical.name}_pkey`,
513
+ kind: "primary-key",
514
+ columns: splitTopLevel(primaryKeyMatch[1]).map((column) => column.trim().split(/\s+/u)[0].toLowerCase()),
515
+ });
516
+ continue;
517
+ }
518
+ const tableCheckMatch = /^check\s*\(([\s\S]*)\)$/i.exec(definition);
519
+ if (tableCheckMatch?.[1] !== undefined) {
520
+ constraints.push({
521
+ name: `${canonical.name}_state_check`,
522
+ kind: "check",
523
+ expression: tableCheckMatch[1].trim(),
524
+ });
525
+ continue;
526
+ }
456
527
  const columnMatch = /^([a-z0-9_]+)\s+(text|bigint|integer|boolean|jsonb)\b([\s\S]*)$/i.exec(definition);
457
528
  if (columnMatch === null) {
458
529
  throw new Error(`Unable to parse column definition in ${canonical.name}: ${definition}`);
@@ -461,32 +532,58 @@ const parseCanonicalTable = (canonical) => {
461
532
  if (name === undefined || rawType === undefined) {
462
533
  throw new Error(`Unable to parse column in ${canonical.name}`);
463
534
  }
464
- const inlinePrimaryKey = /constraint ([a-z0-9_]+) primary key/i.exec(modifiers);
465
- if (inlinePrimaryKey?.[1] !== undefined) {
535
+ const inlinePrimaryKey = /\bprimary key\b/i.test(modifiers);
536
+ const namedPrimaryKey = /constraint ([a-z0-9_]+) primary key/i.exec(modifiers)?.[1];
537
+ if (inlinePrimaryKey) {
466
538
  constraints.push({
467
- name: inlinePrimaryKey[1],
539
+ name: namedPrimaryKey ?? `${canonical.name}_pkey`,
468
540
  kind: "primary-key",
469
541
  columns: [name],
470
542
  });
471
543
  }
544
+ if (/\bunique\b/i.test(modifiers)) {
545
+ constraints.push({
546
+ name: `${canonical.name}_${name}_key`,
547
+ kind: "unique",
548
+ columns: [name],
549
+ });
550
+ }
551
+ const inlineCheck = /\bcheck\s*\(([\s\S]*)\)\s*$/i.exec(modifiers);
552
+ if (inlineCheck?.[1] !== undefined) {
553
+ constraints.push({
554
+ name: `${canonical.name}_${name}_check`,
555
+ kind: "check",
556
+ expression: inlineCheck[1].trim(),
557
+ });
558
+ }
472
559
  const defaultMatch = /\bdefault\s+(false|true|-?\d+|'(?:''|[^'])*')/i.exec(modifiers);
473
560
  const defaultValue = defaultMatch?.[1];
474
561
  columns.push({
475
562
  name,
476
563
  type: rawType.toLowerCase(),
477
- notNull: /\bnot null\b/i.test(modifiers) || inlinePrimaryKey !== null,
564
+ notNull: /\bnot null\b/i.test(modifiers) || inlinePrimaryKey,
478
565
  defaultValue: defaultValue === undefined
479
566
  ? undefined
480
567
  : /^(?:false|true)$/iu.test(defaultValue)
481
568
  ? defaultValue.toLowerCase()
482
569
  : defaultValue,
483
570
  identity: /\bgenerated always as identity\b/i.test(modifiers),
571
+ autoincrement: /\bautoincrement\b/i.test(modifiers),
484
572
  collateC: /\bcollate "C"/i.test(modifiers),
485
573
  });
486
574
  }
487
575
  return { canonical, columns, constraints };
488
576
  };
489
- const parsedTableByName = new Map(canonicalTables.map((table) => [table.name, parseCanonicalTable(table)]));
577
+ const parsedTableByDatabase = {
578
+ postgres: new Map(canonicalTablesByDatabase.postgres.map((table) => [
579
+ table.name,
580
+ parseCanonicalTable(table),
581
+ ])),
582
+ sqlite: new Map(canonicalTablesByDatabase.sqlite.map((table) => [
583
+ table.name,
584
+ parseCanonicalTable(table),
585
+ ])),
586
+ };
490
587
  const compareByOrder = (order) => (left, right) => (order.get(left) ?? Number.MAX_SAFE_INTEGER) -
491
588
  (order.get(right) ?? Number.MAX_SAFE_INTEGER);
492
589
  const resolveModules = (directModules) => {
@@ -535,6 +632,21 @@ export const resolveStorageFeatures = (features) => {
535
632
  impliedModules: modules.filter((moduleId) => !directModules.has(moduleId)),
536
633
  };
537
634
  };
635
+ const includeDevelopmentSeedRequirements = (selection) => {
636
+ if (selection.modules.includes("core")) {
637
+ return selection;
638
+ }
639
+ const modules = resolveModules(new Set([...selection.modules, "core"]));
640
+ const selectedModules = new Set(modules);
641
+ return {
642
+ ...selection,
643
+ modules,
644
+ tables: canonicalTables
645
+ .filter((table) => selectedModules.has(table.module))
646
+ .map((table) => table.name),
647
+ impliedModules: [...selection.impliedModules, "core"],
648
+ };
649
+ };
538
650
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
539
651
  const assertKnownKeys = (value, keys, path, code) => {
540
652
  const allowed = new Set(keys);
@@ -551,9 +663,10 @@ const normalizeRequest = (input) => {
551
663
  }
552
664
  const request = input;
553
665
  assertKnownKeys(request, ["database", "adapter", "mode", "features", "outputs"], "$", "INVALID_REQUEST");
554
- if (request.database !== "postgres") {
555
- throw new StorageSchemaGeneratorError("UNSUPPORTED_DATABASE", "database", `Unsupported database ${JSON.stringify(request.database)}.`, ["postgres"]);
666
+ if (request.database !== "postgres" && request.database !== "sqlite") {
667
+ throw new StorageSchemaGeneratorError("UNSUPPORTED_DATABASE", "database", `Unsupported database ${JSON.stringify(request.database)}.`, supportedStorageDatabases);
556
668
  }
669
+ const { database } = request;
557
670
  if (request.adapter !== "effect-qb" && request.adapter !== "drizzle") {
558
671
  throw new StorageSchemaGeneratorError("UNSUPPORTED_ADAPTER", "adapter", `Unsupported adapter ${JSON.stringify(request.adapter)}.`, supportedStorageAdapters);
559
672
  }
@@ -573,7 +686,7 @@ const normalizeRequest = (input) => {
573
686
  features.push(feature);
574
687
  }
575
688
  if (!Array.isArray(request.outputs) || request.outputs.length === 0) {
576
- throw new StorageSchemaGeneratorError("INVALID_REQUEST", "outputs", "At least one output is required.", ["migration-sql", "drizzle-schema"]);
689
+ throw new StorageSchemaGeneratorError("INVALID_REQUEST", "outputs", "At least one output is required.", ["migration-sql", "drizzle-schema", "development-seed"]);
577
690
  }
578
691
  const outputs = [];
579
692
  const outputKinds = new Set();
@@ -582,14 +695,15 @@ const normalizeRequest = (input) => {
582
695
  if (!isRecord(output)) {
583
696
  throw new StorageSchemaGeneratorError("INVALID_OUTPUT", path, "Each output must be an object.");
584
697
  }
585
- assertKnownKeys(output, ["kind", "layout", "includeRelations"], path, "INVALID_OUTPUT");
586
698
  if (output.kind === "migration-sql") {
699
+ assertKnownKeys(output, ["kind", "layout"], path, "INVALID_OUTPUT");
587
700
  if (output.layout !== "single" && output.layout !== "module") {
588
701
  throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.layout`, `Invalid migration SQL layout ${JSON.stringify(output.layout)}.`, ["single", "module"]);
589
702
  }
590
703
  outputs.push({ kind: "migration-sql", layout: output.layout });
591
704
  }
592
705
  else if (output.kind === "drizzle-schema") {
706
+ assertKnownKeys(output, ["kind", "layout", "includeRelations"], path, "INVALID_OUTPUT");
593
707
  if (output.layout !== "single" &&
594
708
  output.layout !== "module" &&
595
709
  output.layout !== "table") {
@@ -605,8 +719,29 @@ const normalizeRequest = (input) => {
605
719
  includeRelations: output.includeRelations === true,
606
720
  });
607
721
  }
722
+ else if (output.kind === "development-seed") {
723
+ assertKnownKeys(output, ["kind", "target", "complexity"], path, "INVALID_OUTPUT");
724
+ if (typeof output.target !== "string" ||
725
+ !supportedDevelopmentSeedTargets.includes(output.target)) {
726
+ throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.target`, `Invalid development seed target ${JSON.stringify(output.target)}.`, supportedDevelopmentSeedTargets);
727
+ }
728
+ if (!developmentSeedTargetsByDatabaseAdapter[database][adapter].includes(output.target)) {
729
+ throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.target`, `Development seed target ${JSON.stringify(output.target)} is not available for ${database}.`, developmentSeedTargetsByDatabaseAdapter[database][adapter]);
730
+ }
731
+ if (output.complexity !== undefined &&
732
+ output.complexity !== "medium" &&
733
+ output.complexity !== "low") {
734
+ throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.complexity`, `Invalid development seed complexity ${JSON.stringify(output.complexity)}.`, supportedDevelopmentSeedComplexities);
735
+ }
736
+ outputs.push({
737
+ kind: "development-seed",
738
+ target: output.target,
739
+ complexity: output.complexity ??
740
+ "medium",
741
+ });
742
+ }
608
743
  else {
609
- throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.kind`, `Unsupported output kind ${JSON.stringify(output.kind)}.`, ["migration-sql", "drizzle-schema"]);
744
+ throw new StorageSchemaGeneratorError("INVALID_OUTPUT", `${path}.kind`, `Unsupported output kind ${JSON.stringify(output.kind)}.`, ["migration-sql", "drizzle-schema", "development-seed"]);
610
745
  }
611
746
  if (outputKinds.has(output.kind)) {
612
747
  throw new StorageSchemaGeneratorError("DUPLICATE_OUTPUT", `${path}.kind`, `Only one ${String(output.kind)} output may be requested.`, ["Combine the requested files by choosing one layout."]);
@@ -614,20 +749,28 @@ const normalizeRequest = (input) => {
614
749
  outputKinds.add(output.kind);
615
750
  }
616
751
  if (adapter === "effect-qb") {
617
- const unsupportedOutputIndex = outputs.findIndex((output) => output.kind !== "migration-sql");
752
+ const unsupportedOutputIndex = outputs.findIndex((output) => output.kind === "drizzle-schema");
618
753
  if (unsupportedOutputIndex !== -1) {
619
- throw new StorageSchemaGeneratorError("UNSUPPORTED_OUTPUT_FOR_ADAPTER", `outputs[${unsupportedOutputIndex}].kind`, "The effect-qb adapter owns its table declarations and only requires migration SQL.", ["migration-sql"]);
754
+ throw new StorageSchemaGeneratorError("UNSUPPORTED_OUTPUT_FOR_ADAPTER", `outputs[${unsupportedOutputIndex}].kind`, "The effect-qb adapter owns its table declarations and does not generate a Drizzle schema.", ["migration-sql", "development-seed"]);
620
755
  }
621
756
  }
622
757
  // oxlint-disable-next-line unicorn/no-array-sort -- ES2023 toSorted is outside the supported target.
623
758
  outputs.sort((left, right) => {
624
759
  if (left.kind !== right.kind) {
625
- return left.kind === "migration-sql" ? -1 : 1;
760
+ const order = new Map([
761
+ ["migration-sql", 0],
762
+ ["drizzle-schema", 1],
763
+ ["development-seed", 2],
764
+ ]);
765
+ return (order.get(left.kind) ?? 3) - (order.get(right.kind) ?? 3);
766
+ }
767
+ if (left.kind === "development-seed") {
768
+ return left.target.localeCompare(right.target);
626
769
  }
627
770
  return left.layout.localeCompare(right.layout);
628
771
  });
629
772
  return {
630
- database: "postgres",
773
+ database,
631
774
  adapter,
632
775
  mode: "fresh",
633
776
  // oxlint-disable-next-line unicorn/no-array-sort -- ES2023 toSorted is outside the supported target.
@@ -639,9 +782,9 @@ const renderCanonicalSql = (tables) => `${tables
639
782
  .flatMap((table) => [table.createTable, ...table.indexes])
640
783
  .join("\n\n")}
641
784
  `;
642
- const sqlFiles = (layout, selection) => {
785
+ const sqlFiles = (database, layout, selection) => {
643
786
  const selectedTables = new Set(selection.tables);
644
- const tables = canonicalTables.filter((table) => selectedTables.has(table.name));
787
+ const tables = canonicalTablesByDatabase[database].filter((table) => selectedTables.has(table.name));
645
788
  if (layout === "single") {
646
789
  return [
647
790
  {
@@ -660,34 +803,42 @@ const sqlFiles = (layout, selection) => {
660
803
  const camelCase = (value) => value.replaceAll(/_([a-z0-9])/g, (_, character) => character.toUpperCase());
661
804
  const kebabCase = (value) => value.replaceAll("_", "-");
662
805
  const escapeTemplate = (value) => value.replaceAll("`", "\\`").replaceAll("${", "\\${");
663
- const renderColumn = (column) => {
806
+ const renderColumn = (column, database) => {
664
807
  let expression;
665
- switch (column.type) {
666
- case "bigint": {
667
- expression = `bigint(${JSON.stringify(column.name)}, { mode: "number" })`;
668
- break;
669
- }
670
- case "boolean": {
671
- expression = `boolean(${JSON.stringify(column.name)})`;
672
- break;
673
- }
674
- case "integer": {
675
- expression = `integer(${JSON.stringify(column.name)})`;
676
- break;
677
- }
678
- case "jsonb": {
679
- expression = `jsonb(${JSON.stringify(column.name)})`;
680
- break;
681
- }
682
- case "text": {
683
- expression = `${column.collateC ? "textC" : "text"}(${JSON.stringify(column.name)})`;
684
- break;
808
+ if (database === "sqlite") {
809
+ expression = `${column.type === "integer" ? "integer" : "text"}(${JSON.stringify(column.name)})`;
810
+ }
811
+ else {
812
+ switch (column.type) {
813
+ case "bigint": {
814
+ expression = `bigint(${JSON.stringify(column.name)}, { mode: "number" })`;
815
+ break;
816
+ }
817
+ case "boolean": {
818
+ expression = `boolean(${JSON.stringify(column.name)})`;
819
+ break;
820
+ }
821
+ case "integer": {
822
+ expression = `integer(${JSON.stringify(column.name)})`;
823
+ break;
824
+ }
825
+ case "jsonb": {
826
+ expression = `jsonb(${JSON.stringify(column.name)})`;
827
+ break;
828
+ }
829
+ case "text": {
830
+ expression = `${column.collateC ? "textC" : "text"}(${JSON.stringify(column.name)})`;
831
+ break;
832
+ }
685
833
  }
686
834
  }
687
- if (column.identity) {
835
+ if (column.autoincrement) {
836
+ expression += ".primaryKey({ autoIncrement: true })";
837
+ }
838
+ else if (column.identity) {
688
839
  expression += ".generatedAlwaysAsIdentity()";
689
840
  }
690
- if (column.notNull && !column.identity) {
841
+ if (column.notNull && !column.identity && !column.autoincrement) {
691
842
  expression += ".notNull()";
692
843
  }
693
844
  if (column.defaultValue !== undefined) {
@@ -710,7 +861,7 @@ const renderIndexColumn = (value) => {
710
861
  : `t.${column}`;
711
862
  };
712
863
  const renderIndex = (statement) => {
713
- const match = /^create (unique )?index ([a-z0-9_]+)\s+on [a-z0-9_]+\s*\(([\s\S]*?)\)(?:\s+where\s+([\s\S]+?))?;$/i.exec(statement);
864
+ const match = /^create (unique )?index (?:if not exists )?([a-z0-9_]+)\s+on [a-z0-9_]+\s*\(([\s\S]*?)\)(?:\s+where\s+([\s\S]+?))?;$/i.exec(statement);
714
865
  if (match === null || match[2] === undefined || match[3] === undefined) {
715
866
  throw new Error(`Unable to render canonical index: ${statement}`);
716
867
  }
@@ -738,16 +889,27 @@ const renderConstraint = (constraint) => {
738
889
  }
739
890
  }
740
891
  };
741
- const drizzleImports = (tables) => {
742
- const imports = new Set(["pgTable"]);
892
+ const isAutoincrementPrimaryKey = (table, constraint) => constraint.kind === "primary-key" &&
893
+ constraint.columns?.some((columnName) => table.columns.some((column) => column.name === columnName && column.autoincrement)) === true;
894
+ const drizzleImports = (database, tables) => {
895
+ const imports = new Set([
896
+ database === "postgres" ? "pgTable" : "sqliteTable",
897
+ ]);
743
898
  for (const table of tables) {
744
899
  for (const column of table.columns) {
745
- imports.add(column.type);
746
- if (column.collateC) {
900
+ imports.add(database === "sqlite"
901
+ ? column.type === "integer"
902
+ ? "integer"
903
+ : "text"
904
+ : column.type);
905
+ if (database === "postgres" && column.collateC) {
747
906
  imports.add("customType");
748
907
  }
749
908
  }
750
909
  for (const constraint of table.constraints) {
910
+ if (isAutoincrementPrimaryKey(table, constraint)) {
911
+ continue;
912
+ }
751
913
  imports.add(constraint.kind === "primary-key"
752
914
  ? "primaryKey"
753
915
  : constraint.kind === "unique"
@@ -767,31 +929,34 @@ const drizzleImports = (tables) => {
767
929
  // oxlint-disable-next-line unicorn/no-array-sort -- ES2023 toSorted is outside the supported target.
768
930
  return [...imports].sort();
769
931
  };
770
- const renderTable = (table) => {
932
+ const renderTable = (database, table) => {
771
933
  const variable = camelCase(table.canonical.name);
772
934
  const extras = [
773
- ...table.constraints.map(renderConstraint),
935
+ ...table.constraints
936
+ .filter((constraint) => !isAutoincrementPrimaryKey(table, constraint))
937
+ .map(renderConstraint),
774
938
  ...table.canonical.indexes.map(renderIndex),
775
939
  ];
776
- return `export const ${variable} = pgTable(
940
+ return `export const ${variable} = ${database === "postgres" ? "pgTable" : "sqliteTable"}(
777
941
  ${JSON.stringify(table.canonical.name)},
778
942
  {
779
- ${table.columns.map(renderColumn).join("\n")}
943
+ ${table.columns.map((column) => renderColumn(column, database)).join("\n")}
780
944
  },
781
945
  (t) => [
782
946
  ${extras.join("\n")}
783
947
  ]
784
948
  );`;
785
949
  };
786
- const renderDrizzleTables = (tables) => {
787
- const imports = drizzleImports(tables);
950
+ const renderDrizzleTables = (database, tables) => {
951
+ const imports = drizzleImports(database, tables);
788
952
  const usesSql = imports.includes("sql");
789
- const pgImports = imports.filter((name) => name !== "sql");
953
+ const databaseImports = imports.filter((name) => name !== "sql");
790
954
  const importLines = [
791
955
  ...(usesSql ? ['import { sql } from "drizzle-orm";'] : []),
792
- `import { ${pgImports.join(", ")} } from "drizzle-orm/pg-core";`,
956
+ `import { ${databaseImports.join(", ")} } from "drizzle-orm/${database === "postgres" ? "pg-core" : "sqlite-core"}";`,
793
957
  ];
794
- const collatedText = tables.some((table) => table.columns.some((column) => column.collateC))
958
+ const collatedText = database === "postgres" &&
959
+ tables.some((table) => table.columns.some((column) => column.collateC))
795
960
  ? `
796
961
  const textC = customType<{ data: string }>({
797
962
  dataType: () => 'text collate "C"',
@@ -799,7 +964,7 @@ const textC = customType<{ data: string }>({
799
964
  : "";
800
965
  return `${importLines.join("\n")}${collatedText}
801
966
 
802
- ${tables.map(renderTable).join("\n\n")}
967
+ ${tables.map((table) => renderTable(database, table)).join("\n\n")}
803
968
  `;
804
969
  };
805
970
  // oxlint-disable-next-line eslint/complexity -- each branch describes one canonical relation family
@@ -844,6 +1009,7 @@ const logicalRelations = (tables) => {
844
1009
  for (const tableName of [
845
1010
  "auth_oauth_consent",
846
1011
  "auth_oauth_authorization_code",
1012
+ "auth_oauth_device_authorization",
847
1013
  "auth_oauth_client_secret",
848
1014
  "auth_oauth_provider_mode_token",
849
1015
  ]) {
@@ -886,7 +1052,7 @@ const logicalRelations = (tables) => {
886
1052
  addRelation("auth_role_permission", "permission_id", "auth_permission_definition", "id", "permission");
887
1053
  return relations;
888
1054
  };
889
- const renderRelations = (tables, schemaExpression, importPath) => {
1055
+ const renderRelations = (database, tables, schemaExpression, importPath) => {
890
1056
  const relations = logicalRelations(tables);
891
1057
  const grouped = new Map();
892
1058
  for (const relation of relations) {
@@ -916,7 +1082,7 @@ ${tableRelations
916
1082
  ];
917
1083
  return `${imports.join("\n")}
918
1084
 
919
- // Drizzle relations are application-level only and do not create PostgreSQL foreign keys.
1085
+ // Drizzle relations are application-level only and do not create ${database === "postgres" ? "PostgreSQL" : "SQLite"} foreign keys.
920
1086
  export const relations = defineRelations(${schemaExpression}, (r) => ({
921
1087
  ${entries.join("\n")}
922
1088
  }));
@@ -924,18 +1090,18 @@ ${entries.join("\n")}
924
1090
  };
925
1091
  const exportFile = (paths) => `${paths.map((path) => `export * from ${JSON.stringify(path)};`).join("\n")}
926
1092
  `;
927
- const drizzleFiles = (layout, includeRelations, selection) => {
1093
+ const drizzleFiles = (database, layout, includeRelations, selection) => {
928
1094
  const selected = new Set(selection.tables);
929
- const tables = canonicalTables
1095
+ const tables = canonicalTablesByDatabase[database]
930
1096
  .filter((table) => selected.has(table.name))
931
- .map((table) => parsedTableByName.get(table.name));
1097
+ .map((table) => parsedTableByDatabase[database].get(table.name));
932
1098
  if (layout === "single") {
933
- let content = renderDrizzleTables(tables);
1099
+ let content = renderDrizzleTables(database, tables);
934
1100
  if (includeRelations) {
935
1101
  const schema = `{ ${tables
936
1102
  .map((table) => camelCase(table.canonical.name))
937
1103
  .join(", ")} }`;
938
- content += `\n${renderRelations(tables, schema)}`;
1104
+ content += `\n${renderRelations(database, tables, schema)}`;
939
1105
  }
940
1106
  return [{ path: "schema.ts", mediaType: "text/typescript", content }];
941
1107
  }
@@ -952,7 +1118,7 @@ const drizzleFiles = (layout, includeRelations, selection) => {
952
1118
  const files = groups.map((group) => ({
953
1119
  path: `${directory}/${group.name}.ts`,
954
1120
  mediaType: "text/typescript",
955
- content: renderDrizzleTables(group.tables),
1121
+ content: renderDrizzleTables(database, group.tables),
956
1122
  }));
957
1123
  files.push({
958
1124
  path: `${directory}/index.ts`,
@@ -963,7 +1129,7 @@ const drizzleFiles = (layout, includeRelations, selection) => {
963
1129
  files.push({
964
1130
  path: "relations.ts",
965
1131
  mediaType: "text/typescript",
966
- content: renderRelations(tables, "schema", `./${directory}/index.js`),
1132
+ content: renderRelations(database, tables, "schema", `./${directory}/index.js`),
967
1133
  });
968
1134
  }
969
1135
  files.push({
@@ -976,6 +1142,224 @@ const drizzleFiles = (layout, includeRelations, selection) => {
976
1142
  });
977
1143
  return files;
978
1144
  };
1145
+ const developmentSeedCoreImports = (includePassword) => `import {
1146
+ DevelopmentSeed,
1147
+ DevelopmentSeedLive,
1148
+ } from "@effect-auth/core/DevelopmentSeed";
1149
+ import { IdentityKindRegistryDefaultLive } from "@effect-auth/core/Identity";
1150
+ ${includePassword
1151
+ ? `import { WebCryptoLive } from "@effect-auth/core/Crypto";
1152
+ import { Pbkdf2PasswordHasherLive } from "@effect-auth/core/Password";
1153
+ `
1154
+ : ""}`;
1155
+ const developmentSeedDependenciesSource = (includePassword) => includePassword
1156
+ ? `const DevelopmentSeedSupportLive = Layer.mergeAll(
1157
+ IdentityKindRegistryDefaultLive,
1158
+ Pbkdf2PasswordHasherLive().pipe(Layer.provide(WebCryptoLive())),
1159
+ );
1160
+ `
1161
+ : `const DevelopmentSeedSupportLive = IdentityKindRegistryDefaultLive;
1162
+ `;
1163
+ const developmentSeedProgramSource = (includePassword) => `const developmentSeedProgram = Effect.gen(function* () {
1164
+ const seed = yield* DevelopmentSeed;
1165
+ const result = yield* seed.${includePassword ? "seedPasswordUsers" : "seedUsers"};
1166
+ yield* Effect.logInfo(
1167
+ \`Development auth seed complete: \${result.created} created, \${result.existing} existing.\`,
1168
+ );
1169
+ return result;
1170
+ });
1171
+ `;
1172
+ const developmentSeedConfigurationErrorSource = `export class DevelopmentSeedConfigurationError extends Data.TaggedError(
1173
+ "DevelopmentSeedConfigurationError",
1174
+ )<{ readonly message: string }> {}
1175
+ `;
1176
+ const alchemyD1SeedSource = (adapter, includePassword, complexity) => {
1177
+ const drizzle = adapter === "drizzle";
1178
+ const storageModule = drizzle
1179
+ ? "DrizzleD1SqliteStorage"
1180
+ : "EffectQbSqliteStorage";
1181
+ const storageType = drizzle
1182
+ ? "DrizzleD1DatabaseLike"
1183
+ : "D1EffectQbDatabaseLike";
1184
+ const storageLayer = drizzle
1185
+ ? "DrizzleD1SqliteAuthStorageLive"
1186
+ : "D1EffectQbSqliteAuthStorageLive";
1187
+ const medium = complexity === "medium";
1188
+ return `import type { ${storageType} } from "@effect-auth/core/${storageModule}";
1189
+ import { ${storageLayer} } from "@effect-auth/core/${storageModule}";
1190
+ ${developmentSeedCoreImports(includePassword)}import { Data, Effect, Layer } from "effect";
1191
+
1192
+ ${developmentSeedConfigurationErrorSource}
1193
+ ${developmentSeedDependenciesSource(includePassword)}
1194
+ ${developmentSeedProgramSource(includePassword)}
1195
+ ${medium ? "export " : ""}const AppDevelopmentSeedLive = (database: ${storageType}) =>
1196
+ DevelopmentSeedLive.pipe(
1197
+ Layer.provideMerge(
1198
+ Layer.mergeAll(
1199
+ ${storageLayer}(database),
1200
+ DevelopmentSeedSupportLive,
1201
+ ),
1202
+ ),
1203
+ );
1204
+
1205
+ ${medium ? "export const runDevelopmentSeed" : "export const seedDevelopmentAuth"} = (
1206
+ database: ${storageType},
1207
+ isDevelopment: boolean,
1208
+ ) =>
1209
+ Effect.gen(function* () {
1210
+ if (!isDevelopment) {
1211
+ return yield* new DevelopmentSeedConfigurationError({
1212
+ message: "Refusing to seed Effect Auth outside Alchemy development mode.",
1213
+ });
1214
+ }
1215
+ return yield* developmentSeedProgram.pipe(
1216
+ Effect.provide(AppDevelopmentSeedLive(database)),
1217
+ );
1218
+ });
1219
+ ${medium
1220
+ ? ""
1221
+ : `
1222
+ // In alchemy.run.ts, after migrations:
1223
+ // const alchemy = yield* Alchemy.AlchemyContext;
1224
+ // yield* Database;
1225
+ // const client = yield* Cloudflare.D1.QueryDatabase(Database);
1226
+ // yield* seedDevelopmentAuth(yield* client.raw, alchemy.dev);
1227
+ `}`;
1228
+ };
1229
+ const standaloneConfigurationSource = (value) => {
1230
+ const readValue = value === "sqlite"
1231
+ ? 'const filename = process.env.EFFECT_AUTH_SQLITE_FILE ?? "./data/auth.sqlite";'
1232
+ : `const databaseUrl = process.env.DATABASE_URL;
1233
+ if (databaseUrl === undefined || databaseUrl.length === 0) {
1234
+ return yield* new DevelopmentSeedConfigurationError({
1235
+ message: "DATABASE_URL is required.",
1236
+ });
1237
+ }`;
1238
+ const returned = value === "sqlite" ? "filename" : "databaseUrl";
1239
+ return `const developmentSeedConfiguration = Effect.gen(function* () {
1240
+ if (
1241
+ process.env.NODE_ENV === "production" ||
1242
+ process.env.EFFECT_AUTH_DEVELOPMENT_SEED !== "true"
1243
+ ) {
1244
+ return yield* new DevelopmentSeedConfigurationError({
1245
+ message: "Set EFFECT_AUTH_DEVELOPMENT_SEED=true to run the development seed.",
1246
+ });
1247
+ }
1248
+ ${readValue}
1249
+ return ${returned};
1250
+ });
1251
+ `;
1252
+ };
1253
+ const sqliteSeedSource = (runtime, includePassword, complexity) => {
1254
+ const storageModule = runtime === "bun" ? "DrizzleBunSqliteStorage" : "DrizzleNodeSqliteStorage";
1255
+ const storageLayer = runtime === "bun"
1256
+ ? "DrizzleBunSqliteAuthStorageLive"
1257
+ : "DrizzleNodeSqliteAuthStorageLive";
1258
+ const medium = complexity === "medium";
1259
+ return `import { ${storageLayer} } from "@effect-auth/core/${storageModule}";
1260
+ ${developmentSeedCoreImports(includePassword)}import { Data, Effect, Layer } from "effect";
1261
+
1262
+ ${developmentSeedConfigurationErrorSource}
1263
+ ${developmentSeedDependenciesSource(includePassword)}
1264
+ ${developmentSeedProgramSource(includePassword)}
1265
+ ${standaloneConfigurationSource("sqlite")}
1266
+ ${medium ? "export " : ""}const AppDevelopmentSeedLive = (filename: string) =>
1267
+ DevelopmentSeedLive.pipe(
1268
+ Layer.provideMerge(
1269
+ Layer.mergeAll(
1270
+ ${storageLayer}({ filename }),
1271
+ DevelopmentSeedSupportLive,
1272
+ ),
1273
+ ),
1274
+ );
1275
+
1276
+ ${medium ? "export " : ""}const runDevelopmentSeed = Effect.gen(function* () {
1277
+ const filename = yield* developmentSeedConfiguration;
1278
+ return yield* developmentSeedProgram.pipe(
1279
+ Effect.provide(AppDevelopmentSeedLive(filename)),
1280
+ Effect.scoped,
1281
+ );
1282
+ });
1283
+ ${medium ? "" : "\nawait Effect.runPromise(runDevelopmentSeed);\n"}`;
1284
+ };
1285
+ const postgresSeedSource = (adapter, includePassword, complexity) => {
1286
+ const drizzle = adapter === "drizzle";
1287
+ const medium = complexity === "medium";
1288
+ const storageImports = drizzle
1289
+ ? 'import { DrizzlePostgresAuthStorageLive } from "@effect-auth/core/DrizzlePostgresStorage";\n'
1290
+ : `import * as PgClient from "@effect/sql-pg/PgClient";
1291
+ import { EffectQbPostgresCoreStoresLive } from "@effect-auth/core/EffectQbPostgresStorage";
1292
+ `;
1293
+ const layerSource = drizzle
1294
+ ? `${medium ? "export " : ""}const AppDevelopmentSeedLive = (databaseUrl: string) =>
1295
+ DevelopmentSeedLive.pipe(
1296
+ Layer.provideMerge(
1297
+ Layer.mergeAll(
1298
+ DrizzlePostgresAuthStorageLive({ url: Redacted.make(databaseUrl) }),
1299
+ DevelopmentSeedSupportLive,
1300
+ ),
1301
+ ),
1302
+ );`
1303
+ : `${medium ? "export " : ""}const AppDevelopmentSeedLive = (sql: PgClient.PgClient) =>
1304
+ DevelopmentSeedLive.pipe(
1305
+ Layer.provideMerge(
1306
+ Layer.mergeAll(
1307
+ EffectQbPostgresCoreStoresLive(sql),
1308
+ DevelopmentSeedSupportLive,
1309
+ ),
1310
+ ),
1311
+ );`;
1312
+ const runSource = drizzle
1313
+ ? `const databaseUrl = yield* developmentSeedConfiguration;
1314
+ return yield* developmentSeedProgram.pipe(
1315
+ Effect.provide(AppDevelopmentSeedLive(databaseUrl)),
1316
+ Effect.scoped,
1317
+ );`
1318
+ : `const databaseUrl = yield* developmentSeedConfiguration;
1319
+ const PostgresClientLive = PgClient.layer({
1320
+ url: Redacted.make(databaseUrl),
1321
+ });
1322
+ return yield* Effect.gen(function* () {
1323
+ const sql = yield* PgClient.PgClient;
1324
+ return yield* developmentSeedProgram.pipe(
1325
+ Effect.provide(AppDevelopmentSeedLive(sql)),
1326
+ );
1327
+ }).pipe(Effect.provide(PostgresClientLive), Effect.scoped);`;
1328
+ return `${storageImports}${developmentSeedCoreImports(includePassword)}import { Data, Effect, Layer, Redacted } from "effect";
1329
+
1330
+ ${developmentSeedConfigurationErrorSource}
1331
+ ${developmentSeedDependenciesSource(includePassword)}
1332
+ ${developmentSeedProgramSource(includePassword)}
1333
+ ${standaloneConfigurationSource("postgres")}
1334
+ ${layerSource}
1335
+
1336
+ ${medium ? "export " : ""}const runDevelopmentSeed = Effect.gen(function* () {
1337
+ ${runSource}
1338
+ });
1339
+ ${medium ? "" : "\nawait Effect.runPromise(runDevelopmentSeed);\n"}`;
1340
+ };
1341
+ const developmentSeedFiles = (adapter, target, includePassword, complexity) => {
1342
+ let content;
1343
+ switch (target) {
1344
+ case "alchemy-d1": {
1345
+ content = alchemyD1SeedSource(adapter, includePassword, complexity);
1346
+ break;
1347
+ }
1348
+ case "bun-sqlite": {
1349
+ content = sqliteSeedSource("bun", includePassword, complexity);
1350
+ break;
1351
+ }
1352
+ case "node-sqlite": {
1353
+ content = sqliteSeedSource("node", includePassword, complexity);
1354
+ break;
1355
+ }
1356
+ case "postgres": {
1357
+ content = postgresSeedSource(adapter, includePassword, complexity);
1358
+ break;
1359
+ }
1360
+ }
1361
+ return [{ path: "seed-auth.ts", mediaType: "text/typescript", content }];
1362
+ };
979
1363
  const fingerprint = (value) => {
980
1364
  let hash = 2_166_136_261;
981
1365
  for (let index = 0; index < value.length; index += 1) {
@@ -988,21 +1372,33 @@ const fingerprint = (value) => {
988
1372
  export const parseStorageSchemaGeneratorRequest = (request) => normalizeRequest(request);
989
1373
  export const generateStorageArtifacts = (request) => {
990
1374
  const normalized = normalizeRequest(request);
991
- const resolved = resolveStorageFeatures(normalized.features);
1375
+ const featureSelection = resolveStorageFeatures(normalized.features);
1376
+ const resolved = normalized.outputs.some(({ kind }) => kind === "development-seed")
1377
+ ? includeDevelopmentSeedRequirements(featureSelection)
1378
+ : featureSelection;
1379
+ const schemaVersion = storageSchemaVersions[normalized.database];
992
1380
  const files = [];
993
1381
  const warnings = [];
994
1382
  for (const output of normalized.outputs) {
995
1383
  if (output.kind === "migration-sql") {
996
- files.push(...sqlFiles(output.layout, resolved));
1384
+ files.push(...sqlFiles(normalized.database, output.layout, resolved));
997
1385
  }
998
- else {
999
- files.push(...drizzleFiles(output.layout, output.includeRelations, resolved));
1386
+ else if (output.kind === "drizzle-schema") {
1387
+ files.push(...drizzleFiles(normalized.database, output.layout, output.includeRelations, resolved));
1000
1388
  if (output.includeRelations) {
1001
- warnings.push("Drizzle relations are application-level only and do not create PostgreSQL foreign keys.");
1389
+ warnings.push(`Drizzle relations are application-level only and do not create ${normalized.database === "postgres" ? "PostgreSQL" : "SQLite"} foreign keys.`);
1390
+ }
1391
+ }
1392
+ else {
1393
+ files.push(...developmentSeedFiles(normalized.adapter, output.target, normalized.features.includes("password"), output.complexity));
1394
+ warnings.push("The generated development seed creates public demo credentials and must never run against production data.");
1395
+ if (!normalized.features.includes("password")) {
1396
+ warnings.push("The selected features do not include password authentication, so the development seed does not create password credentials.");
1002
1397
  }
1003
1398
  }
1004
1399
  }
1005
1400
  const fingerprintInput = JSON.stringify({
1401
+ schemaVersion,
1006
1402
  request: normalized,
1007
1403
  resolved,
1008
1404
  files,
@@ -1011,7 +1407,7 @@ export const generateStorageArtifacts = (request) => {
1011
1407
  return {
1012
1408
  apiVersion: storageSchemaGeneratorApiVersion,
1013
1409
  generatorVersion: storageSchemaGeneratorVersion,
1014
- schemaVersion: storageSchemaVersion,
1410
+ schemaVersion,
1015
1411
  fingerprint: fingerprint(fingerprintInput),
1016
1412
  request: normalized,
1017
1413
  resolved,