@getstrata/core 0.5.100 → 0.7.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 (65) hide show
  1. package/CHANGELOG.md +67 -32
  2. package/README.md +16 -35
  3. package/dist/core/auth/abilityCatalog.d.ts +2 -2
  4. package/dist/core/auth/basicAuthGuard.d.ts +9 -0
  5. package/dist/core/auth/guard.d.ts +6 -0
  6. package/dist/core/auth/jwt.d.ts +19 -0
  7. package/dist/core/auth/jwtGuard.d.ts +14 -0
  8. package/dist/core/auth/tokenAbilityChecker.d.ts +5 -0
  9. package/dist/core/cache/tags.d.ts +6 -0
  10. package/dist/core/contracts/authUserDirectory.d.ts +4 -0
  11. package/dist/core/database/baseRepository.d.ts +5 -1
  12. package/dist/core/database/dialect.d.ts +18 -0
  13. package/dist/core/database/factory.d.ts +1 -0
  14. package/dist/core/database/index.d.ts +11 -3
  15. package/dist/core/database/model.d.ts +21 -2
  16. package/dist/core/database/mysqlConnection.d.ts +12 -0
  17. package/dist/core/database/namedConnections.d.ts +15 -0
  18. package/dist/core/database/relationQuery.d.ts +22 -3
  19. package/dist/core/database/relationships.d.ts +22 -2
  20. package/dist/core/database/repositoryQuery.d.ts +5 -1
  21. package/dist/core/database/sqliteConnection.d.ts +7 -0
  22. package/dist/core/http/loginThrottleMiddleware.d.ts +5 -2
  23. package/dist/core/http/resources.d.ts +2 -2
  24. package/dist/core/http/response.d.ts +2 -1
  25. package/dist/core/http/statelessAuth.d.ts +8 -0
  26. package/dist/core/http/throttleResponse.d.ts +2 -0
  27. package/dist/core/runtime/frontendMode.d.ts +10 -2
  28. package/dist/entries/auth/basicAuthGuard.js +137 -0
  29. package/dist/entries/auth/jwt.js +135 -0
  30. package/dist/entries/auth/jwtGuard.js +203 -0
  31. package/dist/entries/auth/sessionGuard.js +3 -21
  32. package/dist/entries/auth/tokenAbilityChecker.js +24 -0
  33. package/dist/entries/cache/tags.js +7 -1
  34. package/dist/entries/database/connectionContext.js +1 -0
  35. package/dist/entries/database/dialect.js +1 -0
  36. package/dist/entries/database/factory.js +5 -4
  37. package/dist/entries/database/model.js +189 -33
  38. package/dist/entries/database/mysqlConnection.js +35 -0
  39. package/dist/entries/database/namedConnections.js +1 -0
  40. package/dist/entries/database/query.js +28 -15
  41. package/dist/entries/database/relationships.js +43 -6
  42. package/dist/entries/database/repositoryQuery.js +142 -73
  43. package/dist/entries/database/schema.js +28 -15
  44. package/dist/entries/database/sqliteConnection.js +34 -0
  45. package/dist/entries/facades.js +1 -1
  46. package/dist/entries/http/contentNegotiation.js +5 -2
  47. package/dist/entries/http/csrfMiddleware.js +45 -0
  48. package/dist/entries/http/loginThrottleMiddleware.js +246 -7
  49. package/dist/entries/http/memoryThrottleMiddleware.js +208 -6
  50. package/dist/entries/http/requireAbilityMiddleware.js +35 -11
  51. package/dist/entries/http/requirePasswordConfirmMiddleware.js +5 -2
  52. package/dist/entries/http/requireVerifiedMiddleware.js +5 -2
  53. package/dist/entries/http/requireWebAuthMiddleware.js +12 -3
  54. package/dist/entries/http/resources.js +4 -1
  55. package/dist/entries/http/response.js +46 -12
  56. package/dist/entries/http/statelessAuth.js +48 -0
  57. package/dist/entries/http/throttleMiddleware.js +208 -6
  58. package/dist/entries/http/webErrorResponse.js +35 -11
  59. package/dist/entries/http/webFormRequest.js +5 -2
  60. package/dist/entries/mail/mailer.js +1 -1
  61. package/dist/entries/openapi/generator.js +48 -5
  62. package/dist/entries/runtime/frontendMode.js +39 -10
  63. package/dist/framework/public-api.d.ts +11 -1
  64. package/dist/index.js +1068 -250
  65. package/package.json +56 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,40 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 0.7.3
4
+
5
+ - `JsonResource.whenLoaded` returns `null` when a relation is loaded but empty. It does not call the transform, so `new PositionResource(value).toArray()` cannot crash on a missing belongsTo.
6
+
7
+ ## 0.7.2
8
+
9
+ - `withJsonErrorHandling` always maps thrown errors to JSON. `requestPrefersJson` treats `/api/` as JSON even when `Accept` is HTML, so hybrid staff HTML cannot remap JSON API errors.
10
+
11
+ ## 0.7.1
12
+
13
+ - `readSpaPrefix` / `normalizeSpaPrefix` own `SPA_PREFIX` (default `/app`). Apps set the env value. They do not copy a second static-file server.
14
+ - The log mail driver records `htmlBytes` instead of dumping the HTML document onto stdout.
15
+
16
+ ## 0.7.0
17
+
18
+ - `FRONTEND_MODE=hybrid` turns on staff HTML and the candidate SPA together. Views are on for `server-htmx` and `hybrid`. The SPA is on for `spa-react` and `hybrid`.
19
+ - `parseFrontendMode`, `FRONTEND_MODES`, and `FRONTEND_MODE_PATTERN` are the allowed-value list. Apps should reuse that pattern in env schemas.
20
+ - Named database connections: `registerNamedConnection`, `runOnNamedConnection`, SQLite (`bun:sqlite`), and MySQL (`mysql2`). `runWithSqlDialect` keeps the dialect across `await` via AsyncLocalStorage.
21
+ - New subpaths: `database/namedConnections`, `database/sqliteConnection`, `database/mysqlConnection`, `database/connectionContext`.
22
+ - OpenAPI treats `POST /api/apply/login` as unauthenticated.
23
+
24
+ ## 0.6.0
25
+
26
+ - **Breaking:** cookie session signatures use HMAC-SHA256. Existing HMAC cookies signed with the previous digest will not verify. Rotate `SESSION_SECRET` or sign users in again.
27
+ - **Breaking:** default `MEMBER_ABILITIES` are profile and token scopes (`profile:read`, `auth:tokens:*`), not org/project/task. Apps replace the catalog with `configureAbilityCatalog`. HiroApp maps admin / recruiter / candidate.
28
+ - Named auth guards: opaque Bearer tokens, JWT HS256, HTTP Basic, and cookie sessions. `AuthManager` picks a guard from the `Authorization` scheme. CSRF is skipped for Bearer and Basic.
29
+ - OpenAPI treats HiroApp login, JWT mint (`POST /api/auth/token`), and public careers as unauthenticated. Partner ping and audit export require credentials.
30
+ - SQL dialect helpers (`pgsql`, `mysql`, `sqlite`) for placeholders, quoting, `ILIKE`/`LIKE`, `RETURNING`, and `NULLS LAST`. Full-text `tsMatch` stays Postgres-only and throws on other engines.
31
+ - HiroApp is the only in-repo example product. The leftover `src/db` schema is a test fixture, not a second app.
32
+ - New subpaths: `auth/jwt`, `auth/jwtGuard`, `auth/basicAuthGuard`, `auth/tokenAbilityChecker`, `database/dialect`, `http/statelessAuth`.
33
+
34
+ ## 0.5.101
35
+
36
+ - Tenant middleware falls back with explicit branches when a member, admin, or guest tenant lookup misses, so the request still scopes to the default tenant.
37
+
3
38
  ## 0.5.100
