appos 0.2.1 → 0.2.3-0

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 (232) hide show
  1. package/dist/bin/auth-schema-7KeUwlcd.mjs +2 -0
  2. package/dist/bin/concurrently.mjs +2 -0
  3. package/dist/bin/event-v2sCJkNd.mjs +2 -0
  4. package/dist/bin/extract-blob-metadata-TqNd9w-6.mjs +2 -0
  5. package/dist/bin/generate-image-variant-D8H9FxgD.mjs +2 -0
  6. package/dist/bin/generate-preview-5jLZLX6I.mjs +2 -0
  7. package/dist/bin/main.mjs +362 -0
  8. package/dist/bin/purge-attachment-CMlJMNOk.mjs +2 -0
  9. package/dist/bin/purge-audit-logs-hd6q6vnR.mjs +2 -0
  10. package/dist/bin/purge-unattached-blobs-BYv5b9R9.mjs +2 -0
  11. package/dist/bin/track-db-changes-q0Vl7Htm.mjs +2 -0
  12. package/dist/bin/vite.mjs +2 -0
  13. package/dist/bin/vitest.mjs +2 -0
  14. package/dist/bin/workflow-BagSlsMp.mjs +2 -0
  15. package/dist/bin/youch-handler-Jj6i1XIT.mjs +2 -0
  16. package/dist/exports/api/_virtual/rolldown_runtime.mjs +1 -0
  17. package/dist/exports/api/app-context.d.mts +115 -0
  18. package/dist/exports/api/app-context.mjs +1 -0
  19. package/dist/exports/api/auth-schema.d.mts +4248 -0
  20. package/dist/exports/api/auth-schema.mjs +1 -0
  21. package/dist/exports/api/auth.d.mts +398 -0
  22. package/dist/exports/api/auth.mjs +1 -0
  23. package/dist/exports/api/cache.d.mts +44 -0
  24. package/dist/exports/api/cache.mjs +1 -0
  25. package/dist/exports/api/config.d.mts +28 -0
  26. package/dist/exports/api/config.mjs +1 -0
  27. package/dist/exports/api/container.d.mts +210 -0
  28. package/dist/exports/api/container.mjs +1 -0
  29. package/dist/exports/api/database.d.mts +99 -0
  30. package/dist/exports/api/database.mjs +1 -0
  31. package/dist/exports/api/event.d.mts +235 -0
  32. package/dist/exports/api/event.mjs +1 -0
  33. package/dist/exports/api/i18n.d.mts +34 -0
  34. package/dist/exports/api/i18n.mjs +1 -0
  35. package/dist/exports/api/index.d.mts +21 -0
  36. package/dist/exports/api/index.mjs +1 -0
  37. package/dist/exports/api/logger.d.mts +21 -0
  38. package/dist/exports/api/logger.mjs +1 -0
  39. package/dist/exports/api/mailer.d.mts +70 -0
  40. package/dist/exports/api/mailer.mjs +1 -0
  41. package/dist/exports/api/middleware/request-logger.d.mts +24 -0
  42. package/dist/exports/api/middleware.d.mts +39 -0
  43. package/dist/exports/api/middleware.mjs +1 -0
  44. package/dist/exports/api/node_modules/.bun/change-case@5.4.4/node_modules/change-case/dist/index.mjs +1 -0
  45. package/dist/exports/api/openapi.d.mts +271 -0
  46. package/dist/exports/api/openapi.mjs +1 -0
  47. package/dist/exports/api/orm.d.mts +13 -0
  48. package/dist/exports/api/orm.mjs +1 -0
  49. package/dist/exports/api/otel.d.mts +40 -0
  50. package/dist/exports/api/otel.mjs +1 -0
  51. package/dist/exports/api/packages/appos/src/constants.mjs +1 -0
  52. package/dist/exports/api/packages/appos/src/instrumentation.d.mts +7 -0
  53. package/dist/exports/api/packages/appos/src/instrumentation.mjs +1 -0
  54. package/dist/exports/api/packages/appos/src/web/auth.mjs +1 -0
  55. package/dist/exports/api/redis.d.mts +34 -0
  56. package/dist/exports/api/redis.mjs +1 -0
  57. package/dist/exports/api/storage-schema.d.mts +707 -0
  58. package/dist/exports/api/storage-schema.mjs +1 -0
  59. package/dist/exports/api/storage.d.mts +506 -0
  60. package/dist/exports/api/storage.mjs +1 -0
  61. package/dist/exports/api/workflow.d.mts +250 -0
  62. package/dist/exports/api/workflow.mjs +1 -0
  63. package/dist/exports/api/workflows/_virtual/rolldown_runtime.mjs +1 -0
  64. package/dist/exports/api/workflows/auth-schema.mjs +1 -0
  65. package/dist/exports/api/workflows/auth.d.mts +375 -0
  66. package/dist/exports/api/workflows/cache.d.mts +44 -0
  67. package/dist/exports/api/workflows/config.d.mts +18 -0
  68. package/dist/exports/api/workflows/container.d.mts +167 -0
  69. package/dist/exports/api/workflows/database.d.mts +46 -0
  70. package/dist/exports/api/workflows/event.d.mts +68 -0
  71. package/dist/exports/api/workflows/event.mjs +1 -0
  72. package/dist/exports/api/workflows/extract-blob-metadata.mjs +1 -0
  73. package/dist/exports/api/workflows/generate-image-variant.d.mts +63 -0
  74. package/dist/exports/api/workflows/generate-image-variant.mjs +1 -0
  75. package/dist/exports/api/workflows/generate-preview.mjs +1 -0
  76. package/dist/exports/api/workflows/index.d.mts +2 -0
  77. package/dist/exports/api/workflows/index.mjs +1 -0
  78. package/dist/exports/api/workflows/logger.d.mts +21 -0
  79. package/dist/exports/api/workflows/mailer.d.mts +70 -0
  80. package/dist/exports/api/workflows/orm.d.mts +13 -0
  81. package/dist/exports/api/workflows/purge-attachment.mjs +1 -0
  82. package/dist/exports/api/workflows/purge-audit-logs.mjs +1 -0
  83. package/dist/exports/api/workflows/purge-unattached-blobs.mjs +1 -0
  84. package/dist/exports/api/workflows/redis.mjs +1 -0
  85. package/dist/exports/api/workflows/storage-schema.d.mts +699 -0
  86. package/dist/exports/api/workflows/storage.d.mts +396 -0
  87. package/dist/exports/api/workflows/track-db-changes.d.mts +72 -0
  88. package/dist/exports/api/workflows/track-db-changes.mjs +1 -0
  89. package/dist/exports/api/workflows/workflow.d.mts +24 -0
  90. package/dist/exports/api/workflows/workflow.mjs +1 -0
  91. package/dist/exports/cli/_virtual/rolldown_runtime.mjs +1 -0
  92. package/dist/exports/cli/api/auth-schema.mjs +1 -0
  93. package/dist/exports/cli/api/auth.d.mts +375 -0
  94. package/dist/exports/cli/api/cache.d.mts +44 -0
  95. package/dist/exports/cli/api/config.d.mts +18 -0
  96. package/dist/exports/cli/api/container.d.mts +167 -0
  97. package/dist/exports/cli/api/database.d.mts +46 -0
  98. package/dist/exports/cli/api/event.d.mts +68 -0
  99. package/dist/exports/cli/api/event.mjs +1 -0
  100. package/dist/exports/cli/api/logger.d.mts +21 -0
  101. package/dist/exports/cli/api/mailer.d.mts +70 -0
  102. package/dist/exports/cli/api/orm.d.mts +13 -0
  103. package/dist/exports/cli/api/redis.mjs +1 -0
  104. package/dist/exports/cli/api/storage-schema.d.mts +699 -0
  105. package/dist/exports/cli/api/storage.d.mts +396 -0
  106. package/dist/exports/cli/api/workflow.d.mts +2 -0
  107. package/dist/exports/cli/api/workflow.mjs +1 -0
  108. package/dist/exports/cli/api/workflows/extract-blob-metadata.mjs +1 -0
  109. package/dist/exports/cli/api/workflows/generate-image-variant.d.mts +63 -0
  110. package/dist/exports/cli/api/workflows/generate-image-variant.mjs +1 -0
  111. package/dist/exports/cli/api/workflows/generate-preview.mjs +1 -0
  112. package/dist/exports/cli/api/workflows/purge-attachment.mjs +1 -0
  113. package/dist/exports/cli/api/workflows/purge-audit-logs.mjs +1 -0
  114. package/dist/exports/cli/api/workflows/purge-unattached-blobs.mjs +1 -0
  115. package/dist/exports/cli/api/workflows/track-db-changes.mjs +1 -0
  116. package/dist/exports/cli/command.d.mts +54 -0
  117. package/dist/exports/cli/command.mjs +1 -0
  118. package/dist/exports/cli/context.d.mts +170 -0
  119. package/dist/exports/cli/index.d.mts +3 -0
  120. package/dist/exports/cli/index.mjs +1 -0
  121. package/dist/exports/devtools/index.d.ts +3 -0
  122. package/dist/exports/devtools/index.js +1 -0
  123. package/dist/exports/instrumentation.d.mts +1 -0
  124. package/dist/exports/instrumentation.mjs +1 -0
  125. package/dist/exports/tests/_virtual/rolldown_runtime.mjs +1 -0
  126. package/dist/exports/tests/api.d.mts +86 -0
  127. package/dist/exports/tests/api.mjs +1 -0
  128. package/dist/exports/tests/mock.d.mts +1 -0
  129. package/dist/exports/tests/mock.mjs +1 -0
  130. package/dist/exports/tests/node_modules/.bun/change-case@5.4.4/node_modules/change-case/dist/index.mjs +1 -0
  131. package/dist/exports/tests/node_modules/.bun/rate-limit-redis@4.3.1_f1fa5524233c9c60/node_modules/rate-limit-redis/dist/index.mjs +25 -0
  132. package/dist/exports/tests/packages/appos/src/api/app-context.d.mts +115 -0
  133. package/dist/exports/tests/packages/appos/src/api/auth-schema.d.mts +4248 -0
  134. package/dist/exports/tests/packages/appos/src/api/auth-schema.mjs +1 -0
  135. package/dist/exports/tests/packages/appos/src/api/auth.d.mts +398 -0
  136. package/dist/exports/tests/packages/appos/src/api/cache.d.mts +44 -0
  137. package/dist/exports/tests/packages/appos/src/api/config.d.mts +28 -0
  138. package/dist/exports/tests/packages/appos/src/api/container.d.mts +210 -0
  139. package/dist/exports/tests/packages/appos/src/api/database.d.mts +99 -0
  140. package/dist/exports/tests/packages/appos/src/api/database.mjs +1 -0
  141. package/dist/exports/tests/packages/appos/src/api/event.d.mts +235 -0
  142. package/dist/exports/tests/packages/appos/src/api/event.mjs +1 -0
  143. package/dist/exports/tests/packages/appos/src/api/i18n.d.mts +34 -0
  144. package/dist/exports/tests/packages/appos/src/api/index.d.mts +27 -0
  145. package/dist/exports/tests/packages/appos/src/api/logger.d.mts +21 -0
  146. package/dist/exports/tests/packages/appos/src/api/mailer.d.mts +70 -0
  147. package/dist/exports/tests/packages/appos/src/api/middleware/error-handler.mjs +1 -0
  148. package/dist/exports/tests/packages/appos/src/api/middleware/health.mjs +1 -0
  149. package/dist/exports/tests/packages/appos/src/api/middleware/i18n.mjs +1 -0
  150. package/dist/exports/tests/packages/appos/src/api/middleware/request-logger.d.mts +24 -0
  151. package/dist/exports/tests/packages/appos/src/api/middleware/request-logger.mjs +1 -0
  152. package/dist/exports/tests/packages/appos/src/api/middleware/shutdown.mjs +1 -0
  153. package/dist/exports/tests/packages/appos/src/api/middleware/timeout.mjs +1 -0
  154. package/dist/exports/tests/packages/appos/src/api/middleware/youch-handler.mjs +1 -0
  155. package/dist/exports/tests/packages/appos/src/api/middleware.d.mts +39 -0
  156. package/dist/exports/tests/packages/appos/src/api/middleware.mjs +1 -0
  157. package/dist/exports/tests/packages/appos/src/api/openapi.d.mts +271 -0
  158. package/dist/exports/tests/packages/appos/src/api/orm.d.mts +13 -0
  159. package/dist/exports/tests/packages/appos/src/api/otel.d.mts +40 -0
  160. package/dist/exports/tests/packages/appos/src/api/redis.d.mts +34 -0
  161. package/dist/exports/tests/packages/appos/src/api/redis.mjs +1 -0
  162. package/dist/exports/tests/packages/appos/src/api/server.mjs +1 -0
  163. package/dist/exports/tests/packages/appos/src/api/storage-schema.d.mts +707 -0
  164. package/dist/exports/tests/packages/appos/src/api/storage.d.mts +506 -0
  165. package/dist/exports/tests/packages/appos/src/api/workflow.d.mts +250 -0
  166. package/dist/exports/tests/packages/appos/src/api/workflow.mjs +1 -0
  167. package/dist/exports/tests/packages/appos/src/api/workflows/extract-blob-metadata.mjs +1 -0
  168. package/dist/exports/tests/packages/appos/src/api/workflows/generate-image-variant.d.mts +99 -0
  169. package/dist/exports/tests/packages/appos/src/api/workflows/generate-image-variant.mjs +1 -0
  170. package/dist/exports/tests/packages/appos/src/api/workflows/generate-preview.mjs +1 -0
  171. package/dist/exports/tests/packages/appos/src/api/workflows/purge-attachment.mjs +1 -0
  172. package/dist/exports/tests/packages/appos/src/api/workflows/purge-audit-logs.mjs +1 -0
  173. package/dist/exports/tests/packages/appos/src/api/workflows/purge-unattached-blobs.mjs +1 -0
  174. package/dist/exports/tests/packages/appos/src/api/workflows/track-db-changes.mjs +1 -0
  175. package/dist/exports/tests/packages/appos/src/constants.mjs +1 -0
  176. package/dist/exports/tests/packages/appos/src/instrumentation.d.mts +7 -0
  177. package/dist/exports/tests/packages/appos/src/instrumentation.mjs +1 -0
  178. package/dist/exports/tests/react.d.mts +2 -0
  179. package/dist/exports/tests/react.mjs +1 -0
  180. package/dist/exports/tests/setup.d.mts +1 -0
  181. package/dist/exports/tests/setup.mjs +1 -0
  182. package/dist/exports/vendors/date.js +1 -0
  183. package/dist/exports/vendors/toolkit.js +1 -0
  184. package/dist/exports/vendors/zod.d.ts +1 -0
  185. package/dist/exports/vendors/zod.js +1 -0
  186. package/dist/exports/vite/index.d.mts +19 -0
  187. package/dist/exports/vite/index.mjs +1 -0
  188. package/dist/exports/vitest/config.d.mts +1 -0
  189. package/dist/exports/vitest/config.mjs +1 -0
  190. package/dist/exports/vitest/globals.d.mts +1 -0
  191. package/dist/exports/vitest/globals.mjs +1 -0
  192. package/dist/exports/vitest/index.d.mts +1 -0
  193. package/dist/exports/vitest/index.mjs +1 -0
  194. package/dist/exports/web/api/auth.d.ts +125 -0
  195. package/dist/exports/web/api/database.d.ts +4 -0
  196. package/dist/exports/web/api/logger.d.ts +1 -0
  197. package/dist/exports/web/auth.d.ts +2388 -0
  198. package/dist/exports/web/auth.js +1 -0
  199. package/dist/exports/web/i18n.d.ts +42 -0
  200. package/dist/exports/web/i18n.js +1 -0
  201. package/dist/exports/web/index.d.ts +6 -0
  202. package/dist/exports/web/index.js +1 -0
  203. package/package.json +138 -98
  204. package/build/bin/main.mjs +0 -2
  205. package/build/exports/cli/index.d.mts +0 -325
  206. package/build/exports/cli/index.mjs +0 -1
  207. package/build/exports/instrumentation/execAsync-DaIUcs6_.mjs +0 -1
  208. package/build/exports/instrumentation/getMachineId-bsd-bB6ipDhm.mjs +0 -1
  209. package/build/exports/instrumentation/getMachineId-darwin-D1Bx5aCe.mjs +0 -2
  210. package/build/exports/instrumentation/getMachineId-linux-D_R9Tla0.mjs +0 -1
  211. package/build/exports/instrumentation/getMachineId-unsupported-BZKPE_Ev.mjs +0 -1
  212. package/build/exports/instrumentation/getMachineId-win-CmPvIqHL.mjs +0 -1
  213. package/build/exports/instrumentation/instrumentation.d.mts +0 -1
  214. package/build/exports/instrumentation/instrumentation.mjs +0 -80
  215. package/build/exports/server/index.d.mts +0 -327
  216. package/build/exports/server/index.mjs +0 -219
  217. package/build/exports/server/react-gPO8Jsy-.mjs +0 -13
  218. package/build/exports/server/server.node-D_9RYjm9.mjs +0 -210
  219. package/build/exports/store/index.d.mts +0 -58
  220. package/build/exports/store/index.mjs +0 -15
  221. package/build/exports/support/datetime.js +0 -1
  222. package/build/exports/support/utils.js +0 -1
  223. package/build/exports/support/zod.d.ts +0 -2
  224. package/build/exports/support/zod.js +0 -23
  225. package/build/exports/test/dist-DAsoCGWk.mjs +0 -348
  226. package/build/exports/test/index.d.mts +0 -3
  227. package/build/exports/test/index.mjs +0 -1
  228. package/build/exports/test/magic-string.es-BWgiB2kd.mjs +0 -14
  229. package/build/exports/test/setup.d.mts +0 -1
  230. package/build/exports/test/setup.mjs +0 -329
  231. /package/{build/exports/support/datetime.d.ts → dist/exports/vendors/date.d.ts} +0 -0
  232. /package/{build/exports/support/utils.d.ts → dist/exports/vendors/toolkit.d.ts} +0 -0
