alepha 0.20.2 → 0.20.3

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 (208) hide show
  1. package/README.md +0 -1
  2. package/assets/swagger-ui/swagger-ui-bundle.js +1 -1
  3. package/assets/swagger-ui/swagger-ui.css +1 -1
  4. package/dist/api/audits/index.browser.js +49 -0
  5. package/dist/api/audits/index.browser.js.map +1 -1
  6. package/dist/api/audits/index.d.ts.map +1 -1
  7. package/dist/api/audits/index.js +49 -0
  8. package/dist/api/audits/index.js.map +1 -1
  9. package/dist/api/files/index.d.ts.map +1 -1
  10. package/dist/api/files/index.js.map +1 -1
  11. package/dist/api/jobs/index.d.ts +16 -75
  12. package/dist/api/jobs/index.d.ts.map +1 -1
  13. package/dist/api/jobs/index.js.map +1 -1
  14. package/dist/api/keys/index.js.map +1 -1
  15. package/dist/api/notifications/index.d.ts +1 -10
  16. package/dist/api/notifications/index.d.ts.map +1 -1
  17. package/dist/api/organizations/index.d.ts.map +1 -1
  18. package/dist/api/parameters/index.browser.js +37 -0
  19. package/dist/api/parameters/index.browser.js.map +1 -1
  20. package/dist/api/parameters/index.d.ts +4 -65
  21. package/dist/api/parameters/index.d.ts.map +1 -1
  22. package/dist/api/parameters/index.js +37 -0
  23. package/dist/api/parameters/index.js.map +1 -1
  24. package/dist/api/payments/index.d.ts.map +1 -1
  25. package/dist/api/payments/index.js.map +1 -1
  26. package/dist/api/users/index.d.ts +207 -5184
  27. package/dist/api/users/index.d.ts.map +1 -1
  28. package/dist/api/users/index.js +2 -4
  29. package/dist/api/users/index.js.map +1 -1
  30. package/dist/api/verifications/index.d.ts.map +1 -1
  31. package/dist/api/verifications/index.js +2 -1
  32. package/dist/api/verifications/index.js.map +1 -1
  33. package/dist/bucket/index.js +5 -1
  34. package/dist/bucket/index.js.map +1 -1
  35. package/dist/bucket/index.workerd.js +5 -1
  36. package/dist/bucket/index.workerd.js.map +1 -1
  37. package/dist/cache/core/index.js.map +1 -1
  38. package/dist/cache/core/index.workerd.js.map +1 -1
  39. package/dist/captcha/index.js.map +1 -1
  40. package/dist/cli/core/index.d.ts +217 -11647
  41. package/dist/cli/core/index.d.ts.map +1 -1
  42. package/dist/cli/core/index.js +706 -42
  43. package/dist/cli/core/index.js.map +1 -1
  44. package/dist/cli/devtools/index.js +7 -1
  45. package/dist/cli/devtools/index.js.map +1 -1
  46. package/dist/cli/platform/index.d.ts +41 -64
  47. package/dist/cli/platform/index.d.ts.map +1 -1
  48. package/dist/cli/platform/index.js +47 -0
  49. package/dist/cli/platform/index.js.map +1 -1
  50. package/dist/cli/vendor/index.js +15 -0
  51. package/dist/cli/vendor/index.js.map +1 -1
  52. package/dist/command/index.js +1 -1
  53. package/dist/command/index.js.map +1 -1
  54. package/dist/core/index.browser.js.map +1 -1
  55. package/dist/core/index.d.ts +2 -8
  56. package/dist/core/index.d.ts.map +1 -1
  57. package/dist/core/index.js.map +1 -1
  58. package/dist/core/index.native.js.map +1 -1
  59. package/dist/core/index.workerd.js.map +1 -1
  60. package/dist/crypto/index.js.map +1 -1
  61. package/dist/datetime/index.js.map +1 -1
  62. package/dist/email/core/index.js.map +1 -1
  63. package/dist/email/smtp/index.js +2 -10522
  64. package/dist/email/smtp/index.js.map +1 -1
  65. package/dist/fake/index.d.ts +4 -8085
  66. package/dist/fake/index.d.ts.map +1 -1
  67. package/dist/fake/index.js +3 -33554
  68. package/dist/fake/index.js.map +1 -1
  69. package/dist/lock/core/index.js.map +1 -1
  70. package/dist/lock/redis/index.js.map +1 -1
  71. package/dist/logger/index.js +32 -1
  72. package/dist/logger/index.js.map +1 -1
  73. package/dist/mcp/index.js +5 -1
  74. package/dist/mcp/index.js.map +1 -1
  75. package/dist/orm/core/index.browser.js +1 -361
  76. package/dist/orm/core/index.browser.js.map +1 -1
  77. package/dist/orm/core/index.bun.js +14 -406
  78. package/dist/orm/core/index.bun.js.map +1 -1
  79. package/dist/orm/core/index.d.ts +96 -5117
  80. package/dist/orm/core/index.d.ts.map +1 -1
  81. package/dist/orm/core/index.js +23 -419
  82. package/dist/orm/core/index.js.map +1 -1
  83. package/dist/orm/postgres/index.bun.js +17 -20
  84. package/dist/orm/postgres/index.bun.js.map +1 -1
  85. package/dist/orm/postgres/index.d.ts +2 -613
  86. package/dist/orm/postgres/index.d.ts.map +1 -1
  87. package/dist/orm/postgres/index.js +17 -20
  88. package/dist/orm/postgres/index.js.map +1 -1
  89. package/dist/react/core/index.js.map +1 -1
  90. package/dist/react/i18n/index.js.map +1 -1
  91. package/dist/react/intro/index.js +22 -17
  92. package/dist/react/intro/index.js.map +1 -1
  93. package/dist/react/router/index.browser.js +78 -2
  94. package/dist/react/router/index.browser.js.map +1 -1
  95. package/dist/react/router/index.d.ts +22 -1
  96. package/dist/react/router/index.d.ts.map +1 -1
  97. package/dist/react/router/index.js +102 -4
  98. package/dist/react/router/index.js.map +1 -1
  99. package/dist/react/testing/index.d.ts +1 -411
  100. package/dist/react/testing/index.d.ts.map +1 -1
  101. package/dist/react/testing/index.js +13 -12293
  102. package/dist/react/testing/index.js.map +1 -1
  103. package/dist/react/ui/index.js +3 -0
  104. package/dist/react/ui/index.js.map +1 -1
  105. package/dist/react/websocket/index.js.map +1 -1
  106. package/dist/redis/index.js.map +1 -1
  107. package/dist/scheduler/index.d.ts +1 -83
  108. package/dist/scheduler/index.d.ts.map +1 -1
  109. package/dist/scheduler/index.js +2 -391
  110. package/dist/scheduler/index.js.map +1 -1
  111. package/dist/scheduler/index.workerd.js +2 -391
  112. package/dist/scheduler/index.workerd.js.map +1 -1
  113. package/dist/security/index.browser.js.map +1 -1
  114. package/dist/security/index.d.ts +2 -325
  115. package/dist/security/index.d.ts.map +1 -1
  116. package/dist/security/index.js +3 -1362
  117. package/dist/security/index.js.map +1 -1
  118. package/dist/server/auth/index.d.ts +1 -1054
  119. package/dist/server/auth/index.d.ts.map +1 -1
  120. package/dist/server/auth/index.js +16 -1224
  121. package/dist/server/auth/index.js.map +1 -1
  122. package/dist/server/cookies/index.js.map +1 -1
  123. package/dist/server/core/index.browser.js.map +1 -1
  124. package/dist/server/core/index.d.ts +1 -4
  125. package/dist/server/core/index.d.ts.map +1 -1
  126. package/dist/server/core/index.js +19 -4
  127. package/dist/server/core/index.js.map +1 -1
  128. package/dist/server/links/index.browser.js.map +1 -1
  129. package/dist/server/links/index.js.map +1 -1
  130. package/dist/server/metrics/index.d.ts +1 -514
  131. package/dist/server/metrics/index.d.ts.map +1 -1
  132. package/dist/server/metrics/index.js +4 -4356
  133. package/dist/server/metrics/index.js.map +1 -1
  134. package/dist/server/rate-limit/index.js.map +1 -1
  135. package/dist/server/static/index.js.map +1 -1
  136. package/dist/server/swagger/index.js +1 -1
  137. package/dist/server/swagger/index.js.map +1 -1
  138. package/dist/sms/index.js.map +1 -1
  139. package/dist/system/index.browser.js.map +1 -1
  140. package/dist/system/index.js.map +1 -1
  141. package/dist/system/index.workerd.js.map +1 -1
  142. package/dist/topic/core/index.js.map +1 -1
  143. package/dist/websocket/index.browser.js +21 -0
  144. package/dist/websocket/index.browser.js.map +1 -1
  145. package/dist/websocket/index.js +21 -0
  146. package/dist/websocket/index.js.map +1 -1
  147. package/package.json +18 -15
  148. package/src/api/files/__tests__/FileController.spec.ts +1 -1
  149. package/src/api/jobs/__tests__/$job.spec.ts +5 -1
  150. package/src/api/users/schemas/userQuerySchema.ts +0 -1
  151. package/src/api/users/services/UserService.ts +1 -5
  152. package/src/api/verifications/__tests__/CodeVerification.spec.ts +14 -0
  153. package/src/api/verifications/__tests__/LinkVerification.spec.ts +14 -0
  154. package/src/api/verifications/services/VerificationService.ts +1 -0
  155. package/src/cli/core/__tests__/init.spec.ts +208 -0
  156. package/src/cli/core/commands/init.ts +12 -0
  157. package/src/cli/core/services/PackageManagerUtils.ts +23 -6
  158. package/src/cli/core/services/ProjectScaffolder.ts +298 -20
  159. package/src/cli/core/tasks/BuildDockerTask.ts +9 -10
  160. package/src/cli/core/tasks/BuildServerTask.ts +8 -0
  161. package/src/cli/core/templates/apiIndexTs.ts +23 -1
  162. package/src/cli/core/templates/componentsJsonTs.ts +39 -0
  163. package/src/cli/core/templates/mainCss.ts +1 -0
  164. package/src/cli/core/templates/saasAdminLayoutTsx.ts +77 -0
  165. package/src/cli/core/templates/saasAdminPagesTsx.ts +26 -0
  166. package/src/cli/core/templates/saasAuthLayoutTsx.ts +20 -0
  167. package/src/cli/core/templates/saasAuthPagesTsx.ts +62 -0
  168. package/src/cli/core/templates/saasRealmProviderTs.ts +46 -0
  169. package/src/cli/core/templates/webAppRouterTs.ts +104 -1
  170. package/src/cli/core/templates/webIndexTs.ts +23 -1
  171. package/src/cli/platform/__tests__/SecretsCommand.spec.ts +2 -0
  172. package/src/command/providers/CliProvider.ts +1 -1
  173. package/src/core/interfaces/Service.ts +3 -1
  174. package/src/core/providers/TypeProvider.ts +1 -1
  175. package/src/logger/services/Logger.ts +1 -1
  176. package/src/mcp/__tests__/$resource.spec.ts +1 -1
  177. package/src/mcp/__tests__/$tool.spec.ts +1 -1
  178. package/src/mcp/__tests__/McpServerProvider.spec.ts +1 -1
  179. package/src/orm/__tests__/$repository-tests.ts +1 -0
  180. package/src/orm/__tests__/orm-next-tests.ts +2 -67
  181. package/src/orm/__tests__/orm-next.spec.ts +0 -21
  182. package/src/orm/core/index.shared.ts +0 -2
  183. package/src/orm/core/index.ts +1 -2
  184. package/src/orm/core/primitives/$repository.ts +3 -6
  185. package/src/orm/core/providers/drivers/DatabaseProvider.ts +0 -5
  186. package/src/orm/core/providers/drivers/NodeSqliteProvider.ts +11 -13
  187. package/src/orm/core/services/ModelBuilder.ts +1 -13
  188. package/src/orm/core/services/Repository.ts +1 -42
  189. package/src/orm/core/services/SqliteModelBuilder.ts +2 -33
  190. package/src/orm/postgres/services/PostgresModelBuilder.ts +10 -45
  191. package/src/react/intro/components/GettingStartedAuthSlide.tsx +11 -4
  192. package/src/react/router/__tests__/ReactBrowserProvider.browser.spec.ts +213 -2
  193. package/src/react/router/providers/ReactBrowserProvider.ts +73 -0
  194. package/src/react/router/providers/ReactBrowserRouterProvider.ts +1 -1
  195. package/src/react/router/providers/ReactPreloadProvider.ts +1 -1
  196. package/src/react/router/providers/ReactServerProvider.ts +1 -0
  197. package/src/scheduler/providers/CronProvider.ts +1 -1
  198. package/src/security/primitives/$basicAuth.ts +1 -1
  199. package/src/server/auth/providers/ServerAuthProvider.ts +5 -1
  200. package/src/server/core/interfaces/ServerRequest.ts +1 -0
  201. package/src/server/core/providers/ServerProvider.ts +1 -1
  202. package/src/server/core/providers/ServerRouterProvider.ts +2 -2
  203. package/src/server/core/services/HttpClient.ts +1 -1
  204. package/src/server/swagger/providers/ServerSwaggerProvider.ts +1 -1
  205. package/dist/react/testing/chunk-DBEY4PJZ.js +0 -16
  206. package/src/orm/core/__tests__/parseQueryString.spec.ts +0 -196
  207. package/src/orm/core/helpers/parseQueryString.ts +0 -502
  208. package/src/orm/core/primitives/$view.ts +0 -88
