boltstore 0.5.1 → 0.6.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.
- package/README.md +65 -23
- package/dist/admin/api-keys.d.ts +87 -0
- package/dist/admin/api-keys.d.ts.map +1 -0
- package/dist/admin/api-keys.js +267 -0
- package/dist/admin/api-keys.js.map +1 -0
- package/dist/admin/backup.d.ts +76 -0
- package/dist/admin/backup.d.ts.map +1 -0
- package/dist/admin/backup.js +255 -0
- package/dist/admin/backup.js.map +1 -0
- package/dist/admin/import-export/csv.d.ts +4 -0
- package/dist/admin/import-export/csv.d.ts.map +1 -0
- package/dist/admin/import-export/csv.js +123 -0
- package/dist/admin/import-export/csv.js.map +1 -0
- package/dist/admin/import-export/export.d.ts +4 -0
- package/dist/admin/import-export/export.d.ts.map +1 -0
- package/dist/admin/import-export/export.js +66 -0
- package/dist/admin/import-export/export.js.map +1 -0
- package/dist/admin/import-export/import.d.ts +4 -0
- package/dist/admin/import-export/import.d.ts.map +1 -0
- package/dist/admin/import-export/import.js +253 -0
- package/dist/admin/import-export/import.js.map +1 -0
- package/dist/admin/import-export/index.d.ts +7 -0
- package/dist/admin/import-export/index.d.ts.map +1 -0
- package/dist/admin/import-export/index.js +7 -0
- package/dist/admin/import-export/index.js.map +1 -0
- package/dist/admin/import-export/json-input.d.ts +2 -0
- package/dist/admin/import-export/json-input.d.ts.map +1 -0
- package/dist/admin/import-export/json-input.js +42 -0
- package/dist/admin/import-export/json-input.js.map +1 -0
- package/dist/admin/import-export/schema-inference.d.ts +6 -0
- package/dist/admin/import-export/schema-inference.d.ts.map +1 -0
- package/dist/admin/import-export/schema-inference.js +34 -0
- package/dist/admin/import-export/schema-inference.js.map +1 -0
- package/dist/admin/import-export/types.d.ts +40 -0
- package/dist/admin/import-export/types.d.ts.map +1 -0
- package/dist/admin/import-export/types.js +2 -0
- package/dist/admin/import-export/types.js.map +1 -0
- package/dist/admin/oauth/github.d.ts +12 -0
- package/dist/admin/oauth/github.d.ts.map +1 -0
- package/dist/admin/oauth/github.js +78 -0
- package/dist/admin/oauth/github.js.map +1 -0
- package/dist/admin/oauth/google.d.ts +12 -0
- package/dist/admin/oauth/google.d.ts.map +1 -0
- package/dist/admin/oauth/google.js +55 -0
- package/dist/admin/oauth/google.js.map +1 -0
- package/dist/admin/oauth/handler.d.ts +5 -0
- package/dist/admin/oauth/handler.d.ts.map +1 -0
- package/dist/admin/oauth/handler.js +37 -0
- package/dist/admin/oauth/handler.js.map +1 -0
- package/dist/admin/oauth/index.d.ts +5 -0
- package/dist/admin/oauth/index.d.ts.map +1 -0
- package/dist/admin/oauth/index.js +5 -0
- package/dist/admin/oauth/index.js.map +1 -0
- package/dist/admin/oauth/registry.d.ts +16 -0
- package/dist/admin/oauth/registry.d.ts.map +1 -0
- package/dist/admin/oauth/registry.js +37 -0
- package/dist/admin/oauth/registry.js.map +1 -0
- package/dist/admin/oauth/types.d.ts +14 -0
- package/dist/admin/oauth/types.d.ts.map +1 -0
- package/dist/admin/oauth/types.js +2 -0
- package/dist/admin/oauth/types.js.map +1 -0
- package/dist/admin/oauth/user.d.ts +12 -0
- package/dist/admin/oauth/user.d.ts.map +1 -0
- package/dist/admin/oauth/user.js +28 -0
- package/dist/admin/oauth/user.js.map +1 -0
- package/dist/admin/query.d.ts +54 -0
- package/dist/admin/query.d.ts.map +1 -0
- package/dist/admin/query.js +168 -0
- package/dist/admin/query.js.map +1 -0
- package/dist/admin/transaction.d.ts +46 -0
- package/dist/admin/transaction.d.ts.map +1 -0
- package/dist/admin/transaction.js +52 -0
- package/dist/admin/transaction.js.map +1 -0
- package/dist/admin/views.d.ts +64 -0
- package/dist/admin/views.d.ts.map +1 -0
- package/dist/admin/views.js +197 -0
- package/dist/admin/views.js.map +1 -0
- package/dist/audit.d.ts +36 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +92 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/jwt.d.ts +8 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +81 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/password.d.ts +4 -0
- package/dist/auth/password.d.ts.map +1 -0
- package/dist/auth/password.js +15 -0
- package/dist/auth/password.js.map +1 -0
- package/dist/auth/tables.d.ts +5 -0
- package/dist/auth/tables.d.ts.map +1 -0
- package/dist/auth/tables.js +65 -0
- package/dist/auth/tables.js.map +1 -0
- package/dist/auth/tokens.d.ts +16 -0
- package/dist/auth/tokens.d.ts.map +1 -0
- package/dist/auth/tokens.js +126 -0
- package/dist/auth/tokens.js.map +1 -0
- package/dist/auth/types.d.ts +36 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +2 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/auth/users.d.ts +9 -0
- package/dist/auth/users.d.ts.map +1 -0
- package/dist/auth/users.js +83 -0
- package/dist/auth/users.js.map +1 -0
- package/dist/auth/validation.d.ts +5 -0
- package/dist/auth/validation.d.ts.map +1 -0
- package/dist/auth/validation.js +25 -0
- package/dist/auth/validation.js.map +1 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +9 -0
- package/dist/auth.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +322 -0
- package/dist/cli.js.map +1 -0
- package/dist/collections/ddl.d.ts +25 -0
- package/dist/collections/ddl.d.ts.map +1 -0
- package/dist/collections/ddl.js +83 -0
- package/dist/collections/ddl.js.map +1 -0
- package/dist/collections/management.d.ts +57 -0
- package/dist/collections/management.d.ts.map +1 -0
- package/dist/collections/management.js +289 -0
- package/dist/collections/management.js.map +1 -0
- package/dist/collections.d.ts +3 -0
- package/dist/collections.d.ts.map +1 -0
- package/dist/collections.js +3 -0
- package/dist/collections.js.map +1 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +274 -0
- package/dist/config.js.map +1 -0
- package/dist/db/cast.d.ts +15 -0
- package/dist/db/cast.d.ts.map +1 -0
- package/dist/db/cast.js +16 -0
- package/dist/db/cast.js.map +1 -0
- package/dist/db/manager.d.ts +93 -0
- package/dist/db/manager.d.ts.map +1 -0
- package/dist/db/manager.js +209 -0
- package/dist/db/manager.js.map +1 -0
- package/dist/db/pool.d.ts +99 -0
- package/dist/db/pool.d.ts.map +1 -0
- package/dist/db/pool.js +232 -0
- package/dist/db/pool.js.map +1 -0
- package/dist/entry.d.ts +11 -0
- package/dist/entry.d.ts.map +1 -0
- package/dist/entry.js +80 -0
- package/dist/entry.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/indexes.d.ts +61 -0
- package/dist/indexes.d.ts.map +1 -0
- package/dist/indexes.js +174 -0
- package/dist/indexes.js.map +1 -0
- package/dist/logger.d.ts +40 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +147 -0
- package/dist/logger.js.map +1 -0
- package/dist/middleware/auth.d.ts +43 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +87 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/cors.d.ts +28 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +71 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/proxy.d.ts +10 -0
- package/dist/middleware/proxy.d.ts.map +1 -0
- package/dist/middleware/proxy.js +31 -0
- package/dist/middleware/proxy.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +64 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +130 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/migrations.d.ts +56 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +133 -0
- package/dist/migrations.js.map +1 -0
- package/dist/query/builder.d.ts +6 -0
- package/dist/query/builder.d.ts.map +1 -0
- package/dist/query/builder.js +97 -0
- package/dist/query/builder.js.map +1 -0
- package/dist/query/executor.d.ts +3 -0
- package/dist/query/executor.d.ts.map +1 -0
- package/dist/query/executor.js +64 -0
- package/dist/query/executor.js.map +1 -0
- package/dist/query/filter-builder.d.ts +8 -0
- package/dist/query/filter-builder.d.ts.map +1 -0
- package/dist/query/filter-builder.js +128 -0
- package/dist/query/filter-builder.js.map +1 -0
- package/dist/query/search.d.ts +4 -0
- package/dist/query/search.d.ts.map +1 -0
- package/dist/query/search.js +28 -0
- package/dist/query/search.js.map +1 -0
- package/dist/query/types.d.ts +45 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +2 -0
- package/dist/query/types.js.map +1 -0
- package/dist/query.d.ts +6 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +6 -0
- package/dist/query.js.map +1 -0
- package/dist/records/batch.d.ts +13 -0
- package/dist/records/batch.d.ts.map +1 -0
- package/dist/records/batch.js +113 -0
- package/dist/records/batch.js.map +1 -0
- package/dist/records/count.d.ts +5 -0
- package/dist/records/count.d.ts.map +1 -0
- package/dist/records/count.js +34 -0
- package/dist/records/count.js.map +1 -0
- package/dist/records/crud.d.ts +8 -0
- package/dist/records/crud.d.ts.map +1 -0
- package/dist/records/crud.js +121 -0
- package/dist/records/crud.js.map +1 -0
- package/dist/records/distinct.d.ts +5 -0
- package/dist/records/distinct.d.ts.map +1 -0
- package/dist/records/distinct.js +22 -0
- package/dist/records/distinct.js.map +1 -0
- package/dist/records/helpers.d.ts +4 -0
- package/dist/records/helpers.d.ts.map +1 -0
- package/dist/records/helpers.js +9 -0
- package/dist/records/helpers.js.map +1 -0
- package/dist/records/list.d.ts +44 -0
- package/dist/records/list.d.ts.map +1 -0
- package/dist/records/list.js +133 -0
- package/dist/records/list.js.map +1 -0
- package/dist/records/schema-cache.d.ts +8 -0
- package/dist/records/schema-cache.d.ts.map +1 -0
- package/dist/records/schema-cache.js +64 -0
- package/dist/records/schema-cache.js.map +1 -0
- package/dist/records.d.ts +8 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +8 -0
- package/dist/records.js.map +1 -0
- package/dist/relations.d.ts +50 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/relations.js +179 -0
- package/dist/relations.js.map +1 -0
- package/dist/rls.d.ts +63 -0
- package/dist/rls.d.ts.map +1 -0
- package/dist/rls.js +146 -0
- package/dist/rls.js.map +1 -0
- package/dist/router.d.ts +53 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +104 -0
- package/dist/router.js.map +1 -0
- package/dist/routes/admin-query.d.ts +5 -0
- package/dist/routes/admin-query.d.ts.map +1 -0
- package/dist/routes/admin-query.js +57 -0
- package/dist/routes/admin-query.js.map +1 -0
- package/dist/routes/api-keys.d.ts +12 -0
- package/dist/routes/api-keys.d.ts.map +1 -0
- package/dist/routes/api-keys.js +114 -0
- package/dist/routes/api-keys.js.map +1 -0
- package/dist/routes/auth.d.ts +10 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +116 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/backup.d.ts +5 -0
- package/dist/routes/backup.d.ts.map +1 -0
- package/dist/routes/backup.js +88 -0
- package/dist/routes/backup.js.map +1 -0
- package/dist/routes/collections.d.ts +5 -0
- package/dist/routes/collections.d.ts.map +1 -0
- package/dist/routes/collections.js +110 -0
- package/dist/routes/collections.js.map +1 -0
- package/dist/routes/databases.d.ts +5 -0
- package/dist/routes/databases.d.ts.map +1 -0
- package/dist/routes/databases.js +90 -0
- package/dist/routes/databases.js.map +1 -0
- package/dist/routes/health.d.ts +9 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +34 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/import-export.d.ts +8 -0
- package/dist/routes/import-export.d.ts.map +1 -0
- package/dist/routes/import-export.js +173 -0
- package/dist/routes/import-export.js.map +1 -0
- package/dist/routes/indexes.d.ts +5 -0
- package/dist/routes/indexes.d.ts.map +1 -0
- package/dist/routes/indexes.js +94 -0
- package/dist/routes/indexes.js.map +1 -0
- package/dist/routes/migrations.d.ts +5 -0
- package/dist/routes/migrations.d.ts.map +1 -0
- package/dist/routes/migrations.js +87 -0
- package/dist/routes/migrations.js.map +1 -0
- package/dist/routes/oauth.d.ts +5 -0
- package/dist/routes/oauth.d.ts.map +1 -0
- package/dist/routes/oauth.js +35 -0
- package/dist/routes/oauth.js.map +1 -0
- package/dist/routes/query.d.ts +5 -0
- package/dist/routes/query.d.ts.map +1 -0
- package/dist/routes/query.js +36 -0
- package/dist/routes/query.js.map +1 -0
- package/dist/routes/records.d.ts +5 -0
- package/dist/routes/records.d.ts.map +1 -0
- package/dist/routes/records.js +181 -0
- package/dist/routes/records.js.map +1 -0
- package/dist/routes/transactions.d.ts +5 -0
- package/dist/routes/transactions.d.ts.map +1 -0
- package/dist/routes/transactions.js +43 -0
- package/dist/routes/transactions.js.map +1 -0
- package/dist/routes/views.d.ts +5 -0
- package/dist/routes/views.d.ts.map +1 -0
- package/dist/routes/views.js +123 -0
- package/dist/routes/views.js.map +1 -0
- package/dist/server.d.ts +80 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +271 -0
- package/dist/server.js.map +1 -0
- package/package.json +20 -40
- package/bin/cli.ts +0 -185
- package/src/admin/assets.ts +0 -134
- package/src/admin/serve.ts +0 -447
- package/src/admin-ui/App.vue +0 -71
- package/src/admin-ui/api/client.ts +0 -179
- package/src/admin-ui/components/Icon.vue +0 -85
- package/src/admin-ui/components/Sidebar.vue +0 -275
- package/src/admin-ui/components/StatCard.vue +0 -40
- package/src/admin-ui/dist/assets/ApiKeys.css +0 -1
- package/src/admin-ui/dist/assets/ApiKeys.js +0 -1
- package/src/admin-ui/dist/assets/AppDashboard.css +0 -1
- package/src/admin-ui/dist/assets/AppDashboard.js +0 -1
- package/src/admin-ui/dist/assets/Backups.js +0 -1
- package/src/admin-ui/dist/assets/Dashboard.css +0 -1
- package/src/admin-ui/dist/assets/Dashboard.js +0 -3
- package/src/admin-ui/dist/assets/DataBrowser.css +0 -1
- package/src/admin-ui/dist/assets/DataBrowser.js +0 -1
- package/src/admin-ui/dist/assets/Docs.css +0 -1
- package/src/admin-ui/dist/assets/Docs.js +0 -1
- package/src/admin-ui/dist/assets/FileBrowser.css +0 -1
- package/src/admin-ui/dist/assets/FileBrowser.js +0 -1
- package/src/admin-ui/dist/assets/HookEditor.css +0 -1
- package/src/admin-ui/dist/assets/HookEditor.js +0 -2
- package/src/admin-ui/dist/assets/LogViewer.css +0 -1
- package/src/admin-ui/dist/assets/LogViewer.js +0 -1
- package/src/admin-ui/dist/assets/Login.css +0 -1
- package/src/admin-ui/dist/assets/Login.js +0 -1
- package/src/admin-ui/dist/assets/SchemaBuilder.css +0 -1
- package/src/admin-ui/dist/assets/SchemaBuilder.js +0 -1
- package/src/admin-ui/dist/assets/Settings.js +0 -1
- package/src/admin-ui/dist/assets/Setup.css +0 -1
- package/src/admin-ui/dist/assets/Setup.js +0 -1
- package/src/admin-ui/dist/assets/StatCard.css +0 -1
- package/src/admin-ui/dist/assets/StatCard.js +0 -1
- package/src/admin-ui/dist/assets/main.css +0 -1
- package/src/admin-ui/dist/assets/main.js +0 -30
- package/src/admin-ui/dist/index.html +0 -49
- package/src/admin-ui/index.html +0 -48
- package/src/admin-ui/main.ts +0 -25
- package/src/admin-ui/pages/ApiKeys.vue +0 -198
- package/src/admin-ui/pages/AppDashboard.vue +0 -274
- package/src/admin-ui/pages/Applications.vue +0 -123
- package/src/admin-ui/pages/Backups.vue +0 -140
- package/src/admin-ui/pages/Dashboard.vue +0 -293
- package/src/admin-ui/pages/DataBrowser.vue +0 -310
- package/src/admin-ui/pages/Docs.vue +0 -40
- package/src/admin-ui/pages/FileBrowser.vue +0 -587
- package/src/admin-ui/pages/HookEditor.vue +0 -258
- package/src/admin-ui/pages/LogViewer.vue +0 -109
- package/src/admin-ui/pages/Login.vue +0 -229
- package/src/admin-ui/pages/SchemaBuilder.vue +0 -501
- package/src/admin-ui/pages/Settings.vue +0 -251
- package/src/admin-ui/pages/Setup.vue +0 -275
- package/src/admin-ui/router.ts +0 -169
- package/src/admin-ui/stores/application.ts +0 -68
- package/src/admin-ui/stores/auth.ts +0 -109
- package/src/admin-ui/style.css +0 -530
- package/src/admin-ui/tsconfig.json +0 -21
- package/src/admin-ui/version.ts +0 -3
- package/src/admin-ui/vite.config.ts +0 -35
- package/src/api/admin-config.ts +0 -596
- package/src/api/admin.ts +0 -8
- package/src/api/aggregate.ts +0 -104
- package/src/api/applications.ts +0 -148
- package/src/api/auth.ts +0 -690
- package/src/api/backups.ts +0 -173
- package/src/api/batch.ts +0 -135
- package/src/api/collections.ts +0 -354
- package/src/api/files.ts +0 -801
- package/src/api/health.ts +0 -48
- package/src/api/helpers.ts +0 -86
- package/src/api/middleware/auth.ts +0 -202
- package/src/api/middleware/cors.ts +0 -37
- package/src/api/middleware/logging.ts +0 -27
- package/src/api/middleware/ratelimit.ts +0 -142
- package/src/api/middleware/rls.ts +0 -37
- package/src/api/records.ts +0 -563
- package/src/api/router.ts +0 -296
- package/src/app.ts +0 -617
- package/src/auth/apikey.ts +0 -73
- package/src/auth/jwt.ts +0 -210
- package/src/auth/oauth2.ts +0 -144
- package/src/auth/password.ts +0 -68
- package/src/backup/manager.ts +0 -227
- package/src/config/yaml.ts +0 -78
- package/src/config.ts +0 -240
- package/src/db/application-migrations.ts +0 -112
- package/src/db/application-schema.ts +0 -297
- package/src/db/migrations.ts +0 -54
- package/src/db/pool.ts +0 -143
- package/src/db/system-schema.ts +0 -153
- package/src/hooks/api.ts +0 -157
- package/src/hooks/loader.ts +0 -263
- package/src/hooks/runner.ts +0 -76
- package/src/index.ts +0 -17
- package/src/logging/logger.ts +0 -131
- package/src/realtime/broadcast.ts +0 -132
- package/src/realtime/subscriptions.ts +0 -124
- package/src/security/rls-engine.ts +0 -183
- package/src/security/sanitizer.ts +0 -72
- package/src/storage/interface.ts +0 -48
- package/src/storage/local.ts +0 -189
- package/src/storage/mime-types.ts +0 -32
- package/src/storage/s3.ts +0 -213
- package/src/sync/changelog.ts +0 -247
- package/src/sync/engine.ts +0 -159
- package/src/sync/lamport.ts +0 -48
- package/src/sync/protocol.ts +0 -234
- package/src/version.ts +0 -2
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { validateIdentifier } from "@boltstore/utils";
|
|
2
|
+
import { toBindings } from "../db/cast";
|
|
3
|
+
import { applyRLS, toRLSContext } from "../rls";
|
|
4
|
+
import { getColumnNames } from "./schema-cache";
|
|
5
|
+
function distinctValues(pool, collection, field, auth) {
|
|
6
|
+
validateIdentifier(field, "field name");
|
|
7
|
+
getColumnNames(pool, collection);
|
|
8
|
+
const db = pool.read();
|
|
9
|
+
const rlsCtx = auth ? toRLSContext(auth) : null;
|
|
10
|
+
const rls = rlsCtx ? applyRLS(pool, collection, "read", rlsCtx) : null;
|
|
11
|
+
let sql = `SELECT DISTINCT "${field}" FROM "${collection}"`;
|
|
12
|
+
const params = [];
|
|
13
|
+
if (rls?.whereClause) {
|
|
14
|
+
sql += ` WHERE ${rls.whereClause}`;
|
|
15
|
+
params.push(...rls.params);
|
|
16
|
+
}
|
|
17
|
+
sql += ` ORDER BY "${field}"`;
|
|
18
|
+
const rows = db.query(sql).all(...toBindings(params));
|
|
19
|
+
return rows.map((r) => r[field]);
|
|
20
|
+
}
|
|
21
|
+
export { distinctValues };
|
|
22
|
+
//# sourceMappingURL=distinct.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"distinct.js","sourceRoot":"","sources":["../../src/records/distinct.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAmB,MAAM,QAAQ,CAAC;AAEjE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,SAAS,cAAc,CACrB,IAAkB,EAClB,UAAkB,EAClB,KAAa,EACb,IAAkB;IAElB,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;IACxC,cAAc,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IACjC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAEvB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEvE,IAAI,GAAG,GAAG,oBAAoB,KAAK,WAAW,UAAU,GAAG,CAAC;IAC5D,MAAM,MAAM,GAAc,EAAE,CAAC;IAC7B,IAAI,GAAG,EAAE,WAAW,EAAE,CAAC;QACrB,GAAG,IAAI,UAAU,GAAG,CAAC,WAAW,EAAE,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IAC7B,CAAC;IACD,GAAG,IAAI,cAAc,KAAK,GAAG,CAAC;IAE9B,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,CAA8B,CAAC;IAEnF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/records/helpers.ts"],"names":[],"mappings":"AAEA,iBAAS,UAAU,IAAI,MAAM,CAE5B;AAED,iBAAS,GAAG,IAAI,MAAM,CAErB;AAED,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/records/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,SAAS,UAAU;IACjB,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { DatabasePool } from "../db/pool";
|
|
2
|
+
import type { AuthContext } from "../middleware/auth";
|
|
3
|
+
interface ListRecordsResult {
|
|
4
|
+
records: Record<string, unknown>[];
|
|
5
|
+
meta: {
|
|
6
|
+
total?: number;
|
|
7
|
+
page?: number;
|
|
8
|
+
per_page?: number;
|
|
9
|
+
total_pages?: number;
|
|
10
|
+
next_cursor?: string | null;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
interface PaginationMetaOptions {
|
|
14
|
+
page: number;
|
|
15
|
+
perPage: number;
|
|
16
|
+
filter?: Record<string, unknown>;
|
|
17
|
+
sort?: string;
|
|
18
|
+
}
|
|
19
|
+
declare function listRecords(pool: DatabasePool, collection: string, options?: {
|
|
20
|
+
filter?: Record<string, unknown>;
|
|
21
|
+
sort?: string;
|
|
22
|
+
direction?: "asc" | "desc";
|
|
23
|
+
limit?: number;
|
|
24
|
+
offset?: number;
|
|
25
|
+
page?: number;
|
|
26
|
+
perPage?: number;
|
|
27
|
+
cursor?: string;
|
|
28
|
+
fields?: string[];
|
|
29
|
+
}, auth?: AuthContext): Record<string, unknown>[];
|
|
30
|
+
declare function buildListSql(collection: string, options?: {
|
|
31
|
+
filter?: Record<string, unknown>;
|
|
32
|
+
sort?: string;
|
|
33
|
+
direction?: "asc" | "desc";
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
cursor?: string;
|
|
37
|
+
fields?: string[];
|
|
38
|
+
}): {
|
|
39
|
+
sql: string;
|
|
40
|
+
params: unknown[];
|
|
41
|
+
};
|
|
42
|
+
declare function buildPaginationMeta(pool: DatabasePool, collection: string, options: PaginationMetaOptions, auth?: AuthContext, lastRecord?: Record<string, unknown>): ListRecordsResult["meta"];
|
|
43
|
+
export { listRecords, buildListSql, buildPaginationMeta, ListRecordsResult, PaginationMetaOptions };
|
|
44
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../src/records/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAI1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAItD,UAAU,iBAAiB;IACzB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC7B,CAAC;CACH;AAED,UAAU,qBAAqB;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,iBAAS,WAAW,CAClB,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,EACD,IAAI,CAAC,EAAE,WAAW,GACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CA2E3B;AAED,iBAAS,YAAY,CACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;IACR,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB,GACA;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAAE,CAkDpC;AAED,iBAAS,mBAAmB,CAC1B,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,qBAAqB,EAC9B,IAAI,CAAC,EAAE,WAAW,EAClB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnC,iBAAiB,CAAC,MAAM,CAAC,CAa3B;AAED,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,CAAC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { validateIdentifier } from "@boltstore/utils";
|
|
2
|
+
import { toBindings } from "../db/cast";
|
|
3
|
+
import { applyRLS, toRLSContext } from "../rls";
|
|
4
|
+
import { getColumnNames, MAX_LIMIT, MAX_OFFSET } from "./schema-cache";
|
|
5
|
+
import { countRecords } from "./count";
|
|
6
|
+
function listRecords(pool, collection, options, auth) {
|
|
7
|
+
getColumnNames(pool, collection);
|
|
8
|
+
const db = pool.read();
|
|
9
|
+
let limit = options?.limit;
|
|
10
|
+
let offset = options?.offset;
|
|
11
|
+
let page;
|
|
12
|
+
let perPage;
|
|
13
|
+
if (options?.page !== undefined && options?.perPage !== undefined) {
|
|
14
|
+
page = options.page;
|
|
15
|
+
perPage = options.perPage;
|
|
16
|
+
limit = perPage;
|
|
17
|
+
offset = (page - 1) * perPage;
|
|
18
|
+
}
|
|
19
|
+
if (limit !== undefined) {
|
|
20
|
+
limit = Math.max(1, Math.min(limit, MAX_LIMIT));
|
|
21
|
+
}
|
|
22
|
+
if (offset !== undefined) {
|
|
23
|
+
offset = Math.max(0, Math.min(offset, MAX_OFFSET));
|
|
24
|
+
}
|
|
25
|
+
const selectCols = options?.fields?.length
|
|
26
|
+
? options.fields.map((f) => `"${f}"`).join(", ")
|
|
27
|
+
: "*";
|
|
28
|
+
let sql = `SELECT ${selectCols} FROM "${collection}"`;
|
|
29
|
+
const params = [];
|
|
30
|
+
const conditions = [];
|
|
31
|
+
const rlsCtx = auth ? toRLSContext(auth) : null;
|
|
32
|
+
const rls = rlsCtx ? applyRLS(pool, collection, "read", rlsCtx) : null;
|
|
33
|
+
if (rls?.whereClause) {
|
|
34
|
+
conditions.push(rls.whereClause);
|
|
35
|
+
params.push(...rls.params);
|
|
36
|
+
}
|
|
37
|
+
if (options?.filter) {
|
|
38
|
+
for (const [key, value] of Object.entries(options.filter)) {
|
|
39
|
+
if (value === null || value === undefined)
|
|
40
|
+
continue;
|
|
41
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
42
|
+
throw Object.assign(new Error(`Filter value for "${key}" must be a scalar or array.`), { status: 400 });
|
|
43
|
+
}
|
|
44
|
+
validateIdentifier(key, "filter field");
|
|
45
|
+
conditions.push(`"${key}" = ?`);
|
|
46
|
+
params.push(value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const sortField = options?.sort || "created_at";
|
|
50
|
+
validateIdentifier(sortField, "sort field");
|
|
51
|
+
if (options?.cursor) {
|
|
52
|
+
const op = options.direction === "asc" ? ">" : "<";
|
|
53
|
+
conditions.push(`"${sortField}" ${op} ?`);
|
|
54
|
+
params.push(options.cursor);
|
|
55
|
+
}
|
|
56
|
+
if (conditions.length > 0) {
|
|
57
|
+
sql += " WHERE " + conditions.join(" AND ");
|
|
58
|
+
}
|
|
59
|
+
const direction = options?.direction === "asc" ? "ASC" : "DESC";
|
|
60
|
+
sql += ` ORDER BY "${sortField}" ${direction}`;
|
|
61
|
+
if (limit !== undefined) {
|
|
62
|
+
sql += ` LIMIT ?`;
|
|
63
|
+
params.push(limit);
|
|
64
|
+
}
|
|
65
|
+
if (offset !== undefined) {
|
|
66
|
+
sql += ` OFFSET ?`;
|
|
67
|
+
params.push(offset);
|
|
68
|
+
}
|
|
69
|
+
const records = db.query(sql).all(...toBindings(params));
|
|
70
|
+
return records;
|
|
71
|
+
}
|
|
72
|
+
function buildListSql(collection, options) {
|
|
73
|
+
validateIdentifier(collection, "collection name");
|
|
74
|
+
const selectCols = options?.fields?.length
|
|
75
|
+
? options.fields.map((f) => `"${f}"`).join(", ")
|
|
76
|
+
: "*";
|
|
77
|
+
let sql = `SELECT ${selectCols} FROM "${collection}"`;
|
|
78
|
+
const params = [];
|
|
79
|
+
const conditions = [];
|
|
80
|
+
if (options?.filter) {
|
|
81
|
+
for (const [key, value] of Object.entries(options.filter)) {
|
|
82
|
+
if (value === null || value === undefined)
|
|
83
|
+
continue;
|
|
84
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
85
|
+
throw Object.assign(new Error(`Filter value for "${key}" must be a scalar or array.`), { status: 400 });
|
|
86
|
+
}
|
|
87
|
+
validateIdentifier(key, "filter field");
|
|
88
|
+
conditions.push(`"${key}" = ?`);
|
|
89
|
+
params.push(value);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const sortField = options?.sort || "created_at";
|
|
93
|
+
validateIdentifier(sortField, "sort field");
|
|
94
|
+
if (options?.cursor) {
|
|
95
|
+
const op = options.direction === "asc" ? ">" : "<";
|
|
96
|
+
conditions.push(`"${sortField}" ${op} ?`);
|
|
97
|
+
params.push(options.cursor);
|
|
98
|
+
}
|
|
99
|
+
if (conditions.length > 0) {
|
|
100
|
+
sql += " WHERE " + conditions.join(" AND ");
|
|
101
|
+
}
|
|
102
|
+
const direction = options?.direction === "asc" ? "ASC" : "DESC";
|
|
103
|
+
sql += ` ORDER BY "${sortField}" ${direction}`;
|
|
104
|
+
let limit = options?.limit;
|
|
105
|
+
let offset = options?.offset;
|
|
106
|
+
if (limit !== undefined) {
|
|
107
|
+
limit = Math.max(1, Math.min(limit, MAX_LIMIT));
|
|
108
|
+
sql += ` LIMIT ?`;
|
|
109
|
+
params.push(limit);
|
|
110
|
+
}
|
|
111
|
+
if (offset !== undefined) {
|
|
112
|
+
offset = Math.max(0, Math.min(offset, MAX_OFFSET));
|
|
113
|
+
sql += ` OFFSET ?`;
|
|
114
|
+
params.push(offset);
|
|
115
|
+
}
|
|
116
|
+
return { sql, params };
|
|
117
|
+
}
|
|
118
|
+
function buildPaginationMeta(pool, collection, options, auth, lastRecord) {
|
|
119
|
+
const total = countRecords(pool, collection, options.filter, auth);
|
|
120
|
+
const meta = {
|
|
121
|
+
total,
|
|
122
|
+
page: options.page,
|
|
123
|
+
per_page: options.perPage,
|
|
124
|
+
total_pages: Math.ceil(total / options.perPage),
|
|
125
|
+
};
|
|
126
|
+
if (lastRecord) {
|
|
127
|
+
const key = options.sort || "created_at";
|
|
128
|
+
meta.next_cursor = lastRecord[key];
|
|
129
|
+
}
|
|
130
|
+
return meta;
|
|
131
|
+
}
|
|
132
|
+
export { listRecords, buildListSql, buildPaginationMeta };
|
|
133
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/records/list.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DatabasePool } from "../db/pool";
|
|
2
|
+
declare const MAX_LIMIT = 1000;
|
|
3
|
+
declare const MAX_OFFSET = 100000;
|
|
4
|
+
declare function getColumnNames(pool: DatabasePool, collection: string): string[];
|
|
5
|
+
declare function invalidateSchemaCache(pool: DatabasePool, collection?: string): void;
|
|
6
|
+
declare function collectionExists(pool: DatabasePool, collection: string): boolean;
|
|
7
|
+
export { invalidateSchemaCache, getColumnNames, collectionExists, MAX_LIMIT, MAX_OFFSET };
|
|
8
|
+
//# sourceMappingURL=schema-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-cache.d.ts","sourceRoot":"","sources":["../../src/records/schema-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,QAAA,MAAM,SAAS,OAAO,CAAC;AACvB,QAAA,MAAM,UAAU,SAAS,CAAC;AAiC1B,iBAAS,cAAc,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBxE;AAED,iBAAS,qBAAqB,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAQ5E;AAED,iBAAS,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CASzE;AAED,OAAO,EAAE,qBAAqB,EAAE,cAAc,EAAE,gBAAgB,EAAE,SAAS,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { validateIdentifier } from "@boltstore/utils";
|
|
2
|
+
const MAX_LIMIT = 1000;
|
|
3
|
+
const MAX_OFFSET = 100000;
|
|
4
|
+
const schemaCache = new WeakMap();
|
|
5
|
+
const SCHEMA_CACHE_TTL_MS = 30000;
|
|
6
|
+
function getPoolCache(pool) {
|
|
7
|
+
let cache = schemaCache.get(pool);
|
|
8
|
+
if (!cache) {
|
|
9
|
+
cache = new Map();
|
|
10
|
+
schemaCache.set(pool, cache);
|
|
11
|
+
}
|
|
12
|
+
return cache;
|
|
13
|
+
}
|
|
14
|
+
function fetchColumns(pool, collection) {
|
|
15
|
+
validateIdentifier(collection, "collection name");
|
|
16
|
+
const db = pool.read();
|
|
17
|
+
const existsRow = db
|
|
18
|
+
.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1")
|
|
19
|
+
.get(collection);
|
|
20
|
+
if (!existsRow) {
|
|
21
|
+
return { columns: [], exists: false, fetchedAt: Date.now() };
|
|
22
|
+
}
|
|
23
|
+
const rows = db.query(`PRAGMA table_info("${collection}")`).all();
|
|
24
|
+
return { columns: rows.map((r) => r.name), exists: true, fetchedAt: Date.now() };
|
|
25
|
+
}
|
|
26
|
+
function getColumnNames(pool, collection) {
|
|
27
|
+
const cache = getPoolCache(pool);
|
|
28
|
+
const entry = cache.get(collection);
|
|
29
|
+
if (entry && Date.now() - entry.fetchedAt < SCHEMA_CACHE_TTL_MS) {
|
|
30
|
+
if (!entry.exists) {
|
|
31
|
+
throw Object.assign(new Error(`Collection "${collection}" not found.`), { status: 404 });
|
|
32
|
+
}
|
|
33
|
+
return entry.columns;
|
|
34
|
+
}
|
|
35
|
+
const fresh = fetchColumns(pool, collection);
|
|
36
|
+
cache.set(collection, fresh);
|
|
37
|
+
if (!fresh.exists) {
|
|
38
|
+
throw Object.assign(new Error(`Collection "${collection}" not found.`), { status: 404 });
|
|
39
|
+
}
|
|
40
|
+
return fresh.columns;
|
|
41
|
+
}
|
|
42
|
+
function invalidateSchemaCache(pool, collection) {
|
|
43
|
+
const cache = schemaCache.get(pool);
|
|
44
|
+
if (!cache)
|
|
45
|
+
return;
|
|
46
|
+
if (collection) {
|
|
47
|
+
cache.delete(collection);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
cache.clear();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function collectionExists(pool, collection) {
|
|
54
|
+
const cache = getPoolCache(pool);
|
|
55
|
+
const entry = cache.get(collection);
|
|
56
|
+
if (entry && Date.now() - entry.fetchedAt < SCHEMA_CACHE_TTL_MS) {
|
|
57
|
+
return entry.exists;
|
|
58
|
+
}
|
|
59
|
+
const fresh = fetchColumns(pool, collection);
|
|
60
|
+
cache.set(collection, fresh);
|
|
61
|
+
return fresh.exists;
|
|
62
|
+
}
|
|
63
|
+
export { invalidateSchemaCache, getColumnNames, collectionExists, MAX_LIMIT, MAX_OFFSET };
|
|
64
|
+
//# sourceMappingURL=schema-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-cache.js","sourceRoot":"","sources":["../../src/records/schema-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./records/schema-cache";
|
|
2
|
+
export * from "./records/crud";
|
|
3
|
+
export * from "./records/list";
|
|
4
|
+
export * from "./records/count";
|
|
5
|
+
export * from "./records/distinct";
|
|
6
|
+
export * from "./records/batch";
|
|
7
|
+
export * from "./records/helpers";
|
|
8
|
+
//# sourceMappingURL=records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../src/records.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
package/dist/records.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from "./records/schema-cache";
|
|
2
|
+
export * from "./records/crud";
|
|
3
|
+
export * from "./records/list";
|
|
4
|
+
export * from "./records/count";
|
|
5
|
+
export * from "./records/distinct";
|
|
6
|
+
export * from "./records/batch";
|
|
7
|
+
export * from "./records/helpers";
|
|
8
|
+
//# sourceMappingURL=records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.js","sourceRoot":"","sources":["../src/records.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relations / References — lightweight foreign key relations for Boltstore.
|
|
3
|
+
*
|
|
4
|
+
* Adds support for:
|
|
5
|
+
* - `expand` parameter to fetch related records by foreign key
|
|
6
|
+
* - Reference field type in schema with optional cascade delete
|
|
7
|
+
*
|
|
8
|
+
* @module boltstore/relations
|
|
9
|
+
*/
|
|
10
|
+
import { DatabasePool } from "./db/pool";
|
|
11
|
+
import type { AuthContext } from "./middleware/auth";
|
|
12
|
+
/** Metadata for a single relation field. */
|
|
13
|
+
export interface RelationDefinition {
|
|
14
|
+
/** The column in this collection that holds the foreign key. */
|
|
15
|
+
field: string;
|
|
16
|
+
/** The target collection (table) that records reference. */
|
|
17
|
+
foreignCollection: string;
|
|
18
|
+
/** Whether deleting a record in this collection cascades to children (Phase 1 feature). */
|
|
19
|
+
cascadeDelete?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Load explicit relation definitions for a collection from metadata. */
|
|
22
|
+
export declare function getRelations(pool: DatabasePool, collection: string): Record<string, RelationDefinition>;
|
|
23
|
+
/** Persist explicit relation definitions for a collection. */
|
|
24
|
+
export declare function setRelations(pool: DatabasePool, collection: string, relations: Record<string, RelationDefinition>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Expand related records for a list of parent records.
|
|
27
|
+
*
|
|
28
|
+
* For each record, if it has a field whose name is provided in `expandFields`,
|
|
29
|
+
* the function fetches the related record from the target collection and nests
|
|
30
|
+
* it under the key `{field}_expanded`.
|
|
31
|
+
*
|
|
32
|
+
* Example:
|
|
33
|
+
* ```
|
|
34
|
+
* GET /api/myapp/collections/posts/records?expand=author
|
|
35
|
+
* ```
|
|
36
|
+
* If `posts` has an `author` column storing user IDs, the returned records
|
|
37
|
+
* will include `author_expanded: { id, name, ... }`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function expandRecords(pool: DatabasePool, parentCollection: string, records: Record<string, unknown>[], expandFields: string[], _depth?: number): Record<string, unknown>[];
|
|
40
|
+
/**
|
|
41
|
+
* Cascade delete: when a parent record is deleted, also delete child records
|
|
42
|
+
* that reference it via a foreign key field.
|
|
43
|
+
*
|
|
44
|
+
* Checks all collections in the database for a field matching `{parentCollection}_id`
|
|
45
|
+
* and deletes records where that field equals the deleted `parentId`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function cascadeDelete(pool: DatabasePool, parentCollection: string, parentId: string, _auth?: AuthContext): {
|
|
48
|
+
deleted: string[];
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=relations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.d.ts","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAEzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrD,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,2FAA2F;IAC3F,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAoCD,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAavG;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAC1B,IAAI,EAAE,YAAY,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAC5C,IAAI,CAQN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,YAAY,EAClB,gBAAgB,EAAE,MAAM,EACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAClC,YAAY,EAAE,MAAM,EAAE,EACtB,MAAM,SAAI,GACT,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAgE3B;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,YAAY,EAClB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,WAAW,GAClB;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CA6BvB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relations / References — lightweight foreign key relations for Boltstore.
|
|
3
|
+
*
|
|
4
|
+
* Adds support for:
|
|
5
|
+
* - `expand` parameter to fetch related records by foreign key
|
|
6
|
+
* - Reference field type in schema with optional cascade delete
|
|
7
|
+
*
|
|
8
|
+
* @module boltstore/relations
|
|
9
|
+
*/
|
|
10
|
+
import { validateIdentifier } from "@boltstore/utils";
|
|
11
|
+
/** Maximum recursion depth for nested expansion. */
|
|
12
|
+
const MAX_EXPAND_DEPTH = 2;
|
|
13
|
+
/** Resolve a target collection for an expand field using relation metadata first, then heuristic. */
|
|
14
|
+
function resolveTargetCollection(pool, parentCollection, field) {
|
|
15
|
+
// 1. Try explicit relation metadata stored in _collections
|
|
16
|
+
try {
|
|
17
|
+
const db = pool.read();
|
|
18
|
+
const meta = db
|
|
19
|
+
.query("SELECT relations_json FROM _collections WHERE name = ?")
|
|
20
|
+
.get(parentCollection);
|
|
21
|
+
if (meta?.relations_json) {
|
|
22
|
+
const relations = JSON.parse(meta.relations_json);
|
|
23
|
+
if (relations[field]?.foreignCollection) {
|
|
24
|
+
return relations[field].foreignCollection;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// _collections may not exist yet; fall through to heuristic.
|
|
30
|
+
}
|
|
31
|
+
// 2. Heuristic pluralization
|
|
32
|
+
let targetCollection = `${field}s`;
|
|
33
|
+
if (field.endsWith("y")) {
|
|
34
|
+
targetCollection = field.slice(0, -1) + "ies";
|
|
35
|
+
}
|
|
36
|
+
else if (field.endsWith("s") || field.endsWith("x") || field.endsWith("ch") || field.endsWith("sh")) {
|
|
37
|
+
targetCollection = field + "es";
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
validateIdentifier(targetCollection, "target collection");
|
|
41
|
+
return targetCollection;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Load explicit relation definitions for a collection from metadata. */
|
|
48
|
+
export function getRelations(pool, collection) {
|
|
49
|
+
try {
|
|
50
|
+
const db = pool.read();
|
|
51
|
+
const meta = db
|
|
52
|
+
.query("SELECT relations_json FROM _collections WHERE name = ?")
|
|
53
|
+
.get(collection);
|
|
54
|
+
if (meta?.relations_json) {
|
|
55
|
+
return JSON.parse(meta.relations_json);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// ignore
|
|
60
|
+
}
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
/** Persist explicit relation definitions for a collection. */
|
|
64
|
+
export function setRelations(pool, collection, relations) {
|
|
65
|
+
const db = pool.write();
|
|
66
|
+
try {
|
|
67
|
+
db.run("ALTER TABLE _collections ADD COLUMN relations_json TEXT");
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Column may already exist.
|
|
71
|
+
}
|
|
72
|
+
db.run("UPDATE _collections SET relations_json = ? WHERE name = ?", [JSON.stringify(relations), collection]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Expand related records for a list of parent records.
|
|
76
|
+
*
|
|
77
|
+
* For each record, if it has a field whose name is provided in `expandFields`,
|
|
78
|
+
* the function fetches the related record from the target collection and nests
|
|
79
|
+
* it under the key `{field}_expanded`.
|
|
80
|
+
*
|
|
81
|
+
* Example:
|
|
82
|
+
* ```
|
|
83
|
+
* GET /api/myapp/collections/posts/records?expand=author
|
|
84
|
+
* ```
|
|
85
|
+
* If `posts` has an `author` column storing user IDs, the returned records
|
|
86
|
+
* will include `author_expanded: { id, name, ... }`.
|
|
87
|
+
*/
|
|
88
|
+
export function expandRecords(pool, parentCollection, records, expandFields, _depth = 0) {
|
|
89
|
+
if (!Array.isArray(records) || records.length === 0)
|
|
90
|
+
return records;
|
|
91
|
+
if (!Array.isArray(expandFields) || expandFields.length === 0)
|
|
92
|
+
return records;
|
|
93
|
+
if (_depth >= MAX_EXPAND_DEPTH)
|
|
94
|
+
return records;
|
|
95
|
+
const expanded = records.map((r) => ({ ...r }));
|
|
96
|
+
for (const field of expandFields) {
|
|
97
|
+
validateIdentifier(field, "field name");
|
|
98
|
+
const targetCollection = resolveTargetCollection(pool, parentCollection, field);
|
|
99
|
+
if (!targetCollection)
|
|
100
|
+
continue;
|
|
101
|
+
// Group parent IDs to fetch in batch
|
|
102
|
+
const foreignIds = new Set();
|
|
103
|
+
for (const record of records) {
|
|
104
|
+
const foreignId = record[field];
|
|
105
|
+
if (foreignId !== null && foreignId !== undefined && typeof foreignId === "string") {
|
|
106
|
+
foreignIds.add(foreignId);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (foreignIds.size === 0)
|
|
110
|
+
continue;
|
|
111
|
+
// Fetch related records
|
|
112
|
+
const db = pool.read();
|
|
113
|
+
// Check if the target collection exists
|
|
114
|
+
const tableExists = db
|
|
115
|
+
.query("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?")
|
|
116
|
+
.get(targetCollection);
|
|
117
|
+
if (!tableExists) {
|
|
118
|
+
// Still set explicit null for non-existent heuristic targets so callers see a deterministic key.
|
|
119
|
+
for (const record of expanded) {
|
|
120
|
+
record[`${field}_expanded`] = null;
|
|
121
|
+
}
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
// Build batch query
|
|
125
|
+
const placeholders = Array.from(foreignIds).map(() => "?").join(", ");
|
|
126
|
+
const relatedRows = db
|
|
127
|
+
.query(`SELECT * FROM "${targetCollection}" WHERE id IN (${placeholders})`)
|
|
128
|
+
.all(...Array.from(foreignIds));
|
|
129
|
+
// Build a map for O(1) lookup
|
|
130
|
+
const relatedMap = new Map();
|
|
131
|
+
for (const row of relatedRows) {
|
|
132
|
+
relatedMap.set(row.id, row);
|
|
133
|
+
}
|
|
134
|
+
// Attach expanded data
|
|
135
|
+
for (const record of expanded) {
|
|
136
|
+
const foreignId = record[field];
|
|
137
|
+
const expandKey = `${field}_expanded`;
|
|
138
|
+
if (foreignId && relatedMap.has(foreignId)) {
|
|
139
|
+
record[expandKey] = relatedMap.get(foreignId);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
record[expandKey] = null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return expanded;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Cascade delete: when a parent record is deleted, also delete child records
|
|
150
|
+
* that reference it via a foreign key field.
|
|
151
|
+
*
|
|
152
|
+
* Checks all collections in the database for a field matching `{parentCollection}_id`
|
|
153
|
+
* and deletes records where that field equals the deleted `parentId`.
|
|
154
|
+
*/
|
|
155
|
+
export function cascadeDelete(pool, parentCollection, parentId, _auth) {
|
|
156
|
+
validateIdentifier(parentCollection, "collection name");
|
|
157
|
+
const deleted = [];
|
|
158
|
+
pool.writeTransaction(() => {
|
|
159
|
+
const db = pool.write();
|
|
160
|
+
// Find all tables that have a column named {parentCollection}_id
|
|
161
|
+
const allTables = db
|
|
162
|
+
.query("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
|
|
163
|
+
.all();
|
|
164
|
+
for (const table of allTables) {
|
|
165
|
+
const foreignKey = `${parentCollection}_id`;
|
|
166
|
+
// Check if this table has the foreign key column via PRAGMA
|
|
167
|
+
const columns = db
|
|
168
|
+
.query(`PRAGMA table_info("${table.name}")`)
|
|
169
|
+
.all();
|
|
170
|
+
const hasColumn = columns.some((c) => c.name === foreignKey);
|
|
171
|
+
if (hasColumn) {
|
|
172
|
+
db.run(`DELETE FROM "${table.name}" WHERE "${foreignKey}" = ?`, [parentId]);
|
|
173
|
+
deleted.push(table.name);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return { deleted };
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGtD,oDAAoD;AACpD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAY3B,qGAAqG;AACrG,SAAS,uBAAuB,CAAC,IAAkB,EAAE,gBAAwB,EAAE,KAAa;IAC1F,2DAA2D;IAC3D,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE;aACZ,KAAK,CAAC,wDAAwD,CAAC;aAC/D,GAAG,CAAC,gBAAgB,CAAuC,CAAC;QAC/D,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAkD,CAAC;YACnG,IAAI,SAAS,CAAC,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;gBACxC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC;YAC5C,CAAC;QACH,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,6DAA6D;IAC/D,CAAC;IAED,6BAA6B;IAC7B,IAAI,gBAAgB,GAAG,GAAG,KAAK,GAAG,CAAC;IACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,gBAAgB,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IAChD,CAAC;SAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACtG,gBAAgB,GAAG,KAAK,GAAG,IAAI,CAAC;IAClC,CAAC;IAED,IAAI,CAAC;QACH,kBAAkB,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,CAAC;QAC1D,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,YAAY,CAAC,IAAkB,EAAE,UAAkB;IACjE,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE;aACZ,KAAK,CAAC,wDAAwD,CAAC;aAC/D,GAAG,CAAC,UAAU,CAAuC,CAAC;QACzD,IAAI,IAAI,EAAE,cAAc,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAuC,CAAC;QAC/E,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAC1B,IAAkB,EAClB,UAAkB,EAClB,SAA6C;IAE7C,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;IACxB,IAAI,CAAC;QACH,EAAE,CAAC,GAAG,CAAC,yDAAyD,CAAC,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,4BAA4B;IAC9B,CAAC;IACD,EAAE,CAAC,GAAG,CAAC,2DAA2D,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAkB,EAClB,gBAAwB,EACxB,OAAkC,EAClC,YAAsB,EACtB,MAAM,GAAG,CAAC;IAEV,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IACpE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,MAAM,IAAI,gBAAgB;QAAE,OAAO,OAAO,CAAC;IAE/C,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhD,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,kBAAkB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QAExC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC;QAChF,IAAI,CAAC,gBAAgB;YAAE,SAAS;QAEhC,qCAAqC;QACrC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;QACrC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBACnF,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,IAAI,UAAU,CAAC,IAAI,KAAK,CAAC;YAAE,SAAS;QAEpC,wBAAwB;QACxB,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAEvB,wCAAwC;QACxC,MAAM,WAAW,GAAG,EAAE;aACnB,KAAK,CAAC,2DAA2D,CAAC;aAClE,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACzB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,iGAAiG;YACjG,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;gBAC9B,MAAM,CAAC,GAAG,KAAK,WAAW,CAAC,GAAG,IAAI,CAAC;YACrC,CAAC;YACD,SAAS;QACX,CAAC;QAED,oBAAoB;QACpB,MAAM,YAAY,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,WAAW,GAAG,EAAE;aACnB,KAAK,CAAC,kBAAkB,gBAAgB,kBAAkB,YAAY,GAAG,CAAC;aAC1E,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAA8B,CAAC;QAE/D,8BAA8B;QAC9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAmC,CAAC;QAC9D,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;YAC9B,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAY,EAAE,GAAG,CAAC,CAAC;QACxC,CAAC;QAED,uBAAuB;QACvB,KAAK,MAAM,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAuB,CAAC;YACtD,MAAM,SAAS,GAAG,GAAG,KAAK,WAAW,CAAC;YACtC,IAAI,SAAS,IAAI,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3C,MAAM,CAAC,SAAS,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAkB,EAClB,gBAAwB,EACxB,QAAgB,EAChB,KAAmB;IAEnB,kBAAkB,CAAC,gBAAgB,EAAE,iBAAiB,CAAC,CAAC;IACxD,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE;QACzB,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAExB,iEAAiE;QACjE,MAAM,SAAS,GAAG,EAAE;aACjB,KAAK,CAAC,gFAAgF,CAAC;aACvF,GAAG,EAAwB,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;YAC9B,MAAM,UAAU,GAAG,GAAG,gBAAgB,KAAK,CAAC;YAE5C,4DAA4D;YAC5D,MAAM,OAAO,GAAG,EAAE;iBACf,KAAK,CAAC,sBAAsB,KAAK,CAAC,IAAI,IAAI,CAAC;iBAC3C,GAAG,EAAwB,CAAC;YAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC;YAE7D,IAAI,SAAS,EAAE,CAAC;gBACd,EAAE,CAAC,GAAG,CAAC,gBAAgB,KAAK,CAAC,IAAI,YAAY,UAAU,OAAO,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;gBAC5E,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,OAAO,EAAE,CAAC;AACrB,CAAC"}
|