@@ -0,0 +1,99 @@
1
+ import { Logger } from "./logger.mjs";
2
+ import * as drizzle_orm0 from "drizzle-orm";
3
+ import { AnyRelations, EmptyRelations } from "drizzle-orm";
4
+ import * as drizzle_orm_node_postgres0 from "drizzle-orm/node-postgres";
5
+ import { Pool, PoolConfig } from "pg";
6
+ import { PgTable } from "drizzle-orm/pg-core";
7
+ import { MigrationConfig } from "drizzle-orm/migrator";
8
+
9
+ //#region src/api/database.d.ts
10
+ /**
11
+ * Extract qualified table names from db object.
12
+ */
13
+ type QualifiedTableNames<TDb> = { [K in keyof TDb]: TDb[K] extends {
14
+ _: {
15
+ fullSchema: infer S;
16
+ };
17
+ } ? `${K & string}.${keyof S & string}` : never }[keyof TDb];
18
+ /**
19
+ * The schema used for migrations.
20
+ */
21
+ declare const migrationsSchema = "public";
22
+ /**
23
+ * The type of migration (schema or data).
24
+ */
25
+ type MigrationType = "schema" | "data";
26
+ /**
27
+ * Get the migration options for a specific database.
28
+ *
29
+ * @param name Name of the database to get migration options for
30
+ * @param type Type of migration (schema or data), defaults to "schema"
31
+ * @returns Migration configuration for drizzle-orm
32
+ */
33
+ declare function defineMigrationOpts(name: string, type?: MigrationType): MigrationConfig;
34
+ /**
35
+ * Options for defining the database.
36
+ */
37
+ interface DefineDatabaseOptions<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations> {
38
+ logger: Logger;
39
+ poolConfig: PoolConfig;
40
+ relations?: TRelations;
41
+ schema?: TSchema;
42
+ }
43
+ /**
44
+ * The database type.
45
+ */
46
+ type Database = Awaited<ReturnType<typeof defineDatabase>>;
47
+ /**
48
+ * Generate old and new row JSONB representations for delete/insert/update queries.
49
+ * Uses PostgreSQL 18's OLD/NEW support in RETURNING clause.
50
+ *
51
+ * @param table The table to generate changes for.
52
+ * @returns Object containing `_table`, `old` and `new` JSONB columns.
53
+ */
54
+ declare function dbChanges<T extends PgTable>(table: T): {
55
+ _table: drizzle_orm0.SQL.Aliased<string>;
56
+ old: drizzle_orm0.SQL.Aliased<({ [Key in keyof T["_"]["columns"] & string]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null } extends infer T_1 ? { [K in keyof T_1]: T_1[K] } : never) | null>;
57
+ new: drizzle_orm0.SQL.Aliased<({ [Key in keyof T["_"]["columns"] & string]: T["_"]["columns"][Key]["_"]["notNull"] extends true ? T["_"]["columns"][Key]["_"]["data"] : T["_"]["columns"][Key]["_"]["data"] | null } extends infer T_2 ? { [K in keyof T_2]: T_2[K] } : never) | null>;
58
+ };
59
+ /**
60
+ * Define the database with the provided options.
61
+ *
62
+ * Algorithm:
63
+ * 1. Create a connection pool with sensible defaults
64
+ * 2. Initialize drizzle ORM with schema and relations
65
+ *
66
+ * @param opts The options for defining the database, including pool configuration, relations, and schema.
67
+ * @template TSchema The schema type for the database.
68
+ * @template TRelations The relations type for the database.
69
+ * @returns The defined database instance.
70
+ */
71
+ declare function defineDatabase<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations>(opts: DefineDatabaseOptions<TSchema, TRelations>): Promise<drizzle_orm_node_postgres0.NodePgDatabase<TSchema, TRelations> & {
72
+ $client: Pool;
73
+ }>;
74
+ /**
75
+ * Options for defining a test database.
76
+ */
77
+ interface DefineTestDatabaseOptions<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations> {
78
+ connectionString: string;
79
+ logger: Logger;
80
+ name: string;
81
+ schema: TSchema;
82
+ relations: TRelations;
83
+ }
84
+ /**
85
+ * Create a single test database with isolated schema.
86
+ *
87
+ * @param connectionString - Database connection string
88
+ * @param name - Name of the test database
89
+ * @returns Test database instance and cleanup function
90
+ */
91
+ declare function defineTestDatabase<TSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations>(opts: DefineTestDatabaseOptions<TSchema, TRelations>): Promise<{
92
+ cleanUp: () => Promise<void>;
93
+ db: drizzle_orm_node_postgres0.NodePgDatabase<TSchema, TRelations> & {
94
+ $client: Pool;
95
+ };
96
+ dbName: string;
97
+ }>;
98
+ //#endregion
99
+ export { Database, DefineDatabaseOptions, DefineTestDatabaseOptions, MigrationType, QualifiedTableNames, dbChanges, defineDatabase, defineMigrationOpts, defineTestDatabase, migrationsSchema };
@@ -0,0 +1 @@
1
+ import{APPOS_DIR as e,DATABASES_DIR as t}from"../constants.mjs";import{join as n}from"node:path";import{sql as r}from"drizzle-orm";import{drizzle as i}from"drizzle-orm/node-postgres";import{migrate as a}from"drizzle-orm/node-postgres/migrator";import{Client as o,Pool as s}from"pg";function c(r,i=`schema`){let a=i===`schema`?`schema-migrations`:`data-migrations`,o=i===`schema`?`schema-migrations`:`data-migrations`;return{migrationsFolder:`${n(e,t)}/${r}/${a}`,migrationsSchema:`public`,migrationsTable:o}}var l=class{#e;constructor(e){this.#e=e}logQuery(e,t){this.#e.info(t,e)}};async function u(e){let t=`test_w${process.env.VITEST_POOL_ID||`0`}_${Date.now()}_${Math.random().toString(36).substring(2,8)}`;await h(e.connectionString,e.name,e.logger);let n=`${e.name}_test_template`,r=p(e.connectionString),a=new o({connectionString:r});await a.connect();try{await a.query(`CREATE DATABASE ${t} WITH TEMPLATE ${n}`)}finally{await a.end()}let c=d(e.connectionString);c.database=t;let u=new s({connectionString:f(c)});return{cleanUp:async()=>{await u.end();let e=new o({connectionString:r});await e.connect();try{await e.query(`SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = $1`,[t]),await e.query(`DROP DATABASE IF EXISTS ${t}`)}catch(e){console.error(`Could not drop test database:`,e)}finally{await e.end()}},db:i({client:u,logger:e.logger?new l(e.logger):void 0,relations:e.relations,schema:e.schema}),dbName:t}}function d(e){let t=new URL(e);return{database:t.pathname.slice(1)||`postgres`,host:t.hostname,password:decodeURIComponent(t.password),port:Number.parseInt(t.port||`5432`,10),user:decodeURIComponent(t.username)}}function f(e){let{user:t,password:n,host:r,port:i,database:a}=e;return`postgresql://${encodeURIComponent(t)}:${encodeURIComponent(n)}@${r}:${i}/${a}`}function p(e){let t=d(e);return t.database=`postgres`,f(t)}function m(e){let t=0;for(let n=0;n<e.length;n++){let r=e.charCodeAt(n);t=(t<<5)-t+r,t&=t}return Math.abs(t)}async function h(e,t,n){let r=`${t}_test_template`,u=new o({connectionString:p(e)});await u.connect();let h=m(r);try{if(await u.query(`SELECT pg_advisory_lock($1)`,[h]),(await u.query(`SELECT 1 FROM pg_database WHERE datname = $1`,[r])).rowCount===0){await u.query(`CREATE DATABASE ${r} WITH IS_TEMPLATE = true`);let o=d(e);o.database=r;let p=new s({connectionString:f(o)});try{await a(i({client:p,logger:n?new l(n):void 0}),c(t))}finally{await p.end()}}}finally{await u.query(`SELECT pg_advisory_unlock($1)`,[h]),await u.end()}}export{u as defineTestDatabase};
@@ -0,0 +1,235 @@
1
+ import { Logger } from "./logger.mjs";
2
+ import { Container } from "./container.mjs";
3
+ import { z } from "zod";
4
+
5
+ //#region src/api/event.d.ts
6
+
7
+ /**
8
+ * Context available to event handlers.
9
+ */
10
+ interface EventContext<TInput> {
11
+ /**
12
+ * The application container with access to db, mailer, logger, etc.
13
+ */
14
+ container: Container;
15
+ /**
16
+ * The validated input passed to this event.
17
+ */
18
+ input: TInput;
19
+ }
20
+ /**
21
+ * Options for defining an event bus.
22
+ */
23
+ interface DefineEventBusOptions {
24
+ /**
25
+ * Redis URL for pub/sub.
26
+ */
27
+ dbUrl: string;
28
+ /**
29
+ * Logger instance for error reporting.
30
+ */
31
+ logger: Logger;
32
+ }
33
+ /**
34
+ * The event bus type for container.
35
+ */
36
+ type EventBus = ReturnType<typeof defineEventBus>;
37
+ /**
38
+ * Defines the event bus for pub/sub messaging.
39
+ * Uses Redis for cross-server communication.
40
+ *
41
+ * Algorithm:
42
+ * 1. Create lazy Redis publisher client
43
+ * 2. Create lazy Redis subscriber client
44
+ * 3. Manage subscriptions via callback registry
45
+ * 4. Provide publish/subscribe/close methods
46
+ *
47
+ * @param opts - Event bus configuration
48
+ * @returns Event bus instance
49
+ */
50
+ declare function defineEventBus(opts: DefineEventBusOptions): {
51
+ /**
52
+ * Publish a message to a channel.
53
+ * Auto-connects on first call if not already connected.
54
+ *
55
+ * @param channel - Channel name
56
+ * @param message - Message payload (will be JSON stringified)
57
+ */
58
+ publish(channel: string, message: unknown): Promise<void>;
59
+ /**
60
+ * Subscribe to a channel. Returns unsubscribe function.
61
+ * Uses single connection with callback management.
62
+ *
63
+ * @param channel - Channel name
64
+ * @param callback - Callback for received messages
65
+ * @returns Unsubscribe function
66
+ */
67
+ subscribe(channel: string, callback: (message: unknown) => void): Promise<() => void>;
68
+ /**
69
+ * Check if a channel has any subscribers.
70
+ *
71
+ * @param channel - Channel name
72
+ * @returns true if channel has subscribers
73
+ */
74
+ hasSubscribers(channel: string): boolean;
75
+ /**
76
+ * Close the Redis connections.
77
+ */
78
+ close(): Promise<void>;
79
+ };
80
+ /**
81
+ * Options for defining an event.
82
+ */
83
+ interface DefineEventOptions<TInput extends z.ZodType> {
84
+ /**
85
+ * Zod schema for validating event input.
86
+ */
87
+ input: TInput;
88
+ /**
89
+ * In-memory handler that runs only on the emitting server.
90
+ *
91
+ * @param ctx - Event context with container and input
92
+ */
93
+ run: (ctx: EventContext<z.infer<TInput>>) => Promise<void>;
94
+ }
95
+ /**
96
+ * Event object returned by defineEvent.
97
+ */
98
+ interface Event<TInput> {
99
+ /**
100
+ * The event name (set during registration from filename).
101
+ */
102
+ readonly name: string | null;
103
+ /**
104
+ * Emit this event with the given input.
105
+ * Runs in-memory handler, then publishes to Redis (fire-and-forget).
106
+ *
107
+ * @param input - Input matching the event's input schema.
108
+ */
109
+ emit(input: TInput): Promise<void>;
110
+ /**
111
+ * The Zod schema for validating input.
112
+ */
113
+ inputSchema: z.ZodType<TInput>;
114
+ /**
115
+ * Register this event with the container. Called by the event loader.
116
+ *
117
+ * @param container - The application container
118
+ * @param name - The event name (derived from filename)
119
+ */
120
+ register(container: Container, name: string): void;
121
+ /**
122
+ * Subscribe to this event via Redis pub/sub.
123
+ * Use for tRPC subscriptions, SSE, WebSockets.
124
+ *
125
+ * @param handler - Handler called when event is emitted on any server
126
+ * @returns Unsubscribe function to cleanup the subscription
127
+ */
128
+ subscribe(handler: (ctx: EventContext<TInput>) => Promise<void>): Promise<() => void>;
129
+ }
130
+ /**
131
+ * Defines a type-safe event with in-memory and Redis pub/sub support.
132
+ *
133
+ * Algorithm:
134
+ * 1. Define event with input schema and in-memory run handler
135
+ * 2. On emit(): validate input, run in-memory handler, publish to Redis (fire-and-forget)
136
+ * 3. .subscribe() creates Redis subscription for tRPC/SSE/WebSocket handlers
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * // api/events/order-status.ts
141
+ * export default defineEvent({
142
+ * input: z.object({
143
+ * orderId: z.string(),
144
+ * status: z.enum(["pending", "shipped", "delivered"]),
145
+ * }),
146
+ * async run(ctx) {
147
+ * ctx.container.logger.info(`Order ${ctx.input.orderId} is ${ctx.input.status}`);
148
+ * },
149
+ * });
150
+ *
151
+ * // Emit from anywhere
152
+ * await orderStatus.emit({ orderId: "123", status: "shipped" });
153
+ *
154
+ * // Subscribe (e.g., in tRPC router)
155
+ * const unsubscribe = await orderStatus.subscribe(async (ctx) => {
156
+ * // Push to client via SSE/WebSocket
157
+ * });
158
+ * // Cleanup when client disconnects
159
+ * unsubscribe();
160
+ * ```
161
+ */
162
+ declare function defineEvent<TInput extends z.ZodType>(options: DefineEventOptions<TInput>): Event<z.infer<TInput>>;
163
+ /**
164
+ * Input schema for dbChangesEvent.
165
+ */
166
+ declare const dbChangeInputSchema: z.ZodObject<{
167
+ action: z.ZodEnum<{
168
+ DELETE: "DELETE";
169
+ INSERT: "INSERT";
170
+ UPDATE: "UPDATE";
171
+ }>;
172
+ newData: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
173
+ oldData: z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
174
+ organizationId: z.ZodNullable<z.ZodString>;
175
+ tableName: z.ZodString;
176
+ timestamp: z.ZodString;
177
+ userId: z.ZodNullable<z.ZodString>;
178
+ }, z.core.$strip>;
179
+ /**
180
+ * Type for dbChangesEvent input.
181
+ */
182
+ type DbChangeInput = z.infer<typeof dbChangeInputSchema>;
183
+ /**
184
+ * Built-in event for database changes.
185
+ * Emitted by trackDbChanges workflow.
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * // Subscribe to DB changes (e.g., in tRPC subscription)
190
+ * import { dbChangesEvent } from "appos/api";
191
+ *
192
+ * const unsubscribe = await dbChangesEvent.subscribe(async (ctx) => {
193
+ * wsServer.publish("db-changes", JSON.stringify(ctx.input));
194
+ * });
195
+ *
196
+ * // Cleanup when done
197
+ * unsubscribe();
198
+ * ```
199
+ */
200
+ declare const dbChangesEvent: Event<{
201
+ action: "DELETE" | "INSERT" | "UPDATE";
202
+ newData: Record<string, unknown> | null;
203
+ oldData: Record<string, unknown> | null;
204
+ organizationId: string | null;
205
+ tableName: string;
206
+ timestamp: string;
207
+ userId: string | null;
208
+ }>;
209
+ /**
210
+ * Options for loading events.
211
+ */
212
+ interface LoadEventsOptions {
213
+ /**
214
+ * The application container.
215
+ */
216
+ container: Container;
217
+ /**
218
+ * Optional custom events directory path.
219
+ */
220
+ eventsDir?: string;
221
+ }
222
+ /**
223
+ * Auto-discovers and registers all events from the given directory.
224
+ *
225
+ * Algorithm:
226
+ * 1. Register built-in dbChangesEvent
227
+ * 2. Glob all .ts files in the directory (excluding test files)
228
+ * 3. Import each module's default export
229
+ * 4. If it's an Event (has emit + subscribe), call register(container, name)
230
+ *
231
+ * @param opts - Load events options
232
+ */
233
+ declare function loadEvents(opts: LoadEventsOptions): Promise<void>;
234
+ //#endregion
235
+ export { DbChangeInput, DefineEventBusOptions, Event, EventBus, EventContext, dbChangeInputSchema, dbChangesEvent, defineEvent, defineEventBus, loadEvents };
@@ -0,0 +1 @@
1
+ import"./redis.mjs";import{glob as e}from"node:fs/promises";import{basename as t,join as n}from"node:path";import{z as r}from"zod";import"es-toolkit";function i(e){let t=null,n=null;return{inputSchema:e.input,get name(){return n},register(e,r){t=e,n=r},async emit(r){if(!t||!n)throw Error(`Event not registered. Ensure the worker is started before emitting events.`);let i=e.input.parse(r),a={container:t,input:i};await e.run(a),t.eventBus.publish(n,i).catch(e=>{t.logger.error({err:e,event:n},`Redis publish failed`)})},async subscribe(r){if(!t||!n)throw Error(`Event not registered. Ensure the worker is started before subscribing.`);return t.eventBus.subscribe(n,async i=>{let a=e.input.parse(i),o={container:t,input:a};try{await r(o)}catch(e){t.logger.error({err:e,event:n},`Event subscription handler error`)}})}}}const a=i({input:r.object({action:r.enum([`INSERT`,`UPDATE`,`DELETE`]),newData:r.record(r.string(),r.unknown()).nullable(),oldData:r.record(r.string(),r.unknown()).nullable(),organizationId:r.string().nullable(),tableName:r.string(),timestamp:r.string(),userId:r.string().nullable()}),async run(){}});export{a as dbChangesEvent};
@@ -0,0 +1,34 @@
1
+ import { InitOptions, i18n, i18n as i18n$1 } from "i18next";
2
+
3
+ //#region src/api/i18n.d.ts
4
+
5
+ /**
6
+ * i18n initialization options extending i18next's InitOptions.
7
+ * Used for both server and client initialization.
8
+ */
9
+ interface I18nInitOptions extends InitOptions {}
10
+ /**
11
+ * Type-safe default options for i18n.
12
+ * Apps should spread these and override as needed.
13
+ */
14
+ declare const defaultI18nOptions: {
15
+ readonly fallbackLng: "en";
16
+ readonly supportedLngs: readonly ["en"];
17
+ readonly defaultNS: "translation";
18
+ readonly ns: readonly ["translation"];
19
+ readonly interpolation: {
20
+ readonly escapeValue: false;
21
+ };
22
+ readonly react: {
23
+ readonly useSuspense: false;
24
+ };
25
+ };
26
+ /**
27
+ * Initializes i18next for server-side with filesystem backend.
28
+ *
29
+ * @param opts - Initialization options for i18next.
30
+ * @returns A promise that resolves to the initialized i18next instance.
31
+ */
32
+ declare function defineI18n(opts: I18nInitOptions): Promise<i18n>;
33
+ //#endregion
34
+ export { I18nInitOptions, defaultI18nOptions, defineI18n, type i18n$1 as i18n };
@@ -0,0 +1,27 @@
1
+ import { DefineLoggerOptions, Logger, defineLogger } from "./logger.mjs";
2
+ import { Database, DefineDatabaseOptions, DefineTestDatabaseOptions, MigrationType, QualifiedTableNames, dbChanges, defineDatabase, defineMigrationOpts, defineTestDatabase, migrationsSchema } from "./database.mjs";
3
+ import { AccessControlRoles, AccessController, AuditAction, Auth, AuthConfig, AuthPasskeyConfig, AuthSessionConfig, DefineAuthOptions, Role, auditActionSchema, createAccessControl, defineAuth } from "./auth.mjs";
4
+ import { Cache, DefineCacheOptions, defineCache } from "./cache.mjs";
5
+ import { Config, baseSchema, defineConfig } from "./config.mjs";
6
+ import { DbChangeInput, DefineEventBusOptions, Event, EventBus, EventContext, dbChangeInputSchema, dbChangesEvent, defineEvent, defineEventBus, loadEvents } from "./event.mjs";
7
+ import { DefineMailerOptions, Mailer, MailerPayload, MailerPayloadHtml, MailerPayloadReact, defineMailer } from "./mailer.mjs";
8
+ import { NewStorageAttachment, NewStorageBlob, NewStorageVariantRecord, StorageAttachment, StorageBlob, StorageRelations, StorageRelationsConfig, StorageTables, StorageVariantRecord, defineStorageSchema } from "./storage-schema.mjs";
9
+ import { ScheduledWorkflowContext, WorkflowContext, WorkflowHandle, defineScheduledWorkflow, defineWorkflow, loadWorkflows } from "./workflow.mjs";
10
+ import { ImageTransformations, ResizeOptions, resizeSchema, transformationsSchema } from "./workflows/generate-image-variant.mjs";
11
+ import { DatabaseWithStorage, DefineS3DiskOptions, DefineStorageOptions, Storage, StorageService, defineS3Disk, defineStorage } from "./storage.mjs";
12
+ import { AppContainer, Container, ServerConfig, WorkerConfig, defineAppContainer } from "./container.mjs";
13
+ import { AppContext, DefineAppContextOpts, SessionData, defineAppContext } from "./app-context.mjs";
14
+ import { defineAuthSchema } from "./auth-schema.mjs";
15
+ import { I18nInitOptions, defaultI18nOptions, defineI18n, i18n as i18n$1 } from "./i18n.mjs";
16
+ import { Middleware, defineMiddleware, loadMiddleware } from "./middleware.mjs";
17
+ import { DefineOpenAPIConfig, DefineOpenAPIConfigInput, DefineOpenAPIReturn, HandlerParams, OpenAPIMethodSpec, OpenAPIObjectConfigV31, OpenAPIRegistration, RouteModule, ValidationErrorResponse, appDir, defineOpenAPI, defineOpenAPIConfig, defineOpenAPIEndpoint, defineTypedResponses, generateOpenAPIDocument, loadAndRegisterAPIRoutes, registerRoutes, scanAPIRoutes, writeOpenAPISpecs } from "./openapi.mjs";
18
+ import { withOtelSpan } from "./otel.mjs";
19
+ import { DefineRedisClientOptions, RedisClient, defineRedisClient } from "./redis.mjs";
20
+ import { CustomTypeOptions } from "i18next";
21
+
22
+ //#region src/api/index.d.ts
23
+ declare namespace index_d_exports {
24
+ export { AccessControlRoles, AccessController, AppContainer, AppContext, AuditAction, Auth, AuthConfig, AuthPasskeyConfig, AuthSessionConfig, Cache, Config, Container, CustomTypeOptions, Database, DatabaseWithStorage, DbChangeInput, DefineAppContextOpts, DefineAuthOptions, DefineCacheOptions, DefineDatabaseOptions, DefineEventBusOptions, DefineLoggerOptions, DefineMailerOptions, DefineOpenAPIConfig, DefineOpenAPIConfigInput, DefineOpenAPIReturn, DefineRedisClientOptions, DefineS3DiskOptions, DefineStorageOptions, DefineTestDatabaseOptions, Event, EventBus, EventContext, HandlerParams, I18nInitOptions, ImageTransformations, Logger, Mailer, MailerPayload, MailerPayloadHtml, MailerPayloadReact, Middleware, MigrationType, NewStorageAttachment, NewStorageBlob, NewStorageVariantRecord, OpenAPIMethodSpec, OpenAPIObjectConfigV31, OpenAPIRegistration, QualifiedTableNames, RedisClient, ResizeOptions, Role, RouteModule, ScheduledWorkflowContext, ServerConfig, SessionData, Storage, StorageAttachment, StorageBlob, StorageRelations, StorageRelationsConfig, StorageService, StorageTables, StorageVariantRecord, ValidationErrorResponse, WorkerConfig, WorkflowContext, WorkflowHandle, appDir, auditActionSchema, baseSchema, createAccessControl, dbChangeInputSchema, dbChanges, dbChangesEvent, defaultI18nOptions, defineAppContainer, defineAppContext, defineAuth, defineAuthSchema, defineCache, defineConfig, defineDatabase, defineEvent, defineEventBus, defineI18n, defineLogger, defineMailer, defineMiddleware, defineMigrationOpts, defineOpenAPI, defineOpenAPIConfig, defineOpenAPIEndpoint, defineRedisClient, defineS3Disk, defineScheduledWorkflow, defineStorage, defineStorageSchema, defineTestDatabase, defineTypedResponses, defineWorkflow, generateOpenAPIDocument, i18n$1 as i18n, loadAndRegisterAPIRoutes, loadEvents, loadMiddleware, loadWorkflows, migrationsSchema, registerRoutes, resizeSchema, scanAPIRoutes, transformationsSchema, withOtelSpan, writeOpenAPISpecs };
25
+ }
26
+ //#endregion
27
+ export { index_d_exports };
@@ -0,0 +1,21 @@
1
+ import pino, { LoggerOptions } from "pino";
2
+
3
+ //#region src/api/logger.d.ts
4
+
5
+ /**
6
+ * Extended logger options with OpenTelemetry support.
7
+ */
8
+ interface DefineLoggerOptions extends LoggerOptions {}
9
+ /**
10
+ * The logger instance type.
11
+ */
12
+ type Logger = Awaited<ReturnType<typeof defineLogger>>;
13
+ /**
14
+ * Define the logger instance.
15
+ *
16
+ * @param opts - The options.
17
+ * @returns The logger.
18
+ */
19
+ declare function defineLogger(opts: DefineLoggerOptions): Promise<pino.Logger<never, boolean>>;
20
+ //#endregion
21
+ export { DefineLoggerOptions, Logger, defineLogger };
@@ -0,0 +1,70 @@
1
+ import Mail from "nodemailer/lib/mailer/index.ts";
2
+ import { JSX } from "react";
3
+
4
+ //#region src/api/mailer.d.ts
5
+
6
+ /**
7
+ * The mailer type.
8
+ */
9
+ type Mailer = ReturnType<typeof defineMailer>;
10
+ /**
11
+ * The mailer payload type with React component.
12
+ */
13
+ type MailerPayloadReact = Omit<Mail.Options, "from" | "html" | "text"> & {
14
+ /**
15
+ * The email address to send the email from.
16
+ */
17
+ from?: string;
18
+ /**
19
+ * The React component to render as the email body.
20
+ */
21
+ react: JSX.Element;
22
+ };
23
+ /**
24
+ * The mailer payload type with direct HTML and text.
25
+ */
26
+ type MailerPayloadHtml = Omit<Mail.Options, "from"> & {
27
+ /**
28
+ * The email address to send the email from.
29
+ */
30
+ from?: string;
31
+ /**
32
+ * The HTML content of the email.
33
+ */
34
+ html: string;
35
+ /**
36
+ * The plain text content of the email.
37
+ */
38
+ text: string;
39
+ };
40
+ /**
41
+ * The mailer payload type - supports either React component or direct HTML+text.
42
+ */
43
+ type MailerPayload = MailerPayloadReact | MailerPayloadHtml;
44
+ /**
45
+ * Define the options for the mailer.
46
+ */
47
+ interface DefineMailerOptions {
48
+ /**
49
+ * The default email address to use as the sender.
50
+ */
51
+ from: string;
52
+ /**
53
+ * The SMTP URL to use for sending emails.
54
+ */
55
+ smtpUrl: string;
56
+ }
57
+ /**
58
+ * Define the mailer.
59
+ *
60
+ * @param env - The environment variables.
61
+ * @returns The mailer.
62
+ */
63
+ declare function defineMailer({
64
+ from,
65
+ smtpUrl
66
+ }: DefineMailerOptions): {
67
+ send(payload: MailerPayload): Promise<void>;
68
+ };
69
+ //#endregion
70
+ export { DefineMailerOptions, Mailer, MailerPayload, MailerPayloadHtml, MailerPayloadReact, defineMailer };
@@ -0,0 +1 @@
1
+ function e(e){if(e&&typeof e==`object`&&`status`in e){let t=e.status;if(typeof t==`number`)return t}return e instanceof SyntaxError?400:500}function t(t){return(n,r,i,a)=>{let o=r.headers[`user-agent`];if(t.error({error:n.message,stack:n.stack,url:r.url,method:r.method,userAgent:typeof o==`string`?o:o?.[0],ip:r.ip},`Unhandled request error`),i.headersSent)return a(n);let s=process.env.NODE_ENV!==`production`;i.status(e(n)).json({error:`Internal server error`,...s&&{message:n.message,stack:n.stack}})}}export{t as defineErrorHandlerMiddleware};
@@ -0,0 +1 @@
1
+ function e(e=`/health`){return(t,n,r)=>{if(t.path!==e)return r();n.status(200).json({status:`healthy`,timestamp:new Date().toISOString(),uptime:process.uptime()})}}export{e as defineHealthMiddleware};
@@ -0,0 +1 @@
1
+ function e(e){return(t,n,r)=>{let i=t.headers[`accept-language`],a=`en`;if(i){let e=i.split(`,`)[0].split(`;`)[0].trim();e&&e.length>0&&(a=e)}t.dir=e.dir(a),t.language=a,t.languages=e.languages,t.t=e.getFixedT(a),r()}}export{e as defineI18nMiddleware};
@@ -0,0 +1,24 @@
1
+ import "../logger.mjs";
2
+ import { RequestHandler } from "ultimate-express";
3
+
4
+ //#region src/api/middleware/request-logger.d.ts
5
+
6
+ /**
7
+ * Metadata about the HTTP request.
8
+ */
9
+ interface RequestMetadata {
10
+ /**
11
+ * Client's IP address from X-Forwarded-For or RemoteAddr.
12
+ */
13
+ ipAddress: string;
14
+ /**
15
+ * Client's User-Agent header.
16
+ */
17
+ userAgent: string;
18
+ /**
19
+ * Client IP's country from CF-IPCountry header.
20
+ */
21
+ country: string;
22
+ }
23
+ //#endregion
24
+ export { RequestMetadata };
@@ -0,0 +1 @@
1
+ import{instrumentation_exports as e}from"../../instrumentation.mjs";const t=`cf-ipcountry`;function n(e,n){n?.injectCountry&&!e.headers[t]&&(e.headers[t]=n.injectCountry);let r=e.headers[`x-forwarded-for`],i;i=typeof r==`string`?r.split(`,`)[0].trim():Array.isArray(r)&&r.length>0?r[0].split(`,`)[0].trim():e.socket.remoteAddress||``;let a=e.headers[`user-agent`]||``,o=e.headers[t]||``;return{ipAddress:i,userAgent:a,country:o}}function r(t,r){let i;try{i=e.trace.getTracer(`request-logger`)}catch(e){t.warn({error:e},`Failed to initialize OpenTelemetry tracer`),i=null}return(a,o,s)=>{let c=n(a,r);a.metadata=c;let l=Date.now(),u=null,d;if(i)try{u=i.startSpan(`${a.method} ${a.path}`,{kind:e.SpanKind.SERVER,attributes:{"http.method":a.method,"http.url":a.url,"http.target":a.path,"http.host":(typeof a.headers.host==`string`?a.headers.host:a.headers[`:authority`])||`unknown`,"http.scheme":a.protocol,"http.user_agent":c.userAgent||`unknown`,"http.client_ip":c.ipAddress,"http.client_country":c.country}}),d=u.spanContext().traceId}catch(e){t.warn({error:e},`Failed to create OpenTelemetry span`),u=null,d=crypto.randomUUID()}else t.info(`OpenTelemetry tracer not available, skipping span creation`),u=null,d=crypto.randomUUID();let f=u?e.trace.setSpan(e.context.active(),u):e.context.active(),p=(e=>e.startsWith(`/@`)||e.startsWith(`/node_modules/`)||e.startsWith(`/assets/`)||e.startsWith(`/public/`)||e.match(/\.(js|css|ico|png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot|map)$/)||e.includes(`/locales/`)||e.endsWith(`.ts`)||e.endsWith(`.tsx`)||e.endsWith(`.json`))(a.path);o.setHeader(`X-Request-Id`,d),a.requestId=d,o.on(`finish`,()=>{let n=Date.now()-l;if(u)try{u.setAttributes({"http.status_code":o.statusCode,"http.response_content_length":o.get(`content-length`)||0}),o.statusCode>=400?u.setStatus({code:e.SpanStatusCode.ERROR,message:`HTTP ${o.statusCode}`}):u.setStatus({code:e.SpanStatusCode.OK}),u.end()}catch(e){t.warn({error:e},`Failed to update OpenTelemetry span`)}p||e.context.with(f,()=>{t.info({requestId:d,method:a.method,url:a.path,status:o.statusCode,duration:`${n}ms`},`Request completed`)})}),o.on(`close`,()=>{if(!(o.writableEnded||o.finished)){let n=Date.now()-l;if(u)try{u.setStatus({code:e.SpanStatusCode.ERROR,message:`Request closed unexpectedly`}),u.end()}catch(e){t.warn({error:e},`Failed to update OpenTelemetry span`)}p||e.context.with(f,()=>{t.warn({requestId:d,method:a.method,url:a.path,duration:`${n}ms`},`Request closed unexpectedly`)})}});let m=s;s=n=>{if(n){let r=Date.now()-l;if(u)try{u.recordException(n),u.setStatus({code:e.SpanStatusCode.ERROR,message:n instanceof Error?n.message:String(n)}),u.end()}catch(e){t.warn({error:e},`Failed to record exception in span`)}p||e.context.with(f,()=>{t.error({requestId:d,method:a.method,url:a.path,status:o.statusCode||500,duration:`${r}ms`,error:n instanceof Error?n.message:String(n)},`Request failed`)})}m(n)},e.context.with(f,()=>s())}}export{r as defineRequestLoggerMiddleware};
@@ -0,0 +1 @@
1
+ function e(e,t){return(n,r,i)=>{if(t()){e.info({url:n.url,method:n.method},`Rejecting request during shutdown`),r.status(503).set(`Connection`,`close`).json({error:`Server is shutting down`});return}i()}}export{e as defineShutdownMiddleware};
@@ -0,0 +1 @@
1
+ function e(e,t=3e4){return(n,r,i)=>{let a=!1,o=setTimeout(()=>{a=!0,e.warn({url:n.url,method:n.method,timeout:t},`Request timeout`),r.headersSent||r.status(408).json({error:`Request timeout`}),r.removeListener(`finish`,s),r.removeListener(`close`,s)},t),s=()=>{a||clearTimeout(o)};r.on(`finish`,s),r.on(`close`,s),i()}}export{e as defineTimeoutMiddleware};
@@ -0,0 +1 @@
1
+ function e(e){if(e&&typeof e==`object`&&`status`in e){let t=e.status;if(typeof t==`number`)return t}return e instanceof SyntaxError?400:500}function t(t,n={}){return async(r,i,a,o)=>{if(process.env.NODE_ENV===`production`)return o(r);try{let{default:t}=await import(`youch`),o=new t(r,i);o.addLink(e=>`<a href="${`https://stackoverflow.com/search?q=${encodeURIComponent(e)}`}" target="_blank" title="Search on StackOverflow">Search Stack Overflow</a>`),o.addLink(e=>`<a href="${`https://www.google.com/search?q=${encodeURIComponent(e)}`}" target="_blank" title="Search on Google">Search Google</a>`);let s=await o.toHTML().then(e=>n.theme===`dark`?e.replace(`<style>`,`<style>body { background: #1a1a1a; color: #f0f0f0; }`):e);a.writeHead(e(r),{"content-type":`text/html`}),a.end(s)}catch(n){t.error({error:n instanceof Error?n.message:n,originalError:r instanceof Error?r.message:r},`Failed to render Youch error page`),a.status(e(r)).json({error:{message:r instanceof Error?r.message:`Internal server error`,...process.env.NODE_ENV!==`production`&&{stack:r instanceof Error?r.stack:void 0}}})}}}export{t as defineYouchErrorHandler};
@@ -0,0 +1,39 @@
1
+ import { AppContainer } from "./container.mjs";
2
+ import { Express, RequestHandler } from "ultimate-express";
3
+
4
+ //#region src/api/middleware.d.ts
5
+ interface Middleware {
6
+ name: string | null;
7
+ handler: (container: AppContainer) => RequestHandler;
8
+ }
9
+ /**
10
+ * Defines a user middleware loaded from api/middleware/ directory.
11
+ *
12
+ * Use numeric prefixes (000_, 001_, etc.) to control load order.
13
+ *
14
+ * @example
15
+ * // api/middleware/000_custom-auth.ts
16
+ * export default defineMiddleware({
17
+ * handler: (container) => (req, res, next) => {
18
+ * // Custom logic using container
19
+ * next();
20
+ * },
21
+ * });
22
+ */
23
+ declare function defineMiddleware(config: {
24
+ name?: string;
25
+ handler: (container: AppContainer) => RequestHandler;
26
+ }): Middleware;
27
+ /**
28
+ * Loads user middleware from a directory and registers them with the app.
29
+ *
30
+ * Middleware files are loaded in alphabetical order by filename.
31
+ * Use numeric prefixes (000_, 001_, etc.) to control the load order.
32
+ *
33
+ * @param middlewareDir - The directory containing middleware files
34
+ * @param container - The application container
35
+ * @param app - The Express application instance
36
+ */
37
+ declare function loadMiddleware(middlewareDir: string, container: AppContainer, app: Express): Promise<void>;
38
+ //#endregion
39
+ export { Middleware, defineMiddleware, loadMiddleware };
@@ -0,0 +1 @@
1
+ import{camelCase as e}from"../../../../node_modules/.bun/change-case@5.4.4/node_modules/change-case/dist/index.mjs";import t,{glob as n}from"node:fs/promises";import{basename as r}from"node:path";function i(t){return e(r(t,`.ts`).replace(/^\d+_/,``))}async function a(e,a,o){try{await t.access(e)}catch{return}let s=[];for await(let t of n(`${e}/**/*.ts`))!t.endsWith(`.test.ts`)&&!t.endsWith(`.spec.ts`)&&s.push(t);s.sort((e,t)=>r(e).localeCompare(r(t)));for(let e of s)try{let t=(await import(e)).default;if(t&&typeof t.handler==`function`){let n=t.name??i(e);a.logger.debug({name:n,file:e},`Loading user middleware`),o.use(t.handler(a))}else a.logger.warn({file:e},`Middleware file missing default export with handler function`)}catch(t){throw a.logger.error({file:e,error:t instanceof Error?t.message:t},`Failed to load middleware`),t}}export{a as loadMiddleware};