@@ -10,7 +10,11 @@ import { alias, customType } from "drizzle-orm/pg-core";
10
10
  import { dirname } from "node:path";
11
11
  import { randomUUID } from "node:crypto";
12
12
  import * as pg from "drizzle-orm/sqlite-core";
13
- import { check, foreignKey, index, sqliteTable, sqliteView, unique, uniqueIndex } from "drizzle-orm/sqlite-core";
13
+ import { check, foreignKey, index, sqliteTable, unique, uniqueIndex } from "drizzle-orm/sqlite-core";
14
+ import { migrate } from "drizzle-orm/better-sqlite3/migrator";
15
+ import { BetterSQLiteSession } from "drizzle-orm/better-sqlite3/session";
16
+ import { BaseSQLiteDatabase } from "drizzle-orm/sqlite-core/db";
17
+ import { SQLiteSyncDialect } from "drizzle-orm/sqlite-core/dialect";
14
18
  import { currentUserAtom } from "alepha/security";
15
19
  import { isSQLWrapper as isSQLWrapper$1 } from "drizzle-orm/sql/sql";
16
20
  export * from "drizzle-orm/pg-core";
@@ -38,6 +42,13 @@ var DbError = class extends AlephaError {
38
42
  */
39
43
  const databaseEnvSchema = t.object({
40
44
  DATABASE_URL: t.optional(t.text()),
45
+ /**
46
+ * Enable or disable push-based schema synchronization (drizzle-kit push).
47
+ *
48
+ * Defaults to `true` in development and test, `false` in production.
49
+ * Set to `false` in development to skip automatic schema sync
50
+ * (e.g. when managing migrations manually).
51
+ */
41
52
  DATABASE_SYNC: t.optional(t.boolean())
42
53
  });
43
54
  //#endregion
@@ -353,9 +364,6 @@ var DatabaseProvider = class {
353
364
  this.entityPrimitives.push(entity);
354
365
  this.builder.buildTable(entity, this);
355
366
  }
356
- registerView(view) {
357
- this.builder.buildView(view, this);
358
- }
359
367
  registerSequence(sequence) {
360
368
  this.sequencePrimitives.push(sequence);
361
369
  this.builder.buildSequence(sequence, this);
@@ -612,58 +620,6 @@ var SequencePrimitive = class extends Primitive {
612
620
  };
613
621
  $sequence[KIND] = SequencePrimitive;
614
622
  //#endregion
615
- //#region ../../src/orm/core/primitives/$view.ts
616
- /**
617
- * Creates a database view primitive from a TypeBox schema and SQL query.
618
- *
619
- * Views are read-only: Repository blocks all write operations.
620
- *
621
- * @example
622
- * ```ts
623
- * import { t } from "alepha";
624
- * import { $view } from "alepha/orm";
625
- * import { sql } from "drizzle-orm";
626
- *
627
- * const userSummary = $view({
628
- * name: "user_summary",
629
- * schema: t.object({
630
- * id: t.uuid(),
631
- * fullName: t.text(),
632
- * orderCount: t.integer(),
633
- * }),
634
- * query: sql`SELECT u.id, u.first_name || ' ' || u.last_name AS full_name, COUNT(o.id) AS order_count FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id`,
635
- * });
636
- *
637
- * // Materialized view (PostgreSQL only)
638
- * const monthlyStats = $view({
639
- * name: "monthly_stats",
640
- * schema: t.object({ ... }),
641
- * query: sql`...`,
642
- * materialized: true,
643
- * });
644
- * ```
645
- */
646
- const $view = (options) => {
647
- return new ViewPrimitive(options);
648
- };
649
- var ViewPrimitive = class {
650
- options;
651
- isView = true;
652
- constructor(options) {
653
- this.options = options;
654
- }
655
- get name() {
656
- return this.options.name;
657
- }
658
- get schema() {
659
- return this.options.schema;
660
- }
661
- get materialized() {
662
- return this.options.materialized ?? false;
663
- }
664
- };
665
- $view[KIND] = ViewPrimitive;
666
- //#endregion
667
623
  //#region ../../src/orm/core/services/ModelBuilder.ts
668
624
  /**
669
625
  * Abstract base class for transforming Alepha Primitives (Entity, Sequence, etc...)
@@ -729,7 +685,7 @@ var ModelBuilder = class {
729
685
  const fkName = fkDef.name || `${entity.name}_${columnNames.join("_")}_fk`;
730
686
  const foreignColumns = fkDef.foreignColumns.map((colRef) => {
731
687
  const entityCol = colRef();
732
- if (!entityCol || !entityCol.entity || !entityCol.name) throw new AlephaError(`Invalid foreign column reference in ${entity.name}`);
688
+ if (!entityCol?.entity || !entityCol.name) throw new AlephaError(`Invalid foreign column reference in ${entity.name}`);
733
689
  if (tableResolver) {
734
690
  const foreignTable = tableResolver(entityCol.entity.name);
735
691
  if (!foreignTable) throw new AlephaError(`Foreign table ${entityCol.entity.name} not found for ${entity.name}`);
@@ -776,13 +732,6 @@ var SqliteModelBuilder = class extends ModelBuilder {
776
732
  const table = sqliteTable(tableName, this.schemaToSqliteColumns(tableName, entity.schema, options.enums, options.tables), this.getTableConfig(entity, options.tables));
777
733
  options.tables.set(tableName, table);
778
734
  }
779
- buildView(view, options) {
780
- const viewName = view.name;
781
- if (options.tables.has(viewName)) return;
782
- if (view.materialized) throw new AlephaError("SQLite does not support materialized views");
783
- const drizzleView = sqliteView(viewName, this.schemaToSqliteColumns(viewName, view.schema, /* @__PURE__ */ new Map(), options.tables)).existing();
784
- options.tables.set(viewName, drizzleView);
785
- }
786
735
  buildSequence(sequence, options) {
787
736
  throw new AlephaError("SQLite does not support sequences");
788
737
  }