4
39
 
5
40
  HiroApp dogfood of 0.5.99: eager `with()` / nested `load("a.b")` missed related rows when a Postgres int4 PK arrived as a JS `number` and an int8 FK as a `bigint`. Map matching used `===`.
@@ -12,12 +47,12 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names. `hashe
12
47
 
13
48
  ## 0.5.99
14
49
 
15
- HiroApp dogfood of 0.5.98 found lookalike APIs. This release matches Laravel call shape and SQL, not just export names.
50
+ HiroApp dogfood of 0.5.98 found lookalike APIs. This release matches the previous PHP framework call shape and SQL, not just export names.
16
51
 
17
52
  - Relation queries are thenable: `await user.applications()` delegates to `get()`.
18
53
  - `Model.with()` / `where()` / `whereHas()` return a `ModelQuery` that hydrates models and chains into `where` / `find` / `findOrFail` / `first` / `get`. `first()` / `find()` use `LIMIT` / PK lookup.
19
54
  - `belongsTo.where()` threads constraints into `whereHas` EXISTS (HiroApp application search).
20
- - `{ ilike }` uses the value as-is (Laravel). Pass `%term%` yourself; the operator no longer wraps extra `%`.
55
+ - `{ ilike }` uses the value as-is (the previous PHP framework). Pass `%term%` yourself; the operator no longer wraps extra `%`.
21
56
  - Morph type defaults to `$morphClass` / class name, not `table.name`. Override with `$morphClass = "App\\Models\\User"` or the last `morphMany` argument. `morphTo()` no longer silently defaults to `imageable_*`.
22
57
  - `primaryKey()` defaults to the table PK (`id`).
23
58
  - Nested `load("a.b")` / `with("a.b")` skip already-loaded heads and batch the next level.
@@ -37,7 +72,7 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
37
72
  - `Model.where()`, `firstOrNew()`, `firstOrCreate()`, and `updateOrCreate()`.
38
73
  - Factory `count`/`state`/`sequence`/`for`/`has`/`recycle` plus `afterMaking`/`afterCreating`.
39
74
  - `JsonResource` (`wrap`, `whenLoaded`, `additional`, `collection`).
40
- - Laravel aliases: container `make`/`instance`, EventBus `on`/`emit`, query `whereNull`/`whereIn`/`whereExists`.
75
+ - the previous PHP framework aliases: container `make`/`instance`, EventBus `on`/`emit`, query `whereNull`/`whereIn`/`whereExists`.
41
76
  - Parity audit reports design score separately. Horizon/Nova/CLI stay Bun-native stand-ins.
42
77
 
43
78
  ## 0.5.97
@@ -55,10 +90,10 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
55
90
 
56
91
  ## 0.5.90
57
92
 
58
- - **Breaking identity defaults:** `appKeyPrefix()` is `strata` and `appDisplayName()` is `Strata` when `APP_KEY_PREFIX` / `APP_NAME` are unset (were `workhub` / `WorkHub`). WorkHub pins those env vars.
93
+ - **Breaking identity defaults:** `appKeyPrefix()` is `strata` and `appDisplayName()` is `Strata` when `APP_KEY_PREFIX` / `APP_NAME` are unset (were `strata` / `the previous in-repo app`). the previous in-repo app pins those env vars.
59
94
  - `Schedule.command()` rejects cron strings other than `* * * * *` and `*/N * * * *` instead of silently never running them.
60
95
  - OpenAPI marks `GET /users/me/*` as bearer-authenticated.
61
- - OpenAPI `/users/me/current-organization` summaries no longer say Jetstream.
96
+ - OpenAPI `/users/me/current-organization` summaries no longer say team invitations.
62
97
 
63
98
  ## 0.5.89
64
99
 
@@ -66,16 +101,16 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
66
101
 
67
102
  ## 0.5.88
68
103
 
69
- - Exported cookie name constants (`SESSION_COOKIE`, `CSRF_COOKIE`, `FLASH_COOKIE`, `INTENDED_URL_COOKIE`, `PASSWORD_CONFIRM_COOKIE`) are `appCookieName(...)` so they match the default prefix instead of a hardcoded `workhub_` string.
70
- - `@getstrata/core/jobs/dispatchWebhookJob` is a deprecated compatibility re-export of the WorkHub webhook job.
104
+ - Exported cookie name constants (`SESSION_COOKIE`, `CSRF_COOKIE`, `FLASH_COOKIE`, `INTENDED_URL_COOKIE`, `PASSWORD_CONFIRM_COOKIE`) are `appCookieName(...)` so they match the default prefix instead of a hardcoded `strata_` string.
105
+ - `@getstrata/core/jobs/dispatchWebhookJob` is a deprecated compatibility re-export of the the previous in-repo app webhook job.
71
106
 
72
107
  ## 0.5.87
73
108
 
74
- - `AuthUserDirectory.hasActiveBrowserSession?(userId, issuedAt)` is optional. `SessionGuard` calls it after `session_valid_after` and rejects the HMAC cookie when it returns false so WorkHub can revoke a single browser session by deleting the `sessions` row.
109
+ - `AuthUserDirectory.hasActiveBrowserSession?(userId, issuedAt)` is optional. `SessionGuard` calls it after `session_valid_after` and rejects the HMAC cookie when it returns false so the previous in-repo app can revoke a single browser session by deleting the `sessions` row.
75
110
 
76
111
  ## 0.5.86
77
112
 
78
- - `createSessionCookieDetails()` returns the HMAC session header plus `issuedAt` / `ttlSeconds` so apps can persist Jetstream browser-session rows without changing the cookie format.
113
+ - `createSessionCookieDetails()` returns the HMAC session header plus `issuedAt` / `ttlSeconds` so apps can persist team invitations browser-session rows without changing the cookie format.
79
114
 
80
115
  ## 0.5.85
81
116
 
@@ -87,24 +122,24 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
87
122
 
88
123
  ## 0.5.83
89
124
 
90
- - `MEMBER_ABILITIES` includes `auth:tokens:delete` so Jetstream-style personal access token revoke works for members (`DELETE /auth/tokens/:id`). HTML `/account/tokens/:id/revoke` was already authenticated-only.
125
+ - `MEMBER_ABILITIES` includes `auth:tokens:delete` so team invitations-style personal access token revoke works for members (`DELETE /auth/tokens/:id`). HTML `/account/tokens/:id/revoke` was already authenticated-only.
91
126
  - `buildOtpauthUrl()` defaults the issuer through `appDisplayName()` (`APP_NAME`).
92
127
 
93
128
  ## 0.5.82
94
129
 
95
- - `MEMBER_ABILITIES` includes `organizations:create` so Jetstream-style extra teams work for members (HTML `POST /organizations` and JSON `POST /organizations`). `OrganizationPolicy.create` already allowed any authenticated user.
130
+ - `MEMBER_ABILITIES` includes `organizations:create` so team invitations-style extra teams work for members (HTML `POST /organizations` and JSON `POST /organizations`). `OrganizationPolicy.create` already allowed any authenticated user.
96
131
 
97
132
  ## 0.5.81
98
133
 
99
- - `@getstrata/core/auth/intendedUrlCookie` (`createIntendedUrlCookie`, `readIntendedUrl`, `clearIntendedUrlCookie`, `createIntendedUrlCookieFromRequest`). Fortify intended URL after HTML email verification: register with `redirect=` and Laravel `verified` HTML redirects stash `${APP_KEY_PREFIX}_intended` (`INTENDED_URL_COOKIE_NAME`, default `workhub_intended`; TTL `INTENDED_URL_TTL_SECONDS`, default 86400s). `GET /verify-email` honors and clears it.
134
+ - `@getstrata/core/auth/intendedUrlCookie` (`createIntendedUrlCookie`, `readIntendedUrl`, `clearIntendedUrlCookie`, `createIntendedUrlCookieFromRequest`). HTML auth intended URL after HTML email verification: register with `redirect=` and the previous PHP framework `verified` HTML redirects stash `${APP_KEY_PREFIX}_intended` (`INTENDED_URL_COOKIE_NAME`, default `strata_intended`; TTL `INTENDED_URL_TTL_SECONDS`, default 86400s). `GET /verify-email` honors and clears it.
100
135
 
101
136
  ## 0.5.80
102
137
 
103
- - `appCookieName()` / `appDevSecret()` on `@getstrata/core/runtime/appKeyPrefix`. HMAC session, CSRF, flash, password-confirm, signed-URL, OAuth-state, and token-pepper fallbacks follow `APP_KEY_PREFIX` (WorkHub defaults unchanged).
138
+ - `appCookieName()` / `appDevSecret()` on `@getstrata/core/runtime/appKeyPrefix`. HMAC session, CSRF, flash, password-confirm, signed-URL, OAuth-state, and token-pepper fallbacks follow `APP_KEY_PREFIX` (the previous in-repo app defaults unchanged).
104
139
 
105
140
  ## 0.5.79
106
141
 
107
- - `readSession()` returns `{ userId, issuedAt }` from the HMAC session cookie. `isSessionInvalidated(issuedAt, session_valid_after)` lets `SessionGuard` reject cookies issued before `AuthUserRecord.session_valid_after` (Jetstream logout-other-devices / password change).
142
+ - `readSession()` returns `{ userId, issuedAt }` from the HMAC session cookie. `isSessionInvalidated(issuedAt, session_valid_after)` lets `SessionGuard` reject cookies issued before `AuthUserRecord.session_valid_after` (team invitations logout-other-devices / password change).
108
143
 
109
144
  ## 0.5.78
110
145
 
@@ -112,7 +147,7 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
112
147
 
113
148
  ## 0.5.77
114
149
 
115
- - `@getstrata/core/security/recoveryCodes` (`generateRecoveryCodes`, `hashRecoveryCode`, `recoveryCodeMatches`). Fortify-style one-time MFA backup codes (`abcd-efgh`).
150
+ - `@getstrata/core/security/recoveryCodes` (`generateRecoveryCodes`, `hashRecoveryCode`, `recoveryCodeMatches`). HTML auth-style one-time MFA backup codes (`abcd-efgh`).
116
151
 
117
152
  ## 0.5.76
118
153
 
@@ -120,12 +155,12 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
120
155
 
121
156
  ## 0.5.75
122
157
 
123
- - Laravel `password.confirm`: `@getstrata/core/auth/passwordConfirmCookie` (`createPasswordConfirmCookie`, `hasFreshPasswordConfirmation`, `clearPasswordConfirmCookie`) and `createRequirePasswordConfirmMiddleware()` (HTML 302 `/confirm-password`, JSON 423). Cookie name `PASSWORD_CONFIRM_COOKIE_NAME` (default `workhub_password_confirmed`), TTL `PASSWORD_CONFIRM_TIMEOUT` (default 10800s).
158
+ - the previous PHP framework `password.confirm`: `@getstrata/core/auth/passwordConfirmCookie` (`createPasswordConfirmCookie`, `hasFreshPasswordConfirmation`, `clearPasswordConfirmCookie`) and `createRequirePasswordConfirmMiddleware()` (HTML 302 `/confirm-password`, JSON 423). Cookie name `PASSWORD_CONFIRM_COOKIE_NAME` (default `strata_password_confirmed`), TTL `PASSWORD_CONFIRM_TIMEOUT` (default 10800s).
124
159
 
125
160
  ## 0.5.74
126
161
 
127
162
  - `AuthUser.emailVerifiedAt` and `@getstrata/core/auth/emailVerification` (`isEmailVerificationRequired`, `hasVerifiedEmail`). `null` means unverified; missing is treated as verified (GuestGuard / legacy).
128
- - `createRequireVerifiedMiddleware()` is Laravel `verified` (HTML 302 `/email/verify`, JSON 403).
163
+ - `createRequireVerifiedMiddleware()` is the previous PHP framework `verified` (HTML 302 `/email/verify`, JSON 403).
129
164
 
130
165
  ## 0.5.73
131
166
 
@@ -150,40 +185,40 @@ Still non-conforming (honest): Bun cannot infer `morphTo()` method names (`debug
150
185
 
151
186
  ## 0.5.68
152
187
 
153
- - Published core no longer imports WorkHub `src/config`. Frontend mode, queue retries, CORS, and upload limits read env (`FRONTEND_MODE`, `QUEUE_*`, `CORS_ALLOWED_ORIGINS`, `MAX_UPLOAD_BYTES`).
188
+ - Published core no longer imports the previous in-repo app `src/config`. Frontend mode, queue retries, CORS, and upload limits read env (`FRONTEND_MODE`, `QUEUE_*`, `CORS_ALLOWED_ORIGINS`, `MAX_UPLOAD_BYTES`).
154
189
  - `@getstrata/core/runtime/frontendMode` exports `readFrontendMode` / `isViewsEnabled` / `isSpaEnabled`.
155
190
 
156
191
  ## 0.5.67
157
192
 
158
- - OpenAPI title, server URLs, and generated SDK class name come from `APP_NAME` / `APP_URL` / `API_PREFIX` / `APP_SDK_CLASS` instead of importing WorkHub `src/config/app`.
159
- - `appEnv()`, `appUrl()`, `apiPrefix()`, and `sdkClientClassName()` live on `@getstrata/core/runtime/appKeyPrefix`. SIEM export, HSTS, and `safeFetch` DNS resolve use `appEnv()` instead of WorkHub `appConfig`.
193
+ - OpenAPI title, server URLs, and generated SDK class name come from `APP_NAME` / `APP_URL` / `API_PREFIX` / `APP_SDK_CLASS` instead of importing the previous in-repo app `src/config/app`.
194
+ - `appEnv()`, `appUrl()`, `apiPrefix()`, and `sdkClientClassName()` live on `@getstrata/core/runtime/appKeyPrefix`. SIEM export, HSTS, and `safeFetch` DNS resolve use `appEnv()` instead of the previous in-repo app `appConfig`.
160
195
 
161
196
  ## 0.5.66
162
197
 
163
- - `createValidateSignatureMiddleware()` on `@getstrata/core/http/signedUrl` is Laravel’s `signed` / `ValidateSignature` middleware. Invalid or expired links throw `ForbiddenError`.
198
+ - `createValidateSignatureMiddleware()` on `@getstrata/core/http/signedUrl` is the previous PHP framework's `signed` / `ValidateSignature` middleware. Invalid or expired links throw `ForbiddenError`.
164
199
 
165
200
  ## 0.5.65
166
201
 
167
- - Identity helpers on `@getstrata/core/runtime/appKeyPrefix` (`smtpEhloHost`, `siemEventType`, `appUserAgent`, `otelServiceName`, `appDisplayName`, `webhookSignatureHeader`) so sibling apps are not stuck with WorkHub SMTP/SIEM/OTEL/OAuth names.
168
- - SIEM `event_type` and CEF vendor follow `SIEM_EVENT_TYPE` / `APP_NAME` (defaults stay `workhub.audit` / `WorkHub`).
202
+ - Identity helpers on `@getstrata/core/runtime/appKeyPrefix` (`smtpEhloHost`, `siemEventType`, `appUserAgent`, `otelServiceName`, `appDisplayName`, `webhookSignatureHeader`) so sibling apps are not stuck with the previous in-repo app SMTP/SIEM/OTEL/OAuth names.
203
+ - SIEM `event_type` and CEF vendor follow `SIEM_EVENT_TYPE` / `APP_NAME` (defaults stay `strata.audit` / `the previous in-repo app`).
169
204
 
170
205
  ## 0.5.64
171
206
 
172
- - `APP_KEY_PREFIX` (default `workhub`) namespaces Redis cache, queue, and throttle keys so sibling apps do not share WorkHub’s keyspace.
173
- - `DispatchWebhookJob` implementation lives in the WorkHub webhook module. `@getstrata/core/jobs/dispatchWebhookJob` remains a compatibility re-export.
207
+ - `APP_KEY_PREFIX` (default `strata`) namespaces Redis cache, queue, and throttle keys so sibling apps do not share the previous in-repo app's keyspace.
208
+ - `DispatchWebhookJob` implementation lives in the the previous in-repo app webhook module. `@getstrata/core/jobs/dispatchWebhookJob` remains a compatibility re-export.
174
209
 
175
210
  ## 0.5.63
176
211
 
177
- - Flash cookies honor `FLASH_COOKIE_NAME` (default `workhub_flash`) so sibling apps do not inherit WorkHub’s cookie name.
212
+ - Flash cookies honor `FLASH_COOKIE_NAME` (default `strata_flash`) so sibling apps do not inherit the previous in-repo app's cookie name.
178
213
 
179
214
  ## 0.5.62
180
215
 
181
- Laravel URL signing, Bun-native markdown mail, and session-auth cleanup.
216
+ the previous PHP framework URL signing, Bun-native markdown mail, and session-auth cleanup.
182
217
 
183
218
  - `temporarySignedUrl` / `signedUrl` / `hasValidSignature` / `assertValidSignature` on `@getstrata/core/http/signedUrl`. HMAC uses `SIGNED_URL_SECRET` or `SESSION_SECRET`. Paths must be same-origin (`/` only; reject `//` and `://`).
184
219
  - `markdownToHtml()` uses `Bun.markdown.html()` plus `sanitizeMailHtml` (allowlist). Scripts, `javascript:` links, and unknown tags are stripped.
185
220
  - `ExportAuditLogsJob` (`@getstrata/core/jobs/exportAuditLogsJob`) wraps SIEM export so the scheduler can dispatch a real job.
186
- - `DispatchWebhookJob` reads `APP_ENV` and `WEBHOOK_SIGNATURE_HEADER` instead of WorkHub `appConfig`.
221
+ - `DispatchWebhookJob` reads `APP_ENV` and `WEBHOOK_SIGNATURE_HEADER` instead of the previous in-repo app `appConfig`.
187
222
  - `createRequireWebAuthMiddleware` runs the handler inside `runWithAuthUser` so `currentAuthUser()` works on HTMX session routes.
188
223
  - `generateTotpSecret()` / `buildOtpauthUrl()` on `@getstrata/core/security/totp`.
189
224
  - `createRequireAbilityMiddleware` rethrows `ForbiddenError` for HTML views so HTMX routes render a styled 403 instead of JSON.
@@ -207,13 +242,13 @@ HTMX HTML kernel gaps that sibling apps could not work around without weakening
207
242
 
208
243
  ## 0.5.59
209
244
 
210
- Sibling HTMX apps can consume published packages without WorkHub-only glue.
245
+ Sibling HTMX apps can consume published packages without the previous in-repo app-only glue.
211
246
 
212
247
  - Emit `@getstrata/core/facades` types at `dist/core/facades/index.d.ts` (CI checks every `exports.*.types` path after build).
213
- - Session and CSRF cookie names are configurable (`SESSION_COOKIE_NAME`, `CSRF_COOKIE_NAME`). Defaults remain `workhub_session` and `workhub_csrf` for WorkHub.
248
+ - Session and CSRF cookie names are configurable (`SESSION_COOKIE_NAME`, `CSRF_COOKIE_NAME`). Defaults remain `strata_session` and `strata_csrf` for the previous in-repo app.
214
249
  - Split `CORE_ABILITY_CHECKER_TOKEN` and `CORE_AUTH_USER_DIRECTORY_TOKEN` from `CORE_TOKEN_SERVICE_TOKEN`. HttpKernel prefers the ability-checker token; HMAC `SessionGuard` / layout data prefer the user directory. A compatibility shim uses `CORE_TOKEN_SERVICE_TOKEN` only when the bound value matches the requested type.
215
- - `configureWebLayoutData` lets apps choose `currentUser` vs `authUser`, a custom user loader, and extra template fields. WorkHub still gets `{ authUser, csrfToken, flash }`.
250
+ - `configureWebLayoutData` lets apps choose `currentUser` vs `authUser`, a custom user loader, and extra template fields. the previous in-repo app still gets `{ authUser, csrfToken, flash }`.
216
251
  - `redirectResponse`, `notFoundHtmlResponse`, `textResponse`, and `xmlResponse` join `htmlResponse` / `isHtmxRequest` on `@getstrata/core/view`.
217
252
  - `LOGIN_RATE_LIMIT_WINDOW_MS` is a deprecated alias for `LOGIN_RATE_LIMIT_WINDOW_SECONDS`.
218
253
 
219
- HMAC `SessionGuard` is unchanged for WorkHub API/token apps. Sibling HTMX apps should use `@getstrata/bootstrap` `CookieSessionStore` + `createCookieSessionAuthManager`.
254
+ HMAC `SessionGuard` is unchanged for the previous in-repo app API/token apps. Sibling HTMX apps should use `@getstrata/bootstrap` `CookieSessionStore` + `createCookieSessionAuthManager`.
package/README.md CHANGED
@@ -2,73 +2,56 @@
2
2
 
3
3
  Stable **Strata** framework surface for application modules.
4
4
 
5
- **Source:** `src/framework/public-api.ts` (monorepo)
5
+ **Source:** `src/framework/public-api.ts` (monorepo)
6
6
  **Repository:** [EyK-26/strata](https://github.com/EyK-26/strata), directory `packages/strata-core`
7
7
 
8
- WorkHub is the reference application built on Strata; import the framework from this package in your own modules. Sibling HTMX apps (Eta, cookie sessions, no API tokens) should follow [docs/SIBLING-HTMX.md](../../docs/SIBLING-HTMX.md).
8
+ HiroApp is the example product. Import this package from your own modules. HTML cookie apps: [docs/BUILDING-APPS.md](../../docs/BUILDING-APPS.md). Auth choices: [docs/AUTH.md](../../docs/AUTH.md). SQL engines: [docs/DATABASE.md](../../docs/DATABASE.md).
9
9
 
10
10
  ## Usage
11
11
 
12
- Set `DATABASE_URL` before importing (connection is created lazily on first query):
12
+ Set `DATABASE_URL` before importing (the connection is created lazily on first query):
13
13
 
14
14
  ```typescript
15
15
  process.env.DATABASE_URL ??= "postgresql://postgres:postgres@localhost:5432/myapp";
16
16
 
17
- import { AdminResourceRegistry } from "@getstrata/core/admin/registry";
18
- import { formatAdminValue } from "@getstrata/core/admin/formatValue";
19
17
  import { Policy } from "@getstrata/core/auth/policy";
20
18
  import { BaseRepository } from "@getstrata/core/database/baseRepository";
21
- import { mail } from "@getstrata/core/facades";
22
19
  import { FormRequest } from "@getstrata/core/http/formRequest";
23
20
  import { withErrorHandling } from "@getstrata/core/http/response";
24
- import { mailer } from "@getstrata/core/mail/mailer";
25
- import { storage } from "@getstrata/core/facades";
26
21
  import { EtaViewEngine } from "@getstrata/core/view";
27
22
  ```
28
23
 
29
- `.eta` files are **HTML + Eta tags** (`<% %>`, `<%= %>`, `<%~ include() %>`), not Pug. Class/attribute shorthand such as `section.section` or `a href=` fails at render time with the template name.
24
+ `.eta` files are **HTML + Eta tags** (`<% %>`, `<%= %>`, `<%~ include() %>`), not another template language. Class/attribute shorthand such as `section.section` or `a href=` fails at render time with the template name.
30
25
 
31
- **Dependency:** `eta` is bundled as a direct dependency of `@getstrata/core`. Apps do not need to list it separately. The database driver is your app's choice. WorkHub and getstrata use **Bun's built-in `Bun.sql`** client; create and bind it with `createBunSqlPool()` / `bindBunSql()`, or call `bindDatabaseConnection()` yourself.
26
+ **Dependency:** `eta` is a direct dependency of `@getstrata/core`. Apps do not need to list it separately. The database **engine** is your choice. HiroApp uses **Bun's built-in `Bun.sql`** (Postgres). Create and bind it with `createBunSqlPool()` / `bindBunSql()`, or call `bindDatabaseConnection()` yourself. Extra engines register with `registerNamedConnection` (`database/namedConnections`, `database/sqliteConnection`, `database/mysqlConnection`).
32
27
 
33
- `orderBy` accepts explicit `{ column, direction }` objects or column shorthand such as `{ published_at: "desc" }`.
28
+ `orderBy` accepts `{ column, direction }` objects or column shorthand such as `{ published_at: "desc" }`. `{ ilike }` uses the value as-is. Pass `%term%` yourself.
34
29
 
35
30
  ## Admin and queue helpers
36
31
 
37
- Exports for admin dashboards and queue recovery:
38
-
39
32
  - `AdminResourceRegistry`, `formatAdminValue`: read-only resource browsers
40
33
  - `createFailedJobService`, `FailedJobService.delete()`: failed job persistence and cleanup
41
34
  - `runQueueJob`, `jobRegistry`: dispatch retried jobs from admin UIs
42
35
 
36
+ HiroApp uses failed-job recovery in the staff dashboard. You do not have to use the admin registry.
37
+
43
38
  ## Build and verify (monorepo root)
44
39
 
45
40
  ```bash
46
41
  bun run build:framework
47
- bun run verify:framework # build + public API tests
48
- bun run verify:shared-subpaths # after build: confirm singleton shims
42
+ bun run verify:framework
43
+ bun run verify:shared-subpaths
49
44
  ```
50
45
 
51
46
  ## Subpath imports
52
47
 
53
- `@getstrata/core` publishes **144+ subpaths** (for example `@getstrata/core/http/authMiddleware`,
54
- `@getstrata/core/database/migrations`). Prefer subpaths over the root import in apps, bootstrap, and tests.
48
+ `@getstrata/core` publishes many subpaths (for example `@getstrata/core/http/authMiddleware`, `@getstrata/core/database/migrations`). Prefer subpaths over the root import in apps, bootstrap, and tests.
55
49
 
56
- Some subpaths **re-export the main bundle** so singleton state stays shared (database pool binding,
57
- `AsyncLocalStorage` auth/tenant context, global registries, `HttpError` / `Notification` classes for
58
- `instanceof`). The canonical list lives in `scripts/core-shared-subpaths.ts` and is verified by
59
- `scripts/verify-core-shared-subpaths.ts` after each framework build.
50
+ Some subpaths **re-export the main bundle** so singleton state stays shared (database pool, dialect override, async-local auth/tenant, `HttpError` for `instanceof`). The list lives in `scripts/core-shared-subpaths.ts`.
51
+
52
+ When adding a subpath that owns process-wide state or base classes used with `instanceof`, append it to `CORE_SHARED_SUBPATHS`, run `bun scripts/sync-package-subpaths.ts`, and rebuild.
60
53
 
61
- When adding a subpath that owns process-wide state or base classes used with `instanceof`, append it to
62
- `CORE_SHARED_SUBPATHS`, run `bun scripts/sync-package-subpaths.ts`, and rebuild. Non-shared subpath
63
- bundles are built with generated `--external @getstrata/core/*` flags (all 144+ subpaths) so framework
64
- source can import shared modules via package self-imports (`scripts/codemod-core-self-imports.ts`).
65
- Bootstrap subpath builds externalize all `@getstrata/bootstrap/*` and `@getstrata/core/*` entries.
66
54
  `scripts/verify-no-root-imports.ts` blocks root `@getstrata/core` imports in application source.
67
- `scripts/verify-no-shared-barrel-imports.ts` blocks `@getstrata/core/database` and
68
- `@getstrata/core/http` barrel imports in application source.
69
- `scripts/audit-public-api-surface.ts` reports root exports with no in-repo root import usage.
70
- `scripts/verify-bundled-subpaths.ts` ensures entries like `http/webFormRequest` do not inline
71
- `ValidationError`.
72
55
 
73
56
  ```typescript
74
57
  import { createAuthMiddleware } from "@getstrata/core/http/authMiddleware";
@@ -82,9 +65,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
82
65
  Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
83
66
 
84
67
  1. Add `NPM_TOKEN` to GitHub repository secrets.
85
- 2. Tag a release: `git tag v0.5.96 && git push origin v0.5.96`
68
+ 2. Tag a release: `git tag v0.7.3 && git push origin v0.7.3`
86
69
  3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
87
70
 
88
- Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
89
-
90
- See [docs/PACKAGING.md](../../docs/PACKAGING.md) for boundaries and future extraction.
71
+ See [docs/PACKAGING.md](../../docs/PACKAGING.md).
@@ -1,5 +1,5 @@
1
- declare const MEMBER_ABILITIES: readonly ["organizations:read", "organizations:create", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write", "auth:tokens:delete"];
2
- declare const ADMIN_ABILITIES: readonly ["organizations:read", "organizations:create", "projects:read", "projects:create", "tasks:read", "tasks:create", "comments:read", "comments:create", "attachments:read", "attachments:create", "auth:tokens:read", "auth:tokens:write", "auth:tokens:delete", "organizations:create", "organizations:update", "organizations:delete", "projects:update", "projects:delete", "tasks:update", "tasks:delete", "comments:update", "comments:delete", "attachments:delete", "webhooks:read", "webhooks:write", "audit:read"];
1
+ declare const MEMBER_ABILITIES: readonly ["profile:read", "auth:tokens:read", "auth:tokens:write", "auth:tokens:delete"];
2
+ declare const ADMIN_ABILITIES: readonly ["profile:read", "auth:tokens:read", "auth:tokens:write", "auth:tokens:delete", "webhooks:read", "webhooks:write", "audit:read", "audit:export"];
3
3
  declare const PLATFORM_ADMIN_ABILITIES: readonly ["*"];
4
4
  interface AbilityCatalog {
5
5
  member: readonly string[];
@@ -0,0 +1,9 @@
1
+ import type { ServiceContainerLike } from "../contracts/serviceContainer";
2
+ import type { AuthUser } from "./authContext";
3
+ import type { AuthGuard } from "./guard";
4
+ declare class BasicAuthGuard implements AuthGuard {
5
+ private readonly container;
6
+ constructor(container: ServiceContainerLike);
7
+ resolve(request: Request): Promise<AuthUser | null>;
8
+ }
9
+ export { BasicAuthGuard };
@@ -26,11 +26,17 @@ declare class CompositeGuard implements AuthGuard {
26
26
  }
27
27
  declare class AuthManager {
28
28
  private readonly guard;
29
+ private readonly namedGuards;
29
30
  constructor(guard: AuthGuard);
31
+ registerGuard(name: string, next: AuthGuard): this;
32
+ use(name?: string): AuthGuard;
33
+ guardNames(): string[];
30
34
  resolve(request?: Request): Promise<AuthUser | null>;
31
35
  user(request?: Request): Promise<AuthUser | null>;
32
36
  check(request?: Request): Promise<boolean>;
33
37
  requireUser(request?: Request): Promise<AuthUser>;
38
+ private authenticateRequest;
39
+ private tryNamedGuards;
34
40
  }
35
41
  export type { AuthGuard, AuthUser };
36
42
  export { ApiTokenGuard, AuthManager, CompositeGuard, DatabaseTokenGuard, GuestGuard };
@@ -0,0 +1,19 @@
1
+ interface JwtPayload {
2
+ sub: string | number;
3
+ role?: string;
4
+ abilities?: string[];
5
+ emailVerifiedAt?: Date | string | null;
6
+ iat?: number;
7
+ exp?: number;
8
+ [key: string]: unknown;
9
+ }
10
+ interface SignJwtOptions {
11
+ secret?: string;
12
+ ttlSeconds?: number;
13
+ }
14
+ declare function resolveJwtSecret(secret?: string): string;
15
+ declare function jwtTtlSeconds(override?: number): number;
16
+ declare function signJwt(payload: JwtPayload, options?: SignJwtOptions): string;
17
+ declare function verifyJwt(token: string, secret?: string): JwtPayload | null;
18
+ export type { JwtPayload, SignJwtOptions };
19
+ export { jwtTtlSeconds, resolveJwtSecret, signJwt, verifyJwt };
@@ -0,0 +1,14 @@
1
+ import type { AuthUser } from "./authContext";
2
+ import type { AuthGuard } from "./guard";
3
+ import { type JwtPayload } from "./jwt";
4
+ interface JwtGuardOptions {
5
+ secret?: string;
6
+ }
7
+ declare function authUserFromJwt(payload: JwtPayload): AuthUser;
8
+ declare class JwtGuard implements AuthGuard {
9
+ private readonly options;
10
+ constructor(options?: JwtGuardOptions);
11
+ resolve(request: Request): AuthUser | null;
12
+ }
13
+ export type { JwtGuardOptions };
14
+ export { authUserFromJwt, JwtGuard };
@@ -0,0 +1,5 @@
1
+ import type { AbilityChecker } from "./abilityChecker";
2
+ import type { AuthUser } from "./authContext";
3
+ declare function tokenCan(user: AuthUser | null, ability: string): boolean;
4
+ declare function createTokenAbilityChecker(): AbilityChecker;
5
+ export { createTokenAbilityChecker, tokenCan };
@@ -5,6 +5,12 @@ declare const CACHE_TAGS: {
5
5
  readonly comments: "comments";
6
6
  readonly attachments: "attachments";
7
7
  readonly reports: "reports";
8
+ readonly users: "users";
9
+ readonly departments: "departments";
10
+ readonly positions: "positions";
11
+ readonly applications: "applications";
12
+ readonly careers: "careers";
13
+ readonly offers: "offers";
8
14
  };
9
15
  type CacheTag = (typeof CACHE_TAGS)[keyof typeof CACHE_TAGS];
10
16
  export type { CacheTag };
@@ -10,5 +10,9 @@ interface AuthUserDirectory {
10
10
  resolveUserFromToken(token: string): Promise<AuthUser | null>;
11
11
  findByIdOrThrow(id: number): Promise<AuthUserRecord>;
12
12
  hasActiveBrowserSession?(userId: number, issuedAt: number): Promise<boolean>;
13
+ findByEmail?(email: string): Promise<(AuthUserRecord & {
14
+ password?: string | null;
15
+ }) | null>;
16
+ verifyCredentials?(email: string, password: string): Promise<AuthUser | null>;
13
17
  }
14
18
  export type { AuthUserDirectory, AuthUserRecord };
@@ -1,5 +1,5 @@
1
1
  import { type CursorPaginatedResult, type PaginatedResult } from "../pagination/index.ts";
2
- import { type BelongsToManyRelation, type BelongsToRelation, type HasManyRelation, type MorphManyRelation, type MorphOneRelation, type MorphToRelation } from "./relationships.ts";
2
+ import { type BelongsToManyRelation, type BelongsToRelation, type HasManyRelation, type HasManyThroughRelation, type MorphManyRelation, type MorphOneRelation, type MorphToRelation } from "./relationships.ts";
3
3
  import { RepositoryQuery } from "./repositoryQuery.ts";
4
4
  import type { TableDefinition } from "./table.ts";
5
5
  import type { MutationValues, QueryOptions, QueryWhere, UpdateValues } from "./types.ts";
@@ -59,6 +59,10 @@ declare class BaseRepository<TEntity extends object, PrimaryKey extends keyof TE
59
59
  }>>;
60
60
  findByHasManyRelation<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, parentId: TParent[LocalKey], options?: Omit<QueryOptions<TEntity>, "where">): Promise<TEntity[]>;
61
61
  loadHasManyForParents<TParent extends object, LocalKey extends keyof TParent & string, ForeignKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyRelation<TParent, TEntity, LocalKey, ForeignKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
62
+ findHasManyThrough<TParent extends object, LocalKey extends keyof TParent & string, FirstKey extends string, SecondLocalKey extends string, SecondKey extends keyof TEntity & string>(parentId: unknown, relation: HasManyThroughRelation<TParent, TEntity, LocalKey, FirstKey, SecondLocalKey, SecondKey>, options?: QueryOptions<TEntity>): Promise<TEntity[]>;
63
+ loadHasManyThroughForParents<TParent extends object, LocalKey extends keyof TParent & string, FirstKey extends string, SecondLocalKey extends string, SecondKey extends keyof TEntity & string>(parents: readonly TParent[], relation: HasManyThroughRelation<TParent, TEntity, LocalKey, FirstKey, SecondLocalKey, SecondKey>, options?: QueryOptions<TEntity>): Promise<Map<TParent[LocalKey], TEntity[]>>;
64
+ private throughSoftDeleteClause;
65
+ private buildThroughWhere;
62
66
  loadBelongsToForParents<TChild extends object, TParent extends object, ForeignKey extends keyof TChild & string, OwnerKey extends keyof TParent & string>(children: readonly TChild[], relation: BelongsToRelation<TChild, TParent, ForeignKey, OwnerKey>, parentRepository: BaseRepository<TParent, OwnerKey>, options?: Omit<QueryOptions<TParent>, "where">): Promise<Map<TChild[ForeignKey], TParent>>;
63
67
  loadMorphManyForParents<TParent extends object, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TEntity & string, MorphIdKey extends keyof TEntity & string>(parents: readonly TParent[], relation: MorphManyRelation<TParent, TEntity, LocalKey, MorphTypeKey, MorphIdKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity[]>>;
64
68
  loadMorphOneForParents<TParent extends object, LocalKey extends keyof TParent & string, MorphTypeKey extends keyof TEntity & string, MorphIdKey extends keyof TEntity & string>(parents: readonly TParent[], relation: MorphOneRelation<TParent, TEntity, LocalKey, MorphTypeKey, MorphIdKey>, options?: Omit<QueryOptions<TEntity>, "where">): Promise<Map<TParent[LocalKey], TEntity | undefined>>;
@@ -0,0 +1,18 @@
1
+ import { type DatabaseDriver } from "./schema/driver.ts";
2
+ interface SqlDialect {
3
+ readonly driver: DatabaseDriver;
4
+ placeholder(index: number): string;
5
+ quoteIdentifier(identifier: string): string;
6
+ nowExpression(): string;
7
+ returningClause(columns: string): string;
8
+ ilikeOperator(): "ILIKE" | "LIKE";
9
+ nullsLastSuffix(): string;
10
+ castToText(expression: string): string;
11
+ }
12
+ declare function dialectFor(driver: DatabaseDriver): SqlDialect;
13
+ declare function currentSqlDialect(): SqlDialect;
14
+ declare function useSqlDialect(driver: DatabaseDriver): SqlDialect;
15
+ declare function resetSqlDialect(): void;
16
+ declare function runWithSqlDialect<T>(driver: DatabaseDriver, callback: () => T | Promise<T>): T | Promise<T>;
17
+ export type { SqlDialect };
18
+ export { currentSqlDialect, dialectFor, resetSqlDialect, runWithSqlDialect, useSqlDialect };
@@ -44,6 +44,7 @@ declare class Factory<TRecord extends object, Counted extends boolean = false> {
44
44
  create(overrides?: Partial<TRecord>): Promise<FactoryMakeResult<TRecord, Counted>>;
45
45
  protected makeOne(overrides?: Partial<TRecord>): TRecord;
46
46
  protected createOne(overrides?: Partial<TRecord>): Promise<TRecord>;
47
+ protected persistCreated(record: TRecord): Promise<TRecord>;
47
48
  protected insertable(record: TRecord): Partial<TRecord>;
48
49
  protected persist(values: Partial<TRecord>): Promise<TRecord>;
49
50
  }
@@ -1,17 +1,25 @@
1
1
  export type { DatabaseConnection } from "./baseRepository.ts";
2
2
  export { default as BaseRepository } from "./baseRepository.ts";
3
3
  export { createDatabaseConnection } from "./connection.ts";
4
+ export type { ActiveDatabaseHandle } from "./connectionContext.ts";
5
+ export { getActiveDatabaseConnection, hasActiveDatabaseConnection, runWithDatabaseConnection, } from "./connectionContext.ts";
6
+ export type { SqlDialect } from "./dialect.ts";
7
+ export { currentSqlDialect, dialectFor, resetSqlDialect, runWithSqlDialect, useSqlDialect, } from "./dialect.ts";
4
8
  export { mapDatabaseError, withDatabaseErrorHandling } from "./errors.ts";
5
9
  export { Factory } from "./factory.ts";
6
10
  export { foreignKeyFromTable, pivotTableName, singularize } from "./inflection.ts";
7
11
  export type { CastType, GlobalScopeFn, ModelConstructor } from "./model.ts";
8
- export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasOneRelationQuery, hydrateValue, Model, ModelQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelClass, registerModelRepository, } from "./model.ts";
12
+ export { applyCasts, BelongsToManyRelationQuery, BelongsToRelationQuery, dehydrateValue, filterMassAssignable, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, hydrateValue, Model, ModelQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, registerModelClass, registerModelRepository, } from "./model.ts";
13
+ export { createMysqlConnection, createMysqlConnectionFromPool } from "./mysqlConnection.ts";
14
+ export type { NamedConnectionEntry } from "./namedConnections.ts";
15
+ export { getNamedConnection, hasNamedConnection, registerNamedConnection, resetNamedConnections, runOnNamedConnection, unregisterNamedConnection, } from "./namedConnections.ts";
9
16
  export { buildAdvancedWhereClause, buildCountQuery, buildDeleteByIdQuery, buildGroupedCountQuery, buildInsertQuery, buildJoinClause, buildOrderByClause, buildProjectionQuery, buildQueryWhereClause, buildRestoreByIdQuery, buildSelectQuery, buildSoftDeleteByIdQuery, buildUpdateQuery, buildWhereClause, parseQualifiedColumn, qualifyColumn, quoteIdentifier, resolveQualifiedColumn, resolveSoftDeleteColumn, } from "./query.ts";
10
- export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "./relationships.ts";
11
- export { belongsTo, belongsToMany, hasMany, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "./relationships.ts";
17
+ export type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasManyThroughRelation, HasOneRelation, MorphManyRelation, MorphOneRelation, MorphToRelation, } from "./relationships.ts";
18
+ export { belongsTo, belongsToMany, hasMany, hasManyThrough, hasOne, indexBelongsToManyRelation, indexBelongsToRelation, indexHasManyRelation, indexHasManyThroughRelation, indexHasOneRelation, indexMorphManyRelation, indexMorphOneRelation, indexMorphToRelation, morphMany, morphOne, morphTo, } from "./relationships.ts";
12
19
  export { RepositoryQuery } from "./repositoryQuery.ts";
13
20
  export type { BlueprintAction, BlueprintCallback, ColumnKind, DatabaseDriver, ForeignKeyOptions, Grammar, IndexDefinition, IndexKind, ResolveDatabaseDriverOptions, SchemaBuilder, } from "./schema/index.ts";
14
21
  export { Blueprint, ColumnDefinition, compileBlueprint, createSchemaBuilder, ForeignIdColumnDefinition, grammarForDriver, inferReferencedTable, MySqlGrammar, PostgresGrammar, resolveDatabaseDriver, Schema, SqliteGrammar, UnsupportedSchemaFeatureError, } from "./schema/index.ts";
22
+ export { createSqliteConnection } from "./sqliteConnection.ts";
15
23
  export type { TableDefinition } from "./table.ts";
16
24
  export { defineTable } from "./table.ts";
17
25
  export { runInTransaction } from "./transaction.ts";
@@ -1,6 +1,6 @@
1
1
  import type BaseRepository from "./baseRepository.ts";
2
2
  import type { AnyRelationQuery, RelatedModelClass } from "./relationQuery.ts";
3
- import { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery } from "./relationQuery.ts";
3
+ import { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery } from "./relationQuery.ts";
4
4
  import type { BelongsToManyRelation, BelongsToRelation, HasManyRelation, HasOneRelation } from "./relationships.ts";
5
5
  import type { RepositoryQuery } from "./repositoryQuery.ts";
6
6
  import type { QueryOptions, QueryWhere } from "./types.ts";
@@ -56,6 +56,9 @@ declare class ModelQuery {
56
56
  withMorphMany(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphMany"]>): this;
57
57
  withMorphOne(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphOne"]>): this;
58
58
  withMorphTo(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withMorphTo"]>): this;
59
+ withHasManyThrough(...args: Parameters<RepositoryQuery<Record<string, unknown>, "id">["withHasManyThrough"]>): this;
60
+ withTrashed(): this;
61
+ onlyTrashed(): this;
59
62
  get(): Promise<Array<Model<Record<string, unknown>, "id">>>;
60
63
  first(): Promise<Model<Record<string, unknown>, "id"> | null>;
61
64
  find(id: unknown): Promise<Model<Record<string, unknown>, "id"> | null>;
@@ -103,6 +106,21 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
103
106
  static newFromRecord(this: object, record: object, exists?: boolean): Model<Record<string, unknown>, "id">;
104
107
  static create(this: object, attributes: Record<string, unknown>, forced?: Record<string, unknown>): Promise<Model<Record<string, unknown>, "id">>;
105
108
  static with(this: object, ...relations: string[]): ModelQuery;
109
+ static withTrashed(this: object): ModelQuery;
110
+ static onlyTrashed(this: object): ModelQuery;
111
+ static chunk(this: object, count: number, callback: (models: Array<Model<Record<string, unknown>, "id">>) => Promise<boolean | void>): Promise<void>;
112
+ static cursorPaginate(this: object, options: {
113
+ perPage: number;
114
+ cursor?: unknown;
115
+ }): Promise<{
116
+ data: Array<Model<Record<string, unknown>, "id">>;
117
+ meta: {
118
+ per_page: number;
119
+ next_cursor: unknown;
120
+ prev_cursor: unknown;
121
+ has_more: boolean;
122
+ };
123
+ }>;
106
124
  static whereHas(this: object, name: string, constrain?: (query: AnyRelationQuery) => void): ModelQuery;
107
125
  static has(this: object, name: string): ModelQuery;
108
126
  static doesntHave(this: object, name: string): ModelQuery;
@@ -127,6 +145,7 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
127
145
  hasMany<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TRelated & string, localKey?: PrimaryKey): HasManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
128
146
  hasOne<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TRelated & string, localKey?: PrimaryKey): HasOneRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
129
147
  belongsTo<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, foreignKey?: keyof TEntity & string, ownerKey?: RelatedKey): BelongsToRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
148
+ hasManyThrough<TRelated extends object, TThrough extends object, RelatedKey extends keyof TRelated & string, ThroughKey extends keyof TThrough & string>(related: RelatedRef<TRelated, RelatedKey>, through: RelatedRef<TThrough, ThroughKey>, firstKey?: string, secondKey?: keyof TRelated & string, localKey?: PrimaryKey, secondLocalKey?: ThroughKey): HasManyThroughRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
130
149
  belongsToMany<TRelated extends object, RelatedKey extends keyof TRelated & string, Pivot extends object = Record<string, unknown>>(related: RelatedRef<TRelated, RelatedKey>, pivotTable?: string, foreignPivotKey?: keyof Pivot & string, relatedPivotKey?: keyof Pivot & string): BelongsToManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey, Pivot>;
131
150
  morphMany<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, morphName: string, typeKey?: keyof TRelated & string, idKey?: keyof TRelated & string, morphType?: string): MorphManyRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
132
151
  morphOne<TRelated extends object, RelatedKey extends keyof TRelated & string>(related: RelatedRef<TRelated, RelatedKey>, morphName: string, typeKey?: keyof TRelated & string, idKey?: keyof TRelated & string, morphType?: string): MorphOneRelationQuery<TEntity, PrimaryKey, TRelated, RelatedKey>;
@@ -138,6 +157,6 @@ declare class Model<TEntity extends object, PrimaryKey extends keyof TEntity & s
138
157
  }
139
158
  /** Binds a Model class to its table/connection and names it for `hasMany("User")`. */