@@ -826,10 +775,8 @@ var SqliteModelBuilder = class extends ModelBuilder {
826
775
  col = col.generatedAlwaysAs(gen.expression, { mode: gen.mode ?? "virtual" });
827
776
  }
828
777
  if (schema.required?.includes(key)) col = col.notNull();
829
- return {
830
- ...columns,
831
- [key]: col
832
- };
778
+ columns[key] = col;
779
+ return columns;
833
780
  }, {});
834
781
  };
835
782
  mapFieldToSqliteColumn = (tableName, fieldName, value, enums) => {
@@ -1183,7 +1130,7 @@ var NodeSqliteProvider = class NodeSqliteProvider extends DatabaseProvider {
1183
1130
  onStart = $hook({
1184
1131
  on: "start",
1185
1132
  handler: async () => {
1186
- const { DatabaseSync } = createRequire(import.meta.url)("node:sqlite");
1133
+ const { DatabaseSync } = await import("node:sqlite");
1187
1134
  const filepath = this.url.replace("sqlite://", "").replace("sqlite:", "");
1188
1135
  if (filepath !== ":memory:" && filepath !== "") {
1189
1136
  const dir = dirname(filepath);
@@ -1288,18 +1235,14 @@ var NodeSqliteProvider = class NodeSqliteProvider extends DatabaseProvider {
1288
1235
  */
1289
1236
  initDrizzle() {
1290
1237
  this.shimDatabaseSync();
1291
- const require = createRequire(import.meta.url);
1292
- const { BetterSQLiteSession } = require("drizzle-orm/better-sqlite3/session");
1293
- const { SQLiteSyncDialect } = require("drizzle-orm/sqlite-core/dialect");
1294
- const { BaseSQLiteDatabase } = require("drizzle-orm/sqlite-core/db");
1295
1238
  const dialect = new SQLiteSyncDialect();
1296
- this.drizzleDb = new BaseSQLiteDatabase("sync", dialect, new BetterSQLiteSession(this.sqlite, dialect, void 0, { logger: { logQuery: (query, params) => {
1239
+ const session = new BetterSQLiteSession(this.sqlite, dialect, void 0, { logger: { logQuery: (query, params) => {
1297
1240
  this.log.trace(query, { params });
1298
- } } }));
1241
+ } } });
1242
+ this.drizzleDb = new BaseSQLiteDatabase("sync", dialect, session, void 0);
1299
1243
  this.log.debug("Using node:sqlite with sync driver");
1300
1244
  }
1301
1245
  async executeMigrations(migrationsFolder) {
1302
- const { migrate } = createRequire(import.meta.url)("drizzle-orm/better-sqlite3/migrator");
1303
1246
  migrate(this.drizzleDb, { migrationsFolder });
1304
1247
  }
1305
1248
  };
@@ -1951,8 +1894,7 @@ var Repository = class Repository {
1951
1894
  constructor(entity, provider = DatabaseProvider) {
1952
1895
  this.entity = entity;
1953
1896
  this.provider = this.alepha.inject(provider);
1954
- if (entity.isView) this.provider.registerView(entity);
1955
- else this.provider.registerEntity(entity);
1897
+ this.provider.registerEntity(entity);
1956
1898
  }
1957
1899
  /**
1958
1900
  * Represents the primary key of the table.
@@ -1977,12 +1919,6 @@ var Repository = class Repository {
1977
1919
  return this.entity.name;
1978
1920
  }
1979
1921
  /**
1980
- * Whether this repository is backed by a view (read-only).
1981
- */
1982
- get isReadOnly() {
1983
- return this.entity.isView === true;
1984
- }
1985
- /**
1986
1922
  * Getter for the database connection from the database provider.
1987
1923
  *
1988
1924
  * Automatically picks up a transaction from `alepha.store` if one was set
@@ -2258,7 +2194,6 @@ var Repository = class Repository {
2258
2194
  * @returns The ID of the created entity.
2259
2195
  */
2260
2196
  async create(data, opts = {}) {
2261
- this.assertWritable();
2262
2197
  this.stampOrganization(data);
2263
2198
  await this.alepha.events.emit("repository:create:before", {
2264
2199
  tableName: this.tableName,
@@ -2287,7 +2222,6 @@ var Repository = class Repository {
2287
2222
  * @returns The created entities.
2288
2223
  */
2289
2224
  async createMany(values, opts = {}) {
2290
- this.assertWritable();
2291
2225
  if (values.length === 0) return [];
2292
2226
  for (const value of values) this.stampOrganization(value);
2293
2227
  await this.alepha.events.emit("repository:create:before", {
@@ -2343,7 +2277,6 @@ var Repository = class Repository {
2343
2277
  * ```
2344
2278
  */
2345
2279
  async upsert(data, opts = {}) {
2346
- this.assertWritable();
2347
2280
  this.stampOrganization(data);
2348
2281
  await this.alepha.events.emit("repository:create:before", {
2349
2282
  tableName: this.tableName,
@@ -2380,7 +2313,6 @@ var Repository = class Repository {
2380
2313
  * Find an entity and update it.
2381
2314
  */
2382
2315
  async updateOne(where, data, opts = {}) {
2383
- this.assertWritable();
2384
2316
  await this.alepha.events.emit("repository:update:before", {
2385
2317
  tableName: this.tableName,
2386
2318
  where,
@@ -2431,7 +2363,6 @@ var Repository = class Repository {
2431
2363
  * @see {@link DbVersionMismatchError}
2432
2364
  */
2433
2365
  async save(entity, opts = {}) {
2434
- this.assertWritable();
2435
2366
  const row = entity;
2436
2367
  const id = row[this.id.key];
2437
2368
  if (id == null) throw new AlephaError("Cannot save entity without ID - missing primary key in value");
@@ -2469,7 +2400,6 @@ var Repository = class Repository {
2469
2400
  * Find many entities and update all of them.
2470
2401
  */
2471
2402
  async updateMany(where, data, opts = {}) {
2472
- this.assertWritable();
2473
2403
  await this.alepha.events.emit("repository:update:before", {
2474
2404
  tableName: this.tableName,
2475
2405
  where,
@@ -2498,7 +2428,6 @@ var Repository = class Repository {
2498
2428
  * @returns Array of deleted entity IDs
2499
2429
  */
2500
2430
  async deleteMany(where = {}, opts = {}) {
2501
- this.assertWritable();
2502
2431
  const deletedAt = this.deletedAt();
2503
2432
  if (deletedAt && !opts.force) return await this.updateMany(where, { [deletedAt.key]: opts.now ?? this.dateTimeProvider.nowISOString() }, opts);
2504
2433
  where = this.withOrganization(where);
@@ -2752,19 +2681,6 @@ var Repository = class Repository {
2752
2681
  return entity;
2753
2682
  }
2754
2683
  /**
2755
- * Throw if this repository is read-only (backed by a view).
2756
- */
2757
- assertWritable() {
2758
- if (this.isReadOnly) throw new AlephaError(`Cannot write to view '${this.tableName}'. Views are read-only.`);
2759
- }
2760
- /**
2761
- * Refresh a materialized view. PostgreSQL only.
2762
- */
2763
- async refresh() {
2764
- if (!this.entity.materialized) throw new AlephaError(`Cannot refresh '${this.tableName}'. Only materialized views support refresh.`);
2765
- await this.provider.execute(`REFRESH MATERIALIZED VIEW ${this.tableName}`);
2766
- }
2767
- /**
2768
2684
  * Build a cache key from method name and query parameters.
2769
2685
  */
2770
2686
  buildCacheKey(method, query) {
@@ -2905,314 +2821,6 @@ var DbConnectionError = class DbConnectionError extends DbError {
2905
2821
  }
2906
2822
  };
2907
2823
  //#endregion
2908
- //#region ../../src/orm/core/helpers/parseQueryString.ts
2909
- /**
2910
- * Parse a string query into a PgQueryWhere object.
2911
- *
2912
- * Supported syntax:
2913
- * - Simple equality: "name=John"
2914
- * - Wildcard patterns: "name=John*" (startsWith), "name=*John" (endsWith), "name=*John*" (contains)
2915
- * - Operators: "age>18", "age>=18", "age<65", "age<=65", "status!=active"
2916
- * - NULL checks: "deletedAt=null", "email!=null"
2917
- * - IN arrays: "status=[pending,active]"
2918
- * - AND conditions: "name=John&age>18"
2919
- * - OR conditions: "name=John|email=john@example.com"
2920
- * - Nested AND/OR: "(name=John|name=Jane)&age>18"
2921
- * - JSONB nested: "profile.city=Paris"
2922
- *
2923
- * @example
2924
- * ```ts
2925
- * // Simple equality
2926
- * parseQueryString("name=John")
2927
- * // => { name: { eq: "John" } }
2928
- *
2929
- * // Wildcard patterns
2930
- * parseQueryString("name=John*") // startsWith
2931
- * // => { name: { startsWith: "John" } }
2932
- * parseQueryString("name=*Smith") // endsWith
2933
- * // => { name: { endsWith: "Smith" } }
2934
- * parseQueryString("name=*oh*") // contains
2935
- * // => { name: { contains: "oh" } }
2936
- *
2937
- * // Multiple conditions
2938
- * parseQueryString("name=John&age>18")
2939
- * // => { and: [{ name: { eq: "John" } }, { age: { gt: 18 } }] }
2940
- *
2941
- * // OR conditions
2942
- * parseQueryString("status=active|status=pending")
2943
- * // => { or: [{ status: { eq: "active" } }, { status: { eq: "pending" } }] }
2944
- *
2945
- * // Complex nested
2946
- * parseQueryString("(name=John|name=Jane)&age>18&status!=archived")
2947
- * // => { and: [
2948
- * // { or: [{ name: { eq: "John" } }, { name: { eq: "Jane" } }] },
2949
- * // { age: { gt: 18 } },
2950
- * // { status: { ne: "archived" } }
2951
- * // ] }
2952
- *
2953
- * // JSONB nested query
2954
- * parseQueryString("profile.city=Paris&profile.age>25")
2955
- * // => { profile: { city: { eq: "Paris" }, age: { gt: 25 } } }
2956
- * ```
2957
- */
2958
- function parseQueryString(query) {
2959
- if (!query || query.trim() === "") return {};
2960
- return new QueryStringParser(query).parse();
2961
- }
2962
- var QueryStringParser = class {
2963
- pos = 0;
2964
- query;
2965
- constructor(query) {
2966
- this.query = query.trim();
2967
- }
2968
- parse() {
2969
- return this.parseExpression();
2970
- }
2971
- parseExpression() {
2972
- return this.parseOr();
2973
- }
2974
- parseOr() {
2975
- const left = this.parseAnd();
2976
- if (this.peek() === "|") {
2977
- const conditions = [left];
2978
- while (this.peek() === "|") {
2979
- this.consume("|");
2980
- conditions.push(this.parseAnd());
2981
- }
2982
- return { or: conditions };
2983
- }
2984
- return left;
2985
- }
2986
- parseAnd() {
2987
- const left = this.parsePrimary();
2988
- if (this.peek() === "&") {
2989
- const conditions = [left];
2990
- while (this.peek() === "&") {
2991
- this.consume("&");
2992
- conditions.push(this.parsePrimary());
2993
- }
2994
- return { and: conditions };
2995
- }
2996
- return left;
2997
- }
2998
- parsePrimary() {
2999
- this.skipWhitespace();
3000
- if (this.peek() === "(") {
3001
- this.consume("(");
3002
- const expr = this.parseExpression();
3003
- this.consume(")");
3004
- return expr;
3005
- }
3006
- return this.parseCondition();
3007
- }
3008
- parseCondition() {
3009
- const field = this.parseFieldPath();
3010
- this.skipWhitespace();
3011
- const operator = this.parseOperator();
3012
- this.skipWhitespace();
3013
- const value = this.parseValue();
3014
- if (value === "") throw new AlephaError(`Expected value for field '${field.join(".")}'`);
3015
- return this.buildCondition(field, operator, value);
3016
- }
3017
- parseFieldPath() {
3018
- const path = [];
3019
- let current = "";
3020
- while (this.pos < this.query.length) {
3021
- const ch = this.query[this.pos];
3022
- if (ch === "." && current) {
3023
- path.push(current);
3024
- current = "";
3025
- this.pos++;
3026
- continue;
3027
- }
3028
- if (ch === "=" || ch === "!" || ch === ">" || ch === "<" || ch === " ") break;
3029
- current += ch;
3030
- this.pos++;
3031
- }
3032
- if (current) path.push(current);
3033
- return path;
3034
- }
3035
- parseOperator() {
3036
- this.skipWhitespace();
3037
- const remaining = this.query.slice(this.pos);
3038
- if (remaining.startsWith(">=")) {
3039
- this.pos += 2;
3040
- return ">=";
3041
- }
3042
- if (remaining.startsWith("<=")) {
3043
- this.pos += 2;
3044
- return "<=";
3045
- }
3046
- if (remaining.startsWith("!=")) {
3047
- this.pos += 2;
3048
- return "!=";
3049
- }
3050
- const ch = this.query[this.pos];
3051
- if (ch === "=" || ch === ">" || ch === "<") {
3052
- this.pos++;
3053
- return ch;
3054
- }
3055
- throw new AlephaError(`Expected operator at position ${this.pos}`);
3056
- }
3057
- parseValue() {
3058
- this.skipWhitespace();
3059
- if (this.query.slice(this.pos, this.pos + 4).toLowerCase() === "null") {
3060
- this.pos += 4;
3061
- return null;
3062
- }
3063
- if (this.query[this.pos] === "[") return this.parseArray();
3064
- if (this.query[this.pos] === "\"" || this.query[this.pos] === "'") return this.parseQuotedString();
3065
- let value = "";
3066
- while (this.pos < this.query.length) {
3067
- const ch = this.query[this.pos];
3068
- if (ch === "&" || ch === "|" || ch === ")") break;
3069
- value += ch;
3070
- this.pos++;
3071
- }
3072
- return this.coerceValue(value.trim());
3073
- }
3074
- parseArray() {
3075
- this.consume("[");
3076
- const values = [];
3077
- while (this.pos < this.query.length && this.query[this.pos] !== "]") {
3078
- this.skipWhitespace();
3079
- if (this.query[this.pos] === "\"" || this.query[this.pos] === "'") values.push(this.parseQuotedString());
3080
- else {
3081
- let value = "";
3082
- while (this.pos < this.query.length && this.query[this.pos] !== "," && this.query[this.pos] !== "]") {
3083
- value += this.query[this.pos];
3084
- this.pos++;
3085
- }
3086
- values.push(this.coerceValue(value.trim()));
3087
- }
3088
- this.skipWhitespace();
3089
- if (this.query[this.pos] === ",") this.pos++;
3090
- }
3091
- this.consume("]");
3092
- return values;
3093
- }
3094
- parseQuotedString() {
3095
- const quote = this.query[this.pos];
3096
- this.pos++;
3097
- let value = "";
3098
- let escaped = false;
3099
- while (this.pos < this.query.length) {
3100
- const ch = this.query[this.pos];
3101
- if (escaped) {
3102
- value += ch;
3103
- escaped = false;
3104
- this.pos++;
3105
- continue;
3106
- }
3107
- if (ch === "\\") {
3108
- escaped = true;
3109
- this.pos++;
3110
- continue;
3111
- }
3112
- if (ch === quote) {
3113
- this.pos++;
3114
- break;
3115
- }
3116
- value += ch;
3117
- this.pos++;
3118
- }
3119
- return value;
3120
- }
3121
- coerceValue(value) {
3122
- if (/^-?\d+$/.test(value)) return parseInt(value, 10);
3123
- if (/^-?\d+\.\d+$/.test(value)) return parseFloat(value);
3124
- if (value.toLowerCase() === "true") return true;
3125
- if (value.toLowerCase() === "false") return false;
3126
- return value;
3127
- }
3128
- buildCondition(path, operator, value) {
3129
- let filterOp;
3130
- if (operator === "=") if (value === null) filterOp = { isNull: true };
3131
- else if (Array.isArray(value)) filterOp = { inArray: value };
3132
- else if (typeof value === "string" && value.includes("*")) {
3133
- const startsWithAsterisk = value.startsWith("*");
3134
- const endsWithAsterisk = value.endsWith("*");
3135
- const cleanValue = value.replace(/^\*|\*$/g, "");
3136
- if (startsWithAsterisk && endsWithAsterisk) filterOp = { contains: cleanValue };
3137
- else if (startsWithAsterisk) filterOp = { endsWith: cleanValue };
3138
- else if (endsWithAsterisk) filterOp = { startsWith: cleanValue };
3139
- else filterOp = { eq: value };
3140
- } else filterOp = { eq: value };
3141
- else if (operator === "!=") if (value === null) filterOp = { isNotNull: true };
3142
- else filterOp = { ne: value };
3143
- else if (operator === ">") filterOp = { gt: value };
3144
- else if (operator === ">=") filterOp = { gte: value };
3145
- else if (operator === "<") filterOp = { lt: value };
3146
- else if (operator === "<=") filterOp = { lte: value };
3147
- else throw new AlephaError(`Unsupported operator: ${operator}`);
3148
- if (path.length === 1) return { [path[0]]: filterOp };
3149
- let result = filterOp;
3150
- for (let i = path.length - 1; i >= 0; i--) result = { [path[i]]: result };
3151
- return result;
3152
- }
3153
- peek() {
3154
- this.skipWhitespace();
3155
- return this.query[this.pos] || "";
3156
- }
3157
- consume(expected) {
3158
- this.skipWhitespace();
3159
- if (this.query[this.pos] !== expected) throw new AlephaError(`Expected '${expected}' at position ${this.pos}, got '${this.query[this.pos]}'`);
3160
- this.pos++;
3161
- }
3162
- skipWhitespace() {
3163
- while (this.pos < this.query.length && /\s/.test(this.query[this.pos])) this.pos++;
3164
- }
3165
- };
3166
- /**
3167
- * Helper function to build query strings programmatically
3168
- *
3169
- * @example
3170
- * ```ts
3171
- * buildQueryString({
3172
- * and: [
3173
- * { name: "eq:John" },
3174
- * { age: "gt:18" }
3175
- * ]
3176
- * })
3177
- * // => "name=John&age>18"
3178
- * ```
3179
- */
3180
- function buildQueryString(where) {
3181
- if (!where || typeof where !== "object") return "";
3182
- if ("and" in where && Array.isArray(where.and)) return where.and.map((w) => buildQueryString(w)).join("&");
3183
- if ("or" in where && Array.isArray(where.or)) {
3184
- const parts = where.or.map((w) => buildQueryString(w));
3185
- return parts.length > 1 ? `(${parts.join("|")})` : parts[0];
3186
- }
3187
- if ("not" in where) return "";
3188
- const parts = [];
3189
- for (const [field, condition] of Object.entries(where)) {
3190
- if (typeof condition !== "object" || condition === null) {
3191
- parts.push(`${field}=${condition}`);
3192
- continue;
3193
- }
3194
- if ("eq" in condition) parts.push(`${field}=${condition.eq}`);
3195
- else if ("ne" in condition) parts.push(`${field}!=${condition.ne}`);
3196
- else if ("gt" in condition) parts.push(`${field}>${condition.gt}`);
3197
- else if ("gte" in condition) parts.push(`${field}>=${condition.gte}`);
3198
- else if ("lt" in condition) parts.push(`${field}<${condition.lt}`);
3199
- else if ("lte" in condition) parts.push(`${field}<=${condition.lte}`);
3200
- else if ("contains" in condition) parts.push(`${field}=*${condition.contains}*`);
3201
- else if ("startsWith" in condition) parts.push(`${field}=${condition.startsWith}*`);
3202
- else if ("endsWith" in condition) parts.push(`${field}=*${condition.endsWith}`);
3203
- else if ("isNull" in condition && condition.isNull) parts.push(`${field}=null`);
3204
- else if ("isNotNull" in condition && condition.isNotNull) parts.push(`${field}!=null`);
3205
- else if ("inArray" in condition && Array.isArray(condition.inArray)) {
3206
- const values = condition.inArray.map((v) => typeof v === "string" ? `"${v}"` : v);
3207
- parts.push(`${field}=[${values.join(",")}]`);
3208
- } else {
3209
- const nested = buildQueryString(condition);
3210
- if (nested) parts.push(`${field}.${nested}`);
3211
- }
3212
- }
3213
- return parts.join("&");
3214
- }
3215
- //#endregion
3216
2824
  //#region ../../src/orm/core/providers/DatabaseTypeProvider.ts
3217
2825
  var DatabaseTypeProvider = class {
3218
2826
  attr = pgAttr;
@@ -3321,7 +2929,7 @@ const legacyIdSchema = pgAttr(pgAttr(pgAttr(t.integer(), PG_PRIMARY_KEY), PG_SER
3321
2929
  //#endregion
3322
2930
  //#region ../../src/orm/core/primitives/$repository.ts
3323
2931
  /**
3324
- * Get the repository for the given entity or view.
2932
+ * Get the repository for the given entity.
3325
2933
  */
3326
2934
  const $repository = (entity) => {
3327
2935
  const { alepha } = $context();
@@ -3423,11 +3031,7 @@ const schema = (name, document) => customType({
3423
3031
  const SqliteProvider = NodeSqliteProvider;
3424
3032
  const AlephaOrm = $module({
3425
3033
  name: "alepha.orm",
3426
- primitives: [
3427
- $sequence,
3428
- $entity,
3429
- $view
3430
- ],
3034
+ primitives: [$sequence, $entity],
3431
3035
  imports: [AlephaDateTime],
3432
3036
  services: [
3433
3037
  SqliteModelBuilder,
@@ -3460,6 +3064,6 @@ const AlephaOrm = $module({
3460
3064
  }
3461
3065
  });
3462
3066
  //#endregion
3463
- export { $entity, $repository, $seed, $sequence, $transactional, $view, AlephaOrm, BunSqliteProvider, CloudflareD1Provider, DatabaseProvider, DatabaseTypeProvider, DbCacheProvider, DbColumnNotFoundError, DbConflictError, DbConnectionError, DbDeadlockError, DbEntityNotFoundError, DbError, DbForeignKeyError, DbMigrationError, DbMigrationMode, DbNotNullError, DbTableNotFoundError, DbVersionMismatchError, DrizzleKitProvider, EntityPrimitive, ModelBuilder, NodeSqliteProvider, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_ENUM, PG_GENERATED, PG_IDENTITY, PG_ORGANIZATION, PG_PRIMARY_KEY, PG_REF, PG_SERIAL, PG_UPDATED_AT, PG_VERSION, Repository, RepositoryProvider, SequencePrimitive, SqliteProvider, ViewPrimitive, buildQueryString, bunSqliteOptions, databaseEnvSchema, db, drizzle, getAttrFields, insertSchema, legacyIdSchema, nodeSqliteOptions, pageQuerySchema, pageSchema, parseQueryString, pgAttr, schema, sql, updateSchema };
3067
+ export { $entity, $repository, $seed, $sequence, $transactional, AlephaOrm, BunSqliteProvider, CloudflareD1Provider, DatabaseProvider, DatabaseTypeProvider, DbCacheProvider, DbColumnNotFoundError, DbConflictError, DbConnectionError, DbDeadlockError, DbEntityNotFoundError, DbError, DbForeignKeyError, DbMigrationError, DbMigrationMode, DbNotNullError, DbTableNotFoundError, DbVersionMismatchError, DrizzleKitProvider, EntityPrimitive, ModelBuilder, NodeSqliteProvider, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_ENUM, PG_GENERATED, PG_IDENTITY, PG_ORGANIZATION, PG_PRIMARY_KEY, PG_REF, PG_SERIAL, PG_UPDATED_AT, PG_VERSION, Repository, RepositoryProvider, SequencePrimitive, SqliteProvider, bunSqliteOptions, databaseEnvSchema, db, drizzle, getAttrFields, insertSchema, legacyIdSchema, nodeSqliteOptions, pageQuerySchema, pageSchema, pgAttr, schema, sql, updateSchema };
3464
3068
 
3465
3069
  //# sourceMappingURL=index.js.map