140
159
  declare function registerModelRepository<TModelClass>(model: TModelClass, repository: object): TModelClass;
141
- export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, } from "./relationQuery.ts";
160
+ export { BelongsToManyRelationQuery, BelongsToRelationQuery, HasManyRelationQuery, HasManyThroughRelationQuery, HasOneRelationQuery, MorphManyRelationQuery, MorphOneRelationQuery, MorphToRelationQuery, } from "./relationQuery.ts";
142
161
  export type { CastType, GlobalScopeFn, ModelClassType, ModelConstructor };
143
162
  export { applyCasts, dehydrateValue, filterMassAssignable, hydrateValue, Model, ModelQuery, registerModelClass, registerModelRepository, };
@@ -0,0 +1,12 @@
1
+ import type { ActiveDatabaseHandle } from "./connectionContext.ts";
2
+ type MysqlExecutable = {
3
+ execute: (sql: string, params?: unknown[]) => Promise<[unknown, unknown]>;
4
+ end?: () => Promise<void>;
5
+ };
6
+ type MysqlConnection = ActiveDatabaseHandle & {
7
+ close(): Promise<void>;
8
+ };
9
+ declare function createMysqlConnectionFromPool(pool: MysqlExecutable): MysqlConnection;
10
+ declare function createMysqlConnection(url: string): MysqlConnection;
11
+ export type { MysqlConnection, MysqlExecutable };
12
+ export { createMysqlConnection, createMysqlConnectionFromPool };
@@ -0,0 +1,15 @@
1
+ import { type ActiveDatabaseHandle } from "./connectionContext.ts";
2
+ import type { DatabaseDriver } from "./schema/driver.ts";
3
+ type NamedConnectionEntry = {
4
+ name: string;
5
+ driver: DatabaseDriver;
6
+ connection: ActiveDatabaseHandle;
7
+ };
8
+ declare function registerNamedConnection(name: string, driver: DatabaseDriver, connection: ActiveDatabaseHandle): void;
9
+ declare function unregisterNamedConnection(name: string): boolean;
10
+ declare function hasNamedConnection(name: string): boolean;
11
+ declare function getNamedConnection(name: string): NamedConnectionEntry;
12
+ declare function resetNamedConnections(): void;
13
+ declare function runOnNamedConnection<T>(name: string, callback: () => T | Promise<T>): T | Promise<T>;
14
+ export type { NamedConnectionEntry };
15
+ export { getNamedConnection, hasNamedConnection, registerNamedConnection, resetNamedConnections, runOnNamedConnection, unregisterNamedConnection, };