@mistriwork/engine 0.0.0-stage → 1.0.0-rc.10
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/LICENSE +21 -0
- package/README.md +17 -2
- package/dist/adapters/adapter.d.ts +693 -0
- package/dist/adapters/adapter.js +444 -0
- package/dist/adapters/cache/cacheStore.d.ts +75 -0
- package/dist/adapters/cache/cacheStore.js +160 -0
- package/dist/adapters/cache/resp.d.ts +52 -0
- package/dist/adapters/cache/resp.js +237 -0
- package/dist/adapters/commands/canonicalJson.d.ts +19 -0
- package/dist/adapters/commands/canonicalJson.js +154 -0
- package/dist/adapters/connect.d.ts +29 -0
- package/dist/adapters/connect.js +40 -0
- package/dist/adapters/connectionBudget.d.ts +101 -0
- package/dist/adapters/connectionBudget.js +128 -0
- package/dist/adapters/dbLatency.d.ts +35 -0
- package/dist/adapters/dbLatency.js +79 -0
- package/dist/adapters/epochCache.d.ts +50 -0
- package/dist/adapters/epochCache.js +85 -0
- package/dist/adapters/http/egress.d.ts +48 -0
- package/dist/adapters/http/egress.js +175 -0
- package/dist/adapters/http/httpAdapter.d.ts +71 -0
- package/dist/adapters/http/httpAdapter.js +512 -0
- package/dist/adapters/http/httpDescription.d.ts +32 -0
- package/dist/adapters/http/httpDescription.js +246 -0
- package/dist/adapters/http/openapi.d.ts +98 -0
- package/dist/adapters/http/openapi.js +487 -0
- package/dist/adapters/layoutRefusal.d.ts +24 -0
- package/dist/adapters/layoutRefusal.js +35 -0
- package/dist/adapters/net/networkPolicy.d.ts +44 -0
- package/dist/adapters/net/networkPolicy.js +123 -0
- package/dist/adapters/net/untrustedDatabase.d.ts +18 -0
- package/dist/adapters/net/untrustedDatabase.js +120 -0
- package/dist/adapters/poolAdmission.d.ts +43 -0
- package/dist/adapters/poolAdmission.js +184 -0
- package/dist/adapters/poolMetrics.d.ts +121 -0
- package/dist/adapters/poolMetrics.js +185 -0
- package/dist/adapters/postgres.d.ts +436 -0
- package/dist/adapters/postgres.js +1890 -0
- package/dist/adapters/postgresCatalog.d.ts +96 -0
- package/dist/adapters/postgresCatalog.js +387 -0
- package/dist/adapters/postgresCatalogDigest.d.ts +30 -0
- package/dist/adapters/postgresCatalogDigest.js +87 -0
- package/dist/adapters/postgresCatalogRows.d.ts +62 -0
- package/dist/adapters/postgresCatalogRows.js +37 -0
- package/dist/adapters/postgresDates.d.ts +1 -0
- package/dist/adapters/postgresDates.js +39 -0
- package/dist/adapters/postgresMaintenance.d.ts +33 -0
- package/dist/adapters/postgresMaintenance.js +179 -0
- package/dist/adapters/postgresPool.d.ts +255 -0
- package/dist/adapters/postgresPool.js +838 -0
- package/dist/adapters/postgresPooler.d.ts +28 -0
- package/dist/adapters/postgresPooler.js +267 -0
- package/dist/adapters/postgresReadCatalog.d.ts +31 -0
- package/dist/adapters/postgresReadCatalog.js +226 -0
- package/dist/adapters/postgresReadSnapshot.d.ts +46 -0
- package/dist/adapters/postgresReadSnapshot.js +133 -0
- package/dist/adapters/postgresSchemaEpoch.d.ts +24 -0
- package/dist/adapters/postgresSchemaEpoch.js +149 -0
- package/dist/adapters/postgresTransactionHandle.d.ts +173 -0
- package/dist/adapters/postgresTransactionHandle.js +524 -0
- package/dist/adapters/postgresValues.d.ts +30 -0
- package/dist/adapters/postgresValues.js +75 -0
- package/dist/adapters/replicas.d.ts +70 -0
- package/dist/adapters/replicas.js +208 -0
- package/dist/adapters/reservedSql.d.ts +38 -0
- package/dist/adapters/reservedSql.js +264 -0
- package/dist/adapters/rolePrivileges.d.ts +17 -0
- package/dist/adapters/rolePrivileges.js +22 -0
- package/dist/adapters/scoped/keyRows.d.ts +65 -0
- package/dist/adapters/scoped/keyRows.js +173 -0
- package/dist/adapters/scoped/postgresScopeCatalog.d.ts +16 -0
- package/dist/adapters/scoped/postgresScopeCatalog.js +117 -0
- package/dist/adapters/scoped/scopedCommandOperations.d.ts +5 -0
- package/dist/adapters/scoped/scopedCommandOperations.js +48 -0
- package/dist/adapters/scoped/scopedReads.d.ts +20 -0
- package/dist/adapters/scoped/scopedReads.js +174 -0
- package/dist/adapters/scoped/scopedTransaction.d.ts +89 -0
- package/dist/adapters/scoped/scopedTransaction.js +303 -0
- package/dist/adapters/scoped/sqliteScopeCatalog.d.ts +16 -0
- package/dist/adapters/scoped/sqliteScopeCatalog.js +241 -0
- package/dist/adapters/scoped/updateGuard.d.ts +3 -0
- package/dist/adapters/scoped/updateGuard.js +40 -0
- package/dist/adapters/serverSettings.d.ts +45 -0
- package/dist/adapters/serverSettings.js +46 -0
- package/dist/adapters/sqlite.d.ts +262 -0
- package/dist/adapters/sqlite.js +1434 -0
- package/dist/adapters/sqliteCostlyReads.d.ts +33 -0
- package/dist/adapters/sqliteCostlyReads.js +49 -0
- package/dist/adapters/sqliteDecimals.d.ts +22 -0
- package/dist/adapters/sqliteDecimals.js +57 -0
- package/dist/adapters/sqliteHandles.d.ts +43 -0
- package/dist/adapters/sqliteHandles.js +143 -0
- package/dist/adapters/sqliteStatementProcess.d.ts +29 -0
- package/dist/adapters/sqliteStatementProcess.js +131 -0
- package/dist/adapters/statementControl.d.ts +34 -0
- package/dist/adapters/statementControl.js +90 -0
- package/dist/adapters/storeHost.d.ts +130 -0
- package/dist/adapters/storeHost.js +13 -0
- package/dist/adapters/telemetry/drivers.d.ts +21 -0
- package/dist/adapters/telemetry/drivers.js +321 -0
- package/dist/adapters/telemetry/metrics.d.ts +63 -0
- package/dist/adapters/telemetry/metrics.js +122 -0
- package/dist/adapters/telemetry/otlp.d.ts +12 -0
- package/dist/adapters/telemetry/otlp.js +320 -0
- package/dist/adapters/telemetry/otlpEncode.d.ts +48 -0
- package/dist/adapters/telemetry/otlpEncode.js +366 -0
- package/dist/adapters/telemetry/otlpEnv.d.ts +1 -0
- package/dist/adapters/telemetry/otlpEnv.js +12 -0
- package/dist/adapters/telemetry/serverTiming.d.ts +83 -0
- package/dist/adapters/telemetry/serverTiming.js +503 -0
- package/dist/adapters/telemetry/start.d.ts +10 -0
- package/dist/adapters/telemetry/start.js +25 -0
- package/dist/adapters/telemetry/statement.d.ts +16 -0
- package/dist/adapters/telemetry/statement.js +134 -0
- package/dist/adapters/telemetry/trace.d.ts +127 -0
- package/dist/adapters/telemetry/trace.js +295 -0
- package/dist/adapters/writeAhead.d.ts +27 -0
- package/dist/adapters/writeAhead.js +46 -0
- package/dist/adapters/writeAttribution.d.ts +18 -0
- package/dist/adapters/writeAttribution.js +23 -0
- package/dist/adapters/writeCapture.d.ts +73 -0
- package/dist/adapters/writeCapture.js +20 -0
- package/dist/adapters/writeLanes.d.ts +94 -0
- package/dist/adapters/writeLanes.js +232 -0
- package/dist/engine.d.ts +15 -0
- package/dist/engine.js +13 -0
- package/dist/server/auth/credentials.d.ts +61 -0
- package/dist/server/auth/credentials.js +260 -0
- package/dist/server/cacheService.d.ts +50 -0
- package/dist/server/cacheService.js +110 -0
- package/dist/server/cli/args.d.ts +176 -0
- package/dist/server/cli/args.js +342 -0
- package/dist/server/cli/bin.d.ts +5 -0
- package/dist/server/cli/bin.js +66 -0
- package/dist/server/cli/browser.d.ts +15 -0
- package/dist/server/cli/browser.js +37 -0
- package/dist/server/cli/config.d.ts +50 -0
- package/dist/server/cli/config.js +206 -0
- package/dist/server/cli/startup.d.ts +36 -0
- package/dist/server/cli/startup.js +128 -0
- package/dist/server/cli/startupOptions.d.ts +63 -0
- package/dist/server/cli/startupOptions.js +143 -0
- package/dist/server/cli/subcommand.d.ts +28 -0
- package/dist/server/cli/subcommand.js +32 -0
- package/dist/server/cli/usage.d.ts +13 -0
- package/dist/server/cli/usage.js +31 -0
- package/dist/server/clientHttp.d.ts +184 -0
- package/dist/server/clientHttp.js +700 -0
- package/dist/server/clientSurface.d.ts +128 -0
- package/dist/server/clientSurface.js +182 -0
- package/dist/server/columnGrants.d.ts +103 -0
- package/dist/server/columnGrants.js +730 -0
- package/dist/server/compileCache.d.ts +1 -0
- package/dist/server/compileCache.js +30 -0
- package/dist/server/config/locations.d.ts +1 -0
- package/dist/server/config/locations.js +15 -0
- package/dist/server/configPaths.d.ts +7 -0
- package/dist/server/configPaths.js +34 -0
- package/dist/server/configSections.d.ts +64 -0
- package/dist/server/configSections.js +72 -0
- package/dist/server/connectionRedaction.d.ts +30 -0
- package/dist/server/connectionRedaction.js +77 -0
- package/dist/server/connections.d.ts +353 -0
- package/dist/server/connections.js +978 -0
- package/dist/server/constraints.d.ts +82 -0
- package/dist/server/constraints.js +169 -0
- package/dist/server/demo.d.ts +9 -0
- package/dist/server/demo.js +554 -0
- package/dist/server/devclient.d.ts +108 -0
- package/dist/server/devclient.js +361 -0
- package/dist/server/evidence.d.ts +55 -0
- package/dist/server/evidence.js +323 -0
- package/dist/server/hiddenColumns.d.ts +32 -0
- package/dist/server/hiddenColumns.js +238 -0
- package/dist/server/http/baselineHeaders.d.ts +24 -0
- package/dist/server/http/baselineHeaders.js +12 -0
- package/dist/server/http/captured.d.ts +35 -0
- package/dist/server/http/captured.js +73 -0
- package/dist/server/http/clock.d.ts +1 -0
- package/dist/server/http/clock.js +14 -0
- package/dist/server/http/connectionSource.d.ts +4 -0
- package/dist/server/http/connectionSource.js +48 -0
- package/dist/server/http/context.d.ts +189 -0
- package/dist/server/http/context.js +11 -0
- package/dist/server/http/crawlerText.d.ts +15 -0
- package/dist/server/http/crawlerText.js +32 -0
- package/dist/server/http/dataRoutes.d.ts +88 -0
- package/dist/server/http/dataRoutes.js +137 -0
- package/dist/server/http/door.d.ts +57 -0
- package/dist/server/http/door.js +39 -0
- package/dist/server/http/exportRoute.d.ts +15 -0
- package/dist/server/http/exportRoute.js +320 -0
- package/dist/server/http/firstPaint.d.ts +108 -0
- package/dist/server/http/firstPaint.js +262 -0
- package/dist/server/http/httpOperationRoutes.d.ts +11 -0
- package/dist/server/http/httpOperationRoutes.js +50 -0
- package/dist/server/http/pageReads.d.ts +20 -0
- package/dist/server/http/pageReads.js +205 -0
- package/dist/server/http/pageRoutes.d.ts +39 -0
- package/dist/server/http/pageRoutes.js +291 -0
- package/dist/server/http/probes.d.ts +22 -0
- package/dist/server/http/probes.js +27 -0
- package/dist/server/http/rateLimit.d.ts +131 -0
- package/dist/server/http/rateLimit.js +351 -0
- package/dist/server/http/readRoutes.d.ts +9 -0
- package/dist/server/http/readRoutes.js +131 -0
- package/dist/server/http/readRouting.d.ts +20 -0
- package/dist/server/http/readRouting.js +80 -0
- package/dist/server/http/recordCompose.d.ts +67 -0
- package/dist/server/http/recordCompose.js +167 -0
- package/dist/server/http/recordWrites.d.ts +89 -0
- package/dist/server/http/recordWrites.js +298 -0
- package/dist/server/http/refusalReport.d.ts +38 -0
- package/dist/server/http/refusalReport.js +63 -0
- package/dist/server/http/reply.d.ts +130 -0
- package/dist/server/http/reply.js +394 -0
- package/dist/server/http/requestOrigin.d.ts +11 -0
- package/dist/server/http/requestOrigin.js +8 -0
- package/dist/server/http/router.d.ts +45 -0
- package/dist/server/http/router.js +22 -0
- package/dist/server/http/serverCore.d.ts +68 -0
- package/dist/server/http/serverCore.js +177 -0
- package/dist/server/http/traceRequest.d.ts +30 -0
- package/dist/server/http/traceRequest.js +224 -0
- package/dist/server/http/trust.d.ts +261 -0
- package/dist/server/http/trust.js +472 -0
- package/dist/server/http/writeRefusal.d.ts +33 -0
- package/dist/server/http/writeRefusal.js +24 -0
- package/dist/server/httpConnection.d.ts +49 -0
- package/dist/server/httpConnection.js +170 -0
- package/dist/server/impact.d.ts +55 -0
- package/dist/server/impact.js +47 -0
- package/dist/server/journal.d.ts +134 -0
- package/dist/server/journal.js +398 -0
- package/dist/server/keysetReadHttp.d.ts +52 -0
- package/dist/server/keysetReadHttp.js +85 -0
- package/dist/server/layouts.d.ts +49 -0
- package/dist/server/layouts.js +192 -0
- package/dist/server/migrate/cli.d.ts +55 -0
- package/dist/server/migrate/cli.js +517 -0
- package/dist/server/migrate/database.d.ts +37 -0
- package/dist/server/migrate/database.js +247 -0
- package/dist/server/migrate/desired.d.ts +76 -0
- package/dist/server/migrate/desired.js +292 -0
- package/dist/server/migrate/files.d.ts +34 -0
- package/dist/server/migrate/files.js +141 -0
- package/dist/server/migrate/flyway.d.ts +7 -0
- package/dist/server/migrate/flyway.js +69 -0
- package/dist/server/migrate/history.d.ts +70 -0
- package/dist/server/migrate/history.js +211 -0
- package/dist/server/migrate/runner.d.ts +100 -0
- package/dist/server/migrate/runner.js +409 -0
- package/dist/server/migrate/snapshot.d.ts +30 -0
- package/dist/server/migrate/snapshot.js +239 -0
- package/dist/server/migrate/target.d.ts +33 -0
- package/dist/server/migrate/target.js +82 -0
- package/dist/server/openapiDraft.d.ts +25 -0
- package/dist/server/openapiDraft.js +171 -0
- package/dist/server/operations/diagnostics.d.ts +101 -0
- package/dist/server/operations/diagnostics.js +284 -0
- package/dist/server/operations/requestMetrics.d.ts +33 -0
- package/dist/server/operations/requestMetrics.js +94 -0
- package/dist/server/packageVersion.d.ts +1 -0
- package/dist/server/packageVersion.js +11 -0
- package/dist/server/poolConfig.d.ts +34 -0
- package/dist/server/poolConfig.js +114 -0
- package/dist/server/product.d.ts +20 -0
- package/dist/server/product.js +12 -0
- package/dist/server/recordHistoryOperations.d.ts +39 -0
- package/dist/server/recordHistoryOperations.js +43 -0
- package/dist/server/recordWriteOperations.d.ts +111 -0
- package/dist/server/recordWriteOperations.js +329 -0
- package/dist/server/resourceReadHttp.d.ts +81 -0
- package/dist/server/resourceReadHttp.js +606 -0
- package/dist/server/resourceReadOperations.d.ts +147 -0
- package/dist/server/resourceReadOperations.js +452 -0
- package/dist/server/scaffoldMachine.d.ts +54 -0
- package/dist/server/scaffoldMachine.js +184 -0
- package/dist/server/seed/cli.d.ts +20 -0
- package/dist/server/seed/cli.js +164 -0
- package/dist/server/seed/lexicon.d.ts +26 -0
- package/dist/server/seed/lexicon.js +585 -0
- package/dist/server/seed/seed.d.ts +70 -0
- package/dist/server/seed/seed.js +779 -0
- package/dist/server/seed/words.d.ts +21 -0
- package/dist/server/seed/words.js +164 -0
- package/dist/server/serverRequests.d.ts +6 -0
- package/dist/server/serverRequests.js +33 -0
- package/dist/server/serverTimeouts.d.ts +33 -0
- package/dist/server/serverTimeouts.js +32 -0
- package/dist/server/shapeonly.d.ts +32 -0
- package/dist/server/shapeonly.js +110 -0
- package/dist/server/startupWarnings.d.ts +32 -0
- package/dist/server/startupWarnings.js +92 -0
- package/dist/shared/addressParams.d.ts +28 -0
- package/dist/shared/addressParams.js +47 -0
- package/dist/shared/appCards.d.ts +123 -0
- package/dist/shared/appCards.js +385 -0
- package/dist/shared/appComponents.d.ts +177 -0
- package/dist/shared/appComponents.js +422 -0
- package/dist/shared/appDocuments.d.ts +43 -0
- package/dist/shared/appDocuments.js +131 -0
- package/dist/shared/appEmbeds.d.ts +21 -0
- package/dist/shared/appEmbeds.js +24 -0
- package/dist/shared/appIcons.d.ts +28 -0
- package/dist/shared/appIcons.js +223 -0
- package/dist/shared/appLinks.d.ts +17 -0
- package/dist/shared/appLinks.js +124 -0
- package/dist/shared/appListColumns.d.ts +3 -0
- package/dist/shared/appListColumns.js +66 -0
- package/dist/shared/appLocale.d.ts +36 -0
- package/dist/shared/appLocale.js +86 -0
- package/dist/shared/appMessages.d.ts +44 -0
- package/dist/shared/appMessages.js +78 -0
- package/dist/shared/appNoun.d.ts +21 -0
- package/dist/shared/appNoun.js +32 -0
- package/dist/shared/autoBucket.d.ts +98 -0
- package/dist/shared/autoBucket.js +305 -0
- package/dist/shared/autoValues.d.ts +25 -0
- package/dist/shared/autoValues.js +25 -0
- package/dist/shared/breakdown.d.ts +228 -0
- package/dist/shared/breakdown.js +557 -0
- package/dist/shared/breakdowntext.d.ts +41 -0
- package/dist/shared/breakdowntext.js +400 -0
- package/dist/shared/builtInThemes.d.ts +2 -0
- package/dist/shared/builtInThemes.js +771 -0
- package/dist/shared/chartSeparation.d.ts +11 -0
- package/dist/shared/chartSeparation.js +58 -0
- package/dist/shared/clauses.d.ts +22 -0
- package/dist/shared/clauses.js +97 -0
- package/dist/shared/columnCaption.d.ts +16 -0
- package/dist/shared/columnCaption.js +90 -0
- package/dist/shared/commandFieldPath.d.ts +2 -0
- package/dist/shared/commandFieldPath.js +7 -0
- package/dist/shared/commandWire.d.ts +26 -0
- package/dist/shared/commandWire.js +41 -0
- package/dist/shared/computed.d.ts +161 -0
- package/dist/shared/computed.js +378 -0
- package/dist/shared/computedParse.d.ts +13 -0
- package/dist/shared/computedParse.js +278 -0
- package/dist/shared/computedValue.d.ts +47 -0
- package/dist/shared/computedValue.js +191 -0
- package/dist/shared/connection.d.ts +30 -0
- package/dist/shared/connection.js +1 -0
- package/dist/shared/csv.d.ts +20 -0
- package/dist/shared/csv.js +63 -0
- package/dist/shared/dashLayout.d.ts +30 -0
- package/dist/shared/dashLayout.js +132 -0
- package/dist/shared/dashboardAsks.d.ts +93 -0
- package/dist/shared/dashboardAsks.js +129 -0
- package/dist/shared/dashboardTrend.d.ts +20 -0
- package/dist/shared/dashboardTrend.js +50 -0
- package/dist/shared/declaredReferences.d.ts +50 -0
- package/dist/shared/declaredReferences.js +202 -0
- package/dist/shared/definedFields.d.ts +63 -0
- package/dist/shared/definedFields.js +103 -0
- package/dist/shared/definedValues.d.ts +63 -0
- package/dist/shared/definedValues.js +184 -0
- package/dist/shared/displayDefaults.d.ts +135 -0
- package/dist/shared/displayDefaults.js +529 -0
- package/dist/shared/dueDate.d.ts +6 -0
- package/dist/shared/dueDate.js +6 -0
- package/dist/shared/duration.d.ts +4 -0
- package/dist/shared/duration.js +23 -0
- package/dist/shared/fileBounds.d.ts +9 -0
- package/dist/shared/fileBounds.js +9 -0
- package/dist/shared/filter.d.ts +217 -0
- package/dist/shared/filter.js +305 -0
- package/dist/shared/firstPaint.d.ts +24 -0
- package/dist/shared/firstPaint.js +33 -0
- package/dist/shared/form.d.ts +21 -0
- package/dist/shared/form.js +25 -0
- package/dist/shared/formValues.d.ts +104 -0
- package/dist/shared/formValues.js +352 -0
- package/dist/shared/funnel.d.ts +45 -0
- package/dist/shared/funnel.js +108 -0
- package/dist/shared/httpOperations.d.ts +48 -0
- package/dist/shared/httpOperations.js +9 -0
- package/dist/shared/iconGlyphs.d.ts +4 -0
- package/dist/shared/iconGlyphs.js +151 -0
- package/dist/shared/iconWeights.d.ts +3 -0
- package/dist/shared/iconWeights.js +162 -0
- package/dist/shared/imageWidths.d.ts +32 -0
- package/dist/shared/imageWidths.js +38 -0
- package/dist/shared/keysetPage.d.ts +64 -0
- package/dist/shared/keysetPage.js +161 -0
- package/dist/shared/like.d.ts +20 -0
- package/dist/shared/like.js +29 -0
- package/dist/shared/listEmpty.d.ts +12 -0
- package/dist/shared/listEmpty.js +36 -0
- package/dist/shared/listFilters.d.ts +28 -0
- package/dist/shared/listFilters.js +84 -0
- package/dist/shared/listPresentations.d.ts +199 -0
- package/dist/shared/listPresentations.js +7 -0
- package/dist/shared/listQuestion.d.ts +52 -0
- package/dist/shared/listQuestion.js +137 -0
- package/dist/shared/lookPalettes.d.ts +62 -0
- package/dist/shared/lookPalettes.js +306 -0
- package/dist/shared/maskQuoted.d.ts +24 -0
- package/dist/shared/maskQuoted.js +48 -0
- package/dist/shared/mentionLabel.d.ts +19 -0
- package/dist/shared/mentionLabel.js +38 -0
- package/dist/shared/mentionLookup.d.ts +17 -0
- package/dist/shared/mentionLookup.js +64 -0
- package/dist/shared/menuCounts.d.ts +16 -0
- package/dist/shared/menuCounts.js +74 -0
- package/dist/shared/model.d.ts +79 -0
- package/dist/shared/model.js +153 -0
- package/dist/shared/money.d.ts +44 -0
- package/dist/shared/money.js +81 -0
- package/dist/shared/moneyEntry.d.ts +7 -0
- package/dist/shared/moneyEntry.js +27 -0
- package/dist/shared/mount.d.ts +118 -0
- package/dist/shared/mount.js +248 -0
- package/dist/shared/niceTicks.d.ts +26 -0
- package/dist/shared/niceTicks.js +50 -0
- package/dist/shared/order.d.ts +59 -0
- package/dist/shared/order.js +133 -0
- package/dist/shared/page.d.ts +135 -0
- package/dist/shared/page.js +1187 -0
- package/dist/shared/pageAnatomy.d.ts +99 -0
- package/dist/shared/pageAnatomy.js +370 -0
- package/dist/shared/pageConfig.d.ts +25 -0
- package/dist/shared/pageConfig.js +1308 -0
- package/dist/shared/pageEmptyActions.d.ts +12 -0
- package/dist/shared/pageEmptyActions.js +20 -0
- package/dist/shared/pageLayouts.d.ts +72 -0
- package/dist/shared/pageLayouts.js +205 -0
- package/dist/shared/pageModel.d.ts +785 -0
- package/dist/shared/pageModel.js +121 -0
- package/dist/shared/pagePaths.d.ts +180 -0
- package/dist/shared/pagePaths.js +869 -0
- package/dist/shared/pagePlans.d.ts +52 -0
- package/dist/shared/pagePlans.js +108 -0
- package/dist/shared/pageRoutes.d.ts +9 -0
- package/dist/shared/pageRoutes.js +9 -0
- package/dist/shared/query.d.ts +166 -0
- package/dist/shared/query.js +1302 -0
- package/dist/shared/queryDates.d.ts +12 -0
- package/dist/shared/queryDates.js +101 -0
- package/dist/shared/queryOperands.d.ts +99 -0
- package/dist/shared/queryOperands.js +131 -0
- package/dist/shared/reads.d.ts +103 -0
- package/dist/shared/reads.js +154 -0
- package/dist/shared/recordLabel.d.ts +51 -0
- package/dist/shared/recordLabel.js +123 -0
- package/dist/shared/recordReadKeys.d.ts +30 -0
- package/dist/shared/recordReadKeys.js +51 -0
- package/dist/shared/recordReads.d.ts +268 -0
- package/dist/shared/recordReads.js +368 -0
- package/dist/shared/recordview.d.ts +198 -0
- package/dist/shared/recordview.js +356 -0
- package/dist/shared/referenceModel.d.ts +58 -0
- package/dist/shared/referenceModel.js +194 -0
- package/dist/shared/refusal.d.ts +64 -0
- package/dist/shared/refusal.js +144 -0
- package/dist/shared/relatedCreate.d.ts +30 -0
- package/dist/shared/relatedCreate.js +162 -0
- package/dist/shared/resourceHttp.d.ts +464 -0
- package/dist/shared/resourceHttp.js +1 -0
- package/dist/shared/richText.d.ts +85 -0
- package/dist/shared/richText.js +305 -0
- package/dist/shared/rollups.d.ts +143 -0
- package/dist/shared/rollups.js +64 -0
- package/dist/shared/rowScope.d.ts +72 -0
- package/dist/shared/rowScope.js +376 -0
- package/dist/shared/runtimeTables.d.ts +44 -0
- package/dist/shared/runtimeTables.js +79 -0
- package/dist/shared/schema.d.ts +523 -0
- package/dist/shared/schema.js +627 -0
- package/dist/shared/schematext.d.ts +38 -0
- package/dist/shared/schematext.js +101 -0
- package/dist/shared/scopeCatalog.d.ts +29 -0
- package/dist/shared/scopeCatalog.js +468 -0
- package/dist/shared/servedApp.d.ts +681 -0
- package/dist/shared/servedApp.js +45 -0
- package/dist/shared/sessionLease.d.ts +34 -0
- package/dist/shared/sessionLease.js +3 -0
- package/dist/shared/shape.d.ts +55 -0
- package/dist/shared/shape.js +168 -0
- package/dist/shared/slug.d.ts +1 -0
- package/dist/shared/slug.js +12 -0
- package/dist/shared/sortText.d.ts +14 -0
- package/dist/shared/sortText.js +26 -0
- package/dist/shared/sqlRelation.d.ts +4 -0
- package/dist/shared/sqlRelation.js +8 -0
- package/dist/shared/sqlsaved.d.ts +44 -0
- package/dist/shared/sqlsaved.js +63 -0
- package/dist/shared/stitchedPaths.d.ts +27 -0
- package/dist/shared/stitchedPaths.js +38 -0
- package/dist/shared/structuredFilter.d.ts +22 -0
- package/dist/shared/structuredFilter.js +1 -0
- package/dist/shared/subtitlePath.d.ts +20 -0
- package/dist/shared/subtitlePath.js +24 -0
- package/dist/shared/suggest.d.ts +18 -0
- package/dist/shared/suggest.js +59 -0
- package/dist/shared/tableSizing.d.ts +4 -0
- package/dist/shared/tableSizing.js +9 -0
- package/dist/shared/tagLists.d.ts +57 -0
- package/dist/shared/tagLists.js +34 -0
- package/dist/shared/textRules.d.ts +54 -0
- package/dist/shared/textRules.js +133 -0
- package/dist/shared/theme.d.ts +94 -0
- package/dist/shared/theme.js +800 -0
- package/dist/shared/themeColor.d.ts +34 -0
- package/dist/shared/themeColor.js +109 -0
- package/dist/shared/themeFaces.d.ts +30 -0
- package/dist/shared/themeFaces.js +42 -0
- package/dist/shared/themeInk.d.ts +20 -0
- package/dist/shared/themeInk.js +52 -0
- package/dist/shared/themeLooks.d.ts +9 -0
- package/dist/shared/themeLooks.js +572 -0
- package/dist/shared/themeParts.d.ts +26 -0
- package/dist/shared/themeParts.js +71 -0
- package/dist/shared/themePartsCheck.d.ts +21 -0
- package/dist/shared/themePartsCheck.js +91 -0
- package/dist/shared/themePresets.d.ts +85 -0
- package/dist/shared/themePresets.js +139 -0
- package/dist/shared/themeRecipeWords.d.ts +156 -0
- package/dist/shared/themeRecipeWords.js +156 -0
- package/dist/shared/themeRecipes.d.ts +213 -0
- package/dist/shared/themeRecipes.js +234 -0
- package/dist/shared/themeResolve.d.ts +55 -0
- package/dist/shared/themeResolve.js +52 -0
- package/dist/shared/themeTokens.d.ts +107 -0
- package/dist/shared/themeTokens.js +116 -0
- package/dist/shared/themeType.d.ts +38 -0
- package/dist/shared/themeType.js +205 -0
- package/dist/shared/unitWord.d.ts +4 -0
- package/dist/shared/unitWord.js +37 -0
- package/dist/shared/view.d.ts +195 -0
- package/dist/shared/view.js +987 -0
- package/dist/shared/viewLabels.d.ts +47 -0
- package/dist/shared/viewLabels.js +459 -0
- package/dist/shared/viewPaths.d.ts +248 -0
- package/dist/shared/viewPaths.js +294 -0
- package/dist/shared/viewtext.d.ts +57 -0
- package/dist/shared/viewtext.js +282 -0
- package/dist/shared/vocabulary.d.ts +71 -0
- package/dist/shared/vocabulary.js +249 -0
- package/dist/shared/weekStart.d.ts +7 -0
- package/dist/shared/weekStart.js +36 -0
- package/mark.svg +1 -0
- package/package.json +261 -4
- package/schema/model.d.ts +298 -0
- package/schema/theme.d.ts +587 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Refusal, type Filter, type ScopedReads, type WriteTransaction } from '../adapter.js';
|
|
2
|
+
import type { RowScope } from '../../shared/rowScope.js';
|
|
3
|
+
import type { DatabaseSchema, TableMeta } from '../../shared/schema.js';
|
|
4
|
+
/** Physical metadata read on the writer inside its own transaction. `writable`
|
|
5
|
+
* names the tables this scope may mutate; everything else stays in the graph
|
|
6
|
+
* so a hidden dependent cannot vanish from the FK checks. */
|
|
7
|
+
export interface ScopeCatalog {
|
|
8
|
+
schema: DatabaseSchema;
|
|
9
|
+
writable: ReadonlySet<string>;
|
|
10
|
+
/** What in the database keeps a table out of `writable`, named where it can be: its triggers, and on PostgreSQL its
|
|
11
|
+
* rules, policies or row security. A refusal says it, so the owner knows what to drop or move. */
|
|
12
|
+
blockedBy?: ReadonlyMap<string, string>;
|
|
13
|
+
}
|
|
14
|
+
/** A table's triggers as a refusal names them: `the trigger "audit_order"`, `the triggers "a", "b"`. */
|
|
15
|
+
export declare const triggerNames: (names: readonly string[]) => string;
|
|
16
|
+
/** Row locks taken on the writer before a guard read, for engines whose
|
|
17
|
+
* transaction does not already exclude every other writer. SQLite passes
|
|
18
|
+
* none: `BEGIN IMMEDIATE` is the lock. Each receives the physical table and
|
|
19
|
+
* the complete-key (target) or parent-key (parent) filter. */
|
|
20
|
+
interface ScopeRowLocks {
|
|
21
|
+
target(table: TableMeta, filter: Filter): Promise<void>;
|
|
22
|
+
parent(table: TableMeta, filter: Filter): Promise<void>;
|
|
23
|
+
/**
|
|
24
|
+
* Run `ride` the moment the update of `table` is sent, so the read that
|
|
25
|
+
* proves the row still in scope leaves behind it in the same round trip
|
|
26
|
+
* (postgresTransactionHandle.ts behindWrite). Answers a withdraw, true when
|
|
27
|
+
* `ride` never ran; undefined where reads are not sent behind a write.
|
|
28
|
+
*/
|
|
29
|
+
behind?(table: TableMeta, ride: () => void): (() => boolean) | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Whether statements asked without waiting leave together (a writer that
|
|
32
|
+
* pipelines, postgresPool.ts). A row's lock and the guard read behind it,
|
|
33
|
+
* and every parent's lock and count, are then sent at once, in the order
|
|
34
|
+
* they would have been asked one by one, and judged in that order when the
|
|
35
|
+
* flight is answered: the server still grants each lock before it runs the
|
|
36
|
+
* read behind it, so each read is as fresh as it was a round trip later.
|
|
37
|
+
* Elsewhere each is asked once the one before it is answered.
|
|
38
|
+
*/
|
|
39
|
+
readonly together?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The outer bound a command's scope sits inside, when its host has one: a
|
|
43
|
+
* tenant's rows within the command's footprint, whatever the member's own
|
|
44
|
+
* row policy narrows (tenantWriteKindMember.ts). `reads` reads under it, and
|
|
45
|
+
* `whole` names the tables where the principal's scope is the bound itself —
|
|
46
|
+
* nothing within the bound is hidden from it there.
|
|
47
|
+
*
|
|
48
|
+
* Two checks are judged against it, and only these:
|
|
49
|
+
* - a command's insert may land on a row the principal cannot read, so long
|
|
50
|
+
* as it lands inside the bound; it is answered its key and the values it
|
|
51
|
+
* wrote, never the stored row (a requester's rating opens a desk-only
|
|
52
|
+
* follow-up);
|
|
53
|
+
* - a delete of a row others reference is no longer refused outright: once
|
|
54
|
+
* the row is locked, every table that references it must be `whole` and
|
|
55
|
+
* hold no row referencing it at all, counted on the writer itself across
|
|
56
|
+
* every row of that table — so a cascade can never reach a row anyone hid.
|
|
57
|
+
*/
|
|
58
|
+
export interface ScopeBound {
|
|
59
|
+
readonly reads: Pick<ScopedReads, 'query' | 'count'>;
|
|
60
|
+
readonly whole: ReadonlySet<string>;
|
|
61
|
+
}
|
|
62
|
+
/** The tables where `scope` is exactly `bound`, and the bound admits rows at all: nothing in it is hidden there. */
|
|
63
|
+
export declare function wholeWithin(scope: Readonly<Record<string, unknown>>, bound: Readonly<Record<string, unknown>>): ReadonlySet<string>;
|
|
64
|
+
/** A bound names exactly the scope's tables: it bounds that scope, never another. */
|
|
65
|
+
export declare function sameTables(scope: Readonly<Record<string, unknown>>, bound: Readonly<Record<string, unknown>>): boolean;
|
|
66
|
+
/** A write the row scope refuses: for good, for this request — outside the principal's rows, off the footprint, on a
|
|
67
|
+
* table it cannot bound. Its own class so the executor can say it as forbidden rather than as a while (503); the
|
|
68
|
+
* words stay value-free, the reason recorded beneath them for the owner. */
|
|
69
|
+
export declare class ScopeRefusal extends Refusal {
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The principal's scope and the reads it compiles to, for a transaction that
|
|
73
|
+
* may create a parent and then its children. A scope is resolved before the
|
|
74
|
+
* transaction begins, so a child rule that names its parents by key (`request_id
|
|
75
|
+
* in [...]`) cannot name a parent this transaction made: a resident filing a
|
|
76
|
+
* repair with its first message was refused the message. Rows this transaction
|
|
77
|
+
* inserted inside the principal's rows join those key lists for the rest of the
|
|
78
|
+
* transaction — every other condition of the child's rule still holds, a rule
|
|
79
|
+
* of `false` stays false, and a parent anyone else made gains nothing.
|
|
80
|
+
*/
|
|
81
|
+
export interface ScopeOwnRows {
|
|
82
|
+
readonly scope: RowScope;
|
|
83
|
+
readonly reads: (scope: RowScope, guard: boolean) => Pick<ScopedReads, 'query' | 'count'>;
|
|
84
|
+
}
|
|
85
|
+
/** Runs INSIDE a single queued operation. The supplied guards retain actual
|
|
86
|
+
* stored scalar types, not display conversions (a blob label is not a key).
|
|
87
|
+
* This capability deliberately refuses side effects it cannot bound. */
|
|
88
|
+
export declare function createScopedTransaction(catalog: ScopeCatalog, records: WriteTransaction, reads: Pick<ScopedReads, 'query' | 'count'>, guards: Pick<ScopedReads, 'query' | 'count'>, locks?: ScopeRowLocks, bound?: ScopeBound, own?: ScopeOwnRows): WriteTransaction;
|
|
89
|
+
export {};
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { Refusal } from '../adapter.js';
|
|
2
|
+
import { recordRefusalCause } from '../../shared/refusal.js';
|
|
3
|
+
/** A table's triggers as a refusal names them: `the trigger "audit_order"`, `the triggers "a", "b"`. */
|
|
4
|
+
export const triggerNames = (names) => `the trigger${names.length === 1 ? '' : 's'} ${names.map((name) => JSON.stringify(name)).join(', ')}`;
|
|
5
|
+
/** The tables where `scope` is exactly `bound`, and the bound admits rows at all: nothing in it is hidden there. */
|
|
6
|
+
export function wholeWithin(scope, bound) {
|
|
7
|
+
return new Set(Object.keys(bound).filter((table) => bound[table] !== false && Object.hasOwn(scope, table) && JSON.stringify(scope[table]) === JSON.stringify(bound[table])));
|
|
8
|
+
}
|
|
9
|
+
/** A bound names exactly the scope's tables: it bounds that scope, never another. */
|
|
10
|
+
export function sameTables(scope, bound) {
|
|
11
|
+
const tables = Object.keys(scope);
|
|
12
|
+
return tables.length === Object.keys(bound).length && tables.every((table) => Object.hasOwn(bound, table));
|
|
13
|
+
}
|
|
14
|
+
/* The caller hears one sentence, whatever the rule: which row a scope hides,
|
|
15
|
+
and that it exists, is what the scope keeps. The rule and the row are
|
|
16
|
+
recorded for the operator (shared/refusal.ts) — a command that failed here
|
|
17
|
+
used to answer a bare 503 with nothing anywhere saying which check it was. */
|
|
18
|
+
/** A write the row scope refuses: for good, for this request — outside the principal's rows, off the footprint, on a
|
|
19
|
+
* table it cannot bound. Its own class so the executor can say it as forbidden rather than as a while (503); the
|
|
20
|
+
* words stay value-free, the reason recorded beneath them for the owner. */
|
|
21
|
+
export class ScopeRefusal extends Refusal {
|
|
22
|
+
}
|
|
23
|
+
const refuse = (reason) => {
|
|
24
|
+
throw recordRefusalCause(new ScopeRefusal('This operation is not available in the current row scope.'), reason);
|
|
25
|
+
};
|
|
26
|
+
const keyText = (key) => Object.entries(key)
|
|
27
|
+
.map(([column, value]) => `${column} ${JSON.stringify(value)}`)
|
|
28
|
+
.join(', ');
|
|
29
|
+
const comparable = (value) => typeof value === 'string' || (typeof value === 'number' && Number.isFinite(value));
|
|
30
|
+
/** `scope` with each child rule's key conditions on a reference to a table in `made` extended by those rows' keys. */
|
|
31
|
+
function withOwnRows(schema, scope, made) {
|
|
32
|
+
const out = { ...scope };
|
|
33
|
+
for (const [table, rule] of Object.entries(scope)) {
|
|
34
|
+
if (typeof rule !== 'object')
|
|
35
|
+
continue;
|
|
36
|
+
const extra = new Map();
|
|
37
|
+
for (const fk of schema.foreignKeys) {
|
|
38
|
+
// A composite reference is named by no single condition: it is left as the policy said it.
|
|
39
|
+
if (fk.from.table !== table || fk.from.columns.length !== 1)
|
|
40
|
+
continue;
|
|
41
|
+
const rows = made.get(fk.to.table);
|
|
42
|
+
if (!rows?.length)
|
|
43
|
+
continue;
|
|
44
|
+
const values = rows.map((row) => row[fk.to.columns[0]]).filter(comparable);
|
|
45
|
+
if (values.length)
|
|
46
|
+
extra.set(fk.from.columns[0], [...(extra.get(fk.from.columns[0]) ?? []), ...values]);
|
|
47
|
+
}
|
|
48
|
+
if (!extra.size)
|
|
49
|
+
continue;
|
|
50
|
+
out[table] = {
|
|
51
|
+
...rule,
|
|
52
|
+
groups: rule.groups.map((group) => group.map((condition) => {
|
|
53
|
+
const added = extra.get(condition.column);
|
|
54
|
+
if (!added || condition.other !== undefined || condition.any !== undefined)
|
|
55
|
+
return condition;
|
|
56
|
+
if (condition.op === '=' && comparable(condition.value))
|
|
57
|
+
return { ...condition, op: 'in', value: [condition.value, ...added] };
|
|
58
|
+
if (condition.op === 'in' && Array.isArray(condition.value))
|
|
59
|
+
return { ...condition, value: [...condition.value, ...added] };
|
|
60
|
+
return condition;
|
|
61
|
+
})),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
/** Runs INSIDE a single queued operation. The supplied guards retain actual
|
|
67
|
+
* stored scalar types, not display conversions (a blob label is not a key).
|
|
68
|
+
* This capability deliberately refuses side effects it cannot bound. */
|
|
69
|
+
export function createScopedTransaction(catalog, records, reads, guards, locks, bound, own) {
|
|
70
|
+
/* The rows this transaction inserted inside the principal's rows, by table, and the reads that count them in
|
|
71
|
+
(ScopeOwnRows). Rebuilt on each such insert; until the first, the reads are the ones resolved for the request. */
|
|
72
|
+
const made = new Map();
|
|
73
|
+
const madeBy = (table, row) => {
|
|
74
|
+
if (!own)
|
|
75
|
+
return;
|
|
76
|
+
made.set(table.id, [...(made.get(table.id) ?? []), row]);
|
|
77
|
+
const scope = withOwnRows(catalog.schema, own.scope, made);
|
|
78
|
+
reads = own.reads(scope, false);
|
|
79
|
+
guards = own.reads(scope, true);
|
|
80
|
+
};
|
|
81
|
+
const target = (id) => {
|
|
82
|
+
const table = catalog.schema.tables.find((table) => table.id === id);
|
|
83
|
+
if (!table)
|
|
84
|
+
return refuse(`"${id}" is not in this write's footprint: a command writes only the tables it declares.`);
|
|
85
|
+
if (!catalog.writable.has(id)) {
|
|
86
|
+
/* A trigger writes what no row policy bounds, so a scoped write never fires one: name it, or the owner is left
|
|
87
|
+
guessing why a table they declared written is not. */
|
|
88
|
+
const by = catalog.blockedBy?.get(id);
|
|
89
|
+
return refuse(by
|
|
90
|
+
? `"${id}" cannot be written under a row policy: it has ${by}, whose writes the policy cannot bound.`
|
|
91
|
+
: `"${id}" is read-only in this scope: the command does not declare it written, or the principal's rows do not admit writing it.`);
|
|
92
|
+
}
|
|
93
|
+
if (!table.columns.some((column) => column.primaryKey))
|
|
94
|
+
return refuse(`"${id}" has no primary key, so a scoped write cannot prove which row it changed.`);
|
|
95
|
+
return table;
|
|
96
|
+
};
|
|
97
|
+
const keyFilter = (table, key) => {
|
|
98
|
+
const pk = table.columns.filter((column) => column.primaryKey).map((column) => column.name);
|
|
99
|
+
if (!key || Object.keys(key).length !== pk.length || !pk.every((column) => Object.hasOwn(key, column) && comparable(key[column]))) {
|
|
100
|
+
return refuse(`A key for "${table.id}" names exactly its primary key (${pk.join(', ')}), each a text or a number.`);
|
|
101
|
+
}
|
|
102
|
+
return { groups: [pk.map((column) => ({ column, op: '=', value: key[column] }))] };
|
|
103
|
+
};
|
|
104
|
+
/* A row by its key, read after its lock. Lock before looking: the guard's
|
|
105
|
+
snapshot is then the row no other writer can move until this transaction
|
|
106
|
+
ends, so what the scope checks is what the write will change. Where the
|
|
107
|
+
writer sends together, the read leaves right behind the lock, in its
|
|
108
|
+
flight; the server runs it only once the lock is granted. */
|
|
109
|
+
const lockedRead = async (table, filter, within) => {
|
|
110
|
+
const locked = locks?.target(table, filter);
|
|
111
|
+
const read = () => within.query({ table: table.id, filter, limit: 2, offset: 0 });
|
|
112
|
+
if (!locks?.together) {
|
|
113
|
+
await locked;
|
|
114
|
+
return read();
|
|
115
|
+
}
|
|
116
|
+
const sent = read();
|
|
117
|
+
// Both heard before either is judged: the lock's failure, if it failed, is the one said.
|
|
118
|
+
await Promise.allSettled([locked, sent]);
|
|
119
|
+
await locked;
|
|
120
|
+
return sent;
|
|
121
|
+
};
|
|
122
|
+
const one = (table, key, result) => {
|
|
123
|
+
if (result.rows.length !== 1) {
|
|
124
|
+
return refuse(`The "${table.id}" row ${keyText(key)} is outside the principal's rows: the row policy (or tenant) does not admit it, before or after this write.`);
|
|
125
|
+
}
|
|
126
|
+
const row = result.rows[0];
|
|
127
|
+
// Null / opaque / nonportable keys cannot prove stored-row identity.
|
|
128
|
+
keyFilter(table, Object.fromEntries(table.columns.filter((column) => column.primaryKey).map((column) => [column.name, row[column.name]])));
|
|
129
|
+
return row;
|
|
130
|
+
};
|
|
131
|
+
const stored = async (table, key, within = guards) => one(table, key, await lockedRead(table, keyFilter(table, key), within));
|
|
132
|
+
/* Every reference a write makes must land on a row the principal reads:
|
|
133
|
+
all of a new row's, and of an updated row only those whose columns the
|
|
134
|
+
update sets. A reference the update leaves as it was is not this write's
|
|
135
|
+
to vouch for — it was made before, and refusing over it refused every
|
|
136
|
+
edit of a row whose unrelated parent (a lender's relationship manager on
|
|
137
|
+
a dealer) lies outside the scope, while telling the caller that it does.
|
|
138
|
+
|
|
139
|
+
Each reference is checked by its parent's lock, then that parent's count
|
|
140
|
+
under the principal's rows; a reference that cannot be checked ends the
|
|
141
|
+
list, refused there, so the checks before it still speak first. */
|
|
142
|
+
const parentChecks = (table, row, set) => {
|
|
143
|
+
const checks = [];
|
|
144
|
+
for (const fk of catalog.schema.foreignKeys.filter((fk) => fk.from.table === table.id && (!set || fk.from.columns.some((column) => set.has(column))))) {
|
|
145
|
+
const values = fk.from.columns.map((column) => row[column]);
|
|
146
|
+
if (values.some((value) => value === null))
|
|
147
|
+
continue;
|
|
148
|
+
if (!values.every(comparable)) {
|
|
149
|
+
checks.push({ refused: `"${table.id}".${fk.from.columns.join('+')} holds a value that is not a text or a number.` });
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
// Parent columns stay on the SQL left-hand side so SQLite's parent
|
|
153
|
+
// affinity and collation, rather than JavaScript equality, decide.
|
|
154
|
+
const filter = {
|
|
155
|
+
groups: [fk.to.columns.map((column, index) => ({ column, op: '=', value: values[index] }))],
|
|
156
|
+
};
|
|
157
|
+
const parent = catalog.schema.tables.find((table) => table.id === fk.to.table);
|
|
158
|
+
if (!parent) {
|
|
159
|
+
checks.push({
|
|
160
|
+
refused: `"${table.id}" references "${fk.to.table}", which is not in this write's footprint: declare it read by the command.`,
|
|
161
|
+
});
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
checks.push({ fk, values, filter, parent });
|
|
165
|
+
}
|
|
166
|
+
return checks;
|
|
167
|
+
};
|
|
168
|
+
const unseen = (check) => `"${check.fk.from.table}".${check.fk.from.columns.join('+')} points at the "${check.fk.to.table}" row ${keyText(Object.fromEntries(check.fk.to.columns.map((column, index) => [column, check.values[index]])))}, which the principal's rows do not include (or the command does not declare "${check.fk.to.table}" read).`;
|
|
169
|
+
/** The checks sent at once (locks.together): each parent's lock, then its count, every answer heard. */
|
|
170
|
+
const ask = (checks) => checks.map((check) => {
|
|
171
|
+
if ('refused' in check)
|
|
172
|
+
return check;
|
|
173
|
+
const locked = locks?.parent(check.parent, check.filter) ?? Promise.resolve();
|
|
174
|
+
const counted = guards.count(check.fk.to.table, check.filter);
|
|
175
|
+
locked.catch(() => { });
|
|
176
|
+
counted.catch(() => { });
|
|
177
|
+
return { ...check, locked, counted };
|
|
178
|
+
});
|
|
179
|
+
/* Checks asked ahead of the row they are for (built from what the write
|
|
180
|
+
returned, or from what an update sets), used only where they name exactly
|
|
181
|
+
the references the row as stored holds: the same statements, sent sooner. */
|
|
182
|
+
const sameChecks = (asked, checks) => asked.length === checks.length &&
|
|
183
|
+
asked.every((guess, index) => {
|
|
184
|
+
const check = checks[index];
|
|
185
|
+
if ('refused' in guess || 'refused' in check)
|
|
186
|
+
return 'refused' in guess && 'refused' in check && guess.refused === check.refused;
|
|
187
|
+
return guess.fk === check.fk && guess.values.every((value, at) => value === check.values[at]);
|
|
188
|
+
});
|
|
189
|
+
const parents = async (table, row, set, guessed) => {
|
|
190
|
+
const checks = parentChecks(table, row, set);
|
|
191
|
+
if (!locks?.together) {
|
|
192
|
+
for (const check of checks) {
|
|
193
|
+
if ('refused' in check)
|
|
194
|
+
return refuse(check.refused);
|
|
195
|
+
// A parent's visibility must hold until commit, not just at this read.
|
|
196
|
+
await locks?.parent(check.parent, check.filter);
|
|
197
|
+
if ((await guards.count(check.fk.to.table, check.filter)) !== 1)
|
|
198
|
+
return refuse(unseen(check));
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const asked = guessed && sameChecks(guessed, checks) ? guessed : ask(checks);
|
|
203
|
+
// Every answer is heard before any is judged: nothing is left in flight behind a refusal.
|
|
204
|
+
await Promise.allSettled(asked.flatMap((check) => ('refused' in check ? [] : [check.locked, check.counted])));
|
|
205
|
+
for (const check of asked) {
|
|
206
|
+
if ('refused' in check)
|
|
207
|
+
return refuse(check.refused);
|
|
208
|
+
await check.locked;
|
|
209
|
+
if ((await check.counted) !== 1)
|
|
210
|
+
return refuse(unseen(check));
|
|
211
|
+
}
|
|
212
|
+
};
|
|
213
|
+
return Object.freeze({
|
|
214
|
+
query: (request) => reads.query(request),
|
|
215
|
+
count: (table, filter) => reads.count(table, filter),
|
|
216
|
+
async insert(request) {
|
|
217
|
+
const table = target(request.table);
|
|
218
|
+
const result = await records.insert(request);
|
|
219
|
+
const filter = keyFilter(table, result.key);
|
|
220
|
+
/* The new row locked and read back, and where the writer sends together
|
|
221
|
+
its references checked in the same flight, from the row the INSERT
|
|
222
|
+
returned (parents uses them only where the stored row holds exactly
|
|
223
|
+
those). No other transaction can see this row yet, so its lock is
|
|
224
|
+
granted at once and the read answers what a count before it would. */
|
|
225
|
+
const read = lockedRead(table, filter, guards);
|
|
226
|
+
const guessed = locks?.together && result.row ? ask(parentChecks(table, result.row)) : undefined;
|
|
227
|
+
read.catch(() => { });
|
|
228
|
+
const found = await read;
|
|
229
|
+
// Inside the bound and outside the principal's rows: the command's own row, answered by its key and what it wrote.
|
|
230
|
+
if (bound && found.rows.length !== 1) {
|
|
231
|
+
await parents(table, await stored(table, result.key, bound.reads), undefined, guessed);
|
|
232
|
+
return { key: result.key, applied: result.applied, sql: result.sql };
|
|
233
|
+
}
|
|
234
|
+
const row = one(table, result.key, found);
|
|
235
|
+
await parents(table, row, undefined, guessed);
|
|
236
|
+
madeBy(table, row);
|
|
237
|
+
return result;
|
|
238
|
+
},
|
|
239
|
+
async update(request) {
|
|
240
|
+
const table = target(request.table);
|
|
241
|
+
const protectedColumns = new Set([
|
|
242
|
+
...table.columns.filter((column) => column.primaryKey).map((column) => column.name),
|
|
243
|
+
...catalog.schema.foreignKeys.filter((fk) => fk.to.table === table.id).flatMap((fk) => fk.to.columns),
|
|
244
|
+
]);
|
|
245
|
+
const moved = Object.keys(request.values ?? {}).filter((column) => protectedColumns.has(column));
|
|
246
|
+
if (moved.length)
|
|
247
|
+
return refuse(`A scoped update may not change "${table.id}".${moved.join(', ')}: a key, or a column another table references.`);
|
|
248
|
+
const before = await stored(table, request.key);
|
|
249
|
+
const set = new Set(Object.keys(request.values ?? {}));
|
|
250
|
+
/* The row read again after the write, sent the moment the write is: it
|
|
251
|
+
reads what the write wrote, and leaves in the write's round trip, with
|
|
252
|
+
the references the write sets checked behind it, as the row will hold
|
|
253
|
+
them. A write that fails aborts them too, and the write's failure is
|
|
254
|
+
the one thrown. */
|
|
255
|
+
const after = {};
|
|
256
|
+
const withdraw = locks?.behind?.(table, () => {
|
|
257
|
+
after.row = stored(table, request.key);
|
|
258
|
+
after.row.catch(() => { });
|
|
259
|
+
if (locks.together)
|
|
260
|
+
after.guessed = ask(parentChecks(table, { ...before, ...request.values }, set));
|
|
261
|
+
});
|
|
262
|
+
let result;
|
|
263
|
+
try {
|
|
264
|
+
result = await records.update(request);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
withdraw?.();
|
|
268
|
+
}
|
|
269
|
+
if (result.affected !== 1)
|
|
270
|
+
return refuse(`The update of "${table.id}" changed ${result.affected} rows, not one.`);
|
|
271
|
+
await parents(table, await (after.row ?? stored(table, request.key)), set, after.guessed);
|
|
272
|
+
return result;
|
|
273
|
+
},
|
|
274
|
+
async remove(request) {
|
|
275
|
+
const table = target(request.table);
|
|
276
|
+
const children = catalog.schema.foreignKeys.filter((fk) => fk.to.table === table.id);
|
|
277
|
+
const hidden = children.find((fk) => !bound?.whole.has(fk.from.table));
|
|
278
|
+
if (hidden)
|
|
279
|
+
return refuse(`A scoped command may not delete from "${table.id}": "${hidden.from.table}" references it, and a hidden row might.`);
|
|
280
|
+
const row = await stored(table, request.key);
|
|
281
|
+
/* Locked first (stored), so no reference can be made to it while these
|
|
282
|
+
are counted: across every row of each table, on the writer itself,
|
|
283
|
+
since the principal sees every row the bound holds there and a row
|
|
284
|
+
outside the bound pointing in is the one a cascade must never reach. */
|
|
285
|
+
for (const fk of children) {
|
|
286
|
+
const values = fk.to.columns.map((column) => row[column]);
|
|
287
|
+
if (values.some((value) => value === null))
|
|
288
|
+
continue;
|
|
289
|
+
if (!values.every(comparable))
|
|
290
|
+
return refuse(`"${table.id}".${fk.to.columns.join('+')} holds a value that is not a text or a number.`);
|
|
291
|
+
const filter = {
|
|
292
|
+
groups: [fk.from.columns.map((column, index) => ({ column, op: '=', value: values[index] }))],
|
|
293
|
+
};
|
|
294
|
+
if ((await records.count(fk.from.table, filter)) !== 0)
|
|
295
|
+
return refuse(`A scoped command may not delete the "${table.id}" row ${keyText(request.key)}: "${fk.from.table}" still references it.`);
|
|
296
|
+
}
|
|
297
|
+
const result = await records.remove(request);
|
|
298
|
+
if (result.affected !== 1)
|
|
299
|
+
return refuse(`The delete from "${table.id}" removed ${result.affected} rows, not one.`);
|
|
300
|
+
return result;
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { type DatabaseSchema } from '../../shared/schema.js';
|
|
3
|
+
import { type ScopeCatalog } from './scopedTransaction.js';
|
|
4
|
+
export type SqliteScopeCatalog = ScopeCatalog;
|
|
5
|
+
/** Call only on the writer inside BEGIN IMMEDIATE. No cached/public catalog,
|
|
6
|
+
* estimates or data scans: the lease's physical metadata is the authority.
|
|
7
|
+
* A non-writable table remains in the graph so hidden dependents cannot vanish.
|
|
8
|
+
*/
|
|
9
|
+
export declare function readSqliteScopeCatalog(db: DatabaseSync): SqliteScopeCatalog;
|
|
10
|
+
/** Metadata-only read attestation inside the caller's owned snapshot. Exposes
|
|
11
|
+
* no writer eligibility. Keep every non-engine table and dependency, including
|
|
12
|
+
* private/unmapped objects, so an unknown business edge cannot disappear. */
|
|
13
|
+
export declare function readSqliteReadCatalog(db: DatabaseSync): {
|
|
14
|
+
schema: DatabaseSchema;
|
|
15
|
+
readable: ReadonlySet<string>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { ANSI_STYLE, Refusal } from '../adapter.js';
|
|
2
|
+
import { chooseColumnReference, deleteRuleOf, } from '../../shared/schema.js';
|
|
3
|
+
import { mapScopeCatalog } from '../../shared/scopeCatalog.js';
|
|
4
|
+
import { triggerNames } from './scopedTransaction.js';
|
|
5
|
+
/* `table_xinfo`'s hidden: 0 a plain column, 1 a virtual table's own (its module's plumbing), 2 and 3 generated
|
|
6
|
+
(virtual, stored). A generated column keeps its table in scope: it is computed from the row the policy or the
|
|
7
|
+
tenant predicate already bounds, so a scoped read reads it and a scoped write leaves it to the database (sqlite.ts
|
|
8
|
+
refuses writing one). */
|
|
9
|
+
const generated = (column) => column.hidden === 2 || column.hidden === 3;
|
|
10
|
+
// SQLite's built-in identifier matching folds ASCII, not arbitrary Unicode.
|
|
11
|
+
const folded = (name) => name.replace(/[A-Z]/g, (char) => char.toLowerCase());
|
|
12
|
+
const refuse = (cause) => {
|
|
13
|
+
throw new Refusal('SQLite scoped transactions require a complete, unambiguous catalog and enabled foreign keys.', cause === undefined ? {} : { cause });
|
|
14
|
+
};
|
|
15
|
+
// SQLite's primary result codes for an engine that could not answer, rather
|
|
16
|
+
// than a catalog that is wrong: BUSY, LOCKED, NOMEM, INTERRUPT, IOERR, FULL,
|
|
17
|
+
// CANTOPEN, PROTOCOL. Extended codes keep the primary code in the low byte.
|
|
18
|
+
const UNAVAILABLE = new Set([5, 6, 7, 9, 10, 13, 14, 15]);
|
|
19
|
+
function identities(items) {
|
|
20
|
+
const result = new Map();
|
|
21
|
+
for (const item of items) {
|
|
22
|
+
if (typeof item.name !== 'string' || !item.name || result.has(folded(item.name)))
|
|
23
|
+
return refuse();
|
|
24
|
+
result.set(folded(item.name), item);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
/** Call only on the writer inside BEGIN IMMEDIATE. No cached/public catalog,
|
|
29
|
+
* estimates or data scans: the lease's physical metadata is the authority.
|
|
30
|
+
* A non-writable table remains in the graph so hidden dependents cannot vanish.
|
|
31
|
+
*/
|
|
32
|
+
export function readSqliteScopeCatalog(db) {
|
|
33
|
+
const { schema, writable, blockedBy } = scanSqliteScopeCatalog(db);
|
|
34
|
+
return { schema, writable, ...(blockedBy.size ? { blockedBy } : {}) };
|
|
35
|
+
}
|
|
36
|
+
/** Metadata-only read attestation inside the caller's owned snapshot. Exposes
|
|
37
|
+
* no writer eligibility. Keep every non-engine table and dependency, including
|
|
38
|
+
* private/unmapped objects, so an unknown business edge cannot disappear. */
|
|
39
|
+
export function readSqliteReadCatalog(db) {
|
|
40
|
+
if (!db.isTransaction)
|
|
41
|
+
return refuse();
|
|
42
|
+
const { schema, readable } = scanSqliteScopeCatalog(db);
|
|
43
|
+
const ids = new Map(schema.tables.filter((table) => !folded(table.id).startsWith('sqlite_')).map((table) => [table.id, table.id]));
|
|
44
|
+
return { schema: mapScopeCatalog(schema, ids), readable };
|
|
45
|
+
}
|
|
46
|
+
function scanSqliteScopeCatalog(db) {
|
|
47
|
+
try {
|
|
48
|
+
if (db.prepare('PRAGMA foreign_keys').get()?.foreign_keys !== 1)
|
|
49
|
+
return refuse();
|
|
50
|
+
const entries = db
|
|
51
|
+
.prepare("SELECT name, type FROM main.sqlite_schema WHERE type IN ('table', 'view') ORDER BY name")
|
|
52
|
+
.all();
|
|
53
|
+
const listed = db.prepare('PRAGMA main.table_list').all();
|
|
54
|
+
// Unknown PRAGMAs can return nothing without throwing. Never interpret
|
|
55
|
+
// unavailable table-kind metadata as proof that a table is ordinary.
|
|
56
|
+
if (!listed.some((table) => table.schema === 'main' && table.name === 'sqlite_schema' && table.type === 'table'))
|
|
57
|
+
return refuse();
|
|
58
|
+
const kinds = identities(listed.filter((table) => table.schema === 'main'));
|
|
59
|
+
const blocked = new Set();
|
|
60
|
+
const triggered = new Map();
|
|
61
|
+
for (const namespace of ['main', 'temp']) {
|
|
62
|
+
const triggers = db
|
|
63
|
+
.prepare(`SELECT tbl_name AS name, name AS trigger FROM ${namespace}.sqlite_schema WHERE type = 'trigger' ORDER BY name`)
|
|
64
|
+
.all();
|
|
65
|
+
for (const trigger of triggers) {
|
|
66
|
+
if (typeof trigger.name !== 'string' || typeof trigger.trigger !== 'string')
|
|
67
|
+
return refuse();
|
|
68
|
+
blocked.add(folded(trigger.name));
|
|
69
|
+
triggered.set(folded(trigger.name), [...(triggered.get(folded(trigger.name)) ?? []), trigger.trigger]);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const blockedBy = new Map();
|
|
73
|
+
const shadowed = new Set();
|
|
74
|
+
for (const table of db.prepare("SELECT name FROM temp.sqlite_schema WHERE type IN ('table', 'view')").all()) {
|
|
75
|
+
if (typeof table.name !== 'string')
|
|
76
|
+
return refuse();
|
|
77
|
+
blocked.add(folded(table.name));
|
|
78
|
+
shadowed.add(folded(table.name));
|
|
79
|
+
}
|
|
80
|
+
const tables = [];
|
|
81
|
+
const columnsByTable = new Map();
|
|
82
|
+
const writable = new Set();
|
|
83
|
+
const readable = new Set();
|
|
84
|
+
identities(entries);
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
const kind = kinds.get(folded(entry.name));
|
|
87
|
+
if (!kind ||
|
|
88
|
+
kind.name !== entry.name ||
|
|
89
|
+
!['table', 'view', 'virtual', 'shadow'].includes(kind.type) ||
|
|
90
|
+
(entry.type === 'view') !== (kind.type === 'view'))
|
|
91
|
+
return refuse();
|
|
92
|
+
const columns = db.prepare(`PRAGMA main.table_xinfo(${ANSI_STYLE.quote(entry.name)})`).all();
|
|
93
|
+
identities(columns);
|
|
94
|
+
if (!columns.length || columns.length !== kind.ncol)
|
|
95
|
+
return refuse();
|
|
96
|
+
for (const [i, column] of columns.entries()) {
|
|
97
|
+
if (column.cid !== i ||
|
|
98
|
+
typeof column.type !== 'string' ||
|
|
99
|
+
!Number.isInteger(column.pk) ||
|
|
100
|
+
column.pk < 0 ||
|
|
101
|
+
![0, 1].includes(column.notnull) ||
|
|
102
|
+
![0, 1, 2, 3].includes(column.hidden) ||
|
|
103
|
+
(column.dflt_value !== null && typeof column.dflt_value !== 'string'))
|
|
104
|
+
return refuse();
|
|
105
|
+
}
|
|
106
|
+
const primary = columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk);
|
|
107
|
+
if (primary.some((column, i) => column.pk !== i + 1))
|
|
108
|
+
return refuse();
|
|
109
|
+
const indexes = db.prepare(`PRAGMA main.index_list(${ANSI_STYLE.quote(entry.name)})`).all();
|
|
110
|
+
// INTEGER PRIMARY KEY DESC has a PK index and is not a rowid alias.
|
|
111
|
+
const identity = kind.type === 'table' &&
|
|
112
|
+
kind.wr === 0 &&
|
|
113
|
+
primary.length === 1 &&
|
|
114
|
+
/^INTEGER$/i.test(primary[0].type.trim()) &&
|
|
115
|
+
!indexes.some((index) => index.origin === 'pk')
|
|
116
|
+
? primary[0].name
|
|
117
|
+
: undefined;
|
|
118
|
+
tables.push({
|
|
119
|
+
id: entry.name,
|
|
120
|
+
name: entry.name,
|
|
121
|
+
schema: 'main',
|
|
122
|
+
isView: kind.type === 'view',
|
|
123
|
+
columns: columns.map((column) => ({
|
|
124
|
+
name: column.name,
|
|
125
|
+
type: column.type || (kind.type === 'view' ? '' : 'BLOB'),
|
|
126
|
+
primaryKey: column.pk > 0,
|
|
127
|
+
nullable: column.notnull === 0 && column.name !== identity && !(column.pk > 0 && (kind.wr === 1 || kind.strict === 1)),
|
|
128
|
+
...(column.dflt_value !== null ? { default: column.dflt_value } : {}),
|
|
129
|
+
...(column.name === identity ? { identity: 'by-default' } : {}),
|
|
130
|
+
// The database computes a generated column from its row: read like any other, never written.
|
|
131
|
+
...(generated(column) ? { writable: false } : {}),
|
|
132
|
+
})),
|
|
133
|
+
});
|
|
134
|
+
columnsByTable.set(entry.name, columns);
|
|
135
|
+
if (kind.type === 'table' &&
|
|
136
|
+
!folded(entry.name).startsWith('sqlite_') &&
|
|
137
|
+
!blocked.has(folded(entry.name)) &&
|
|
138
|
+
columns.every((column) => column.hidden !== 1))
|
|
139
|
+
writable.add(entry.name);
|
|
140
|
+
const triggers = kind.type === 'table' ? triggered.get(folded(entry.name)) : undefined;
|
|
141
|
+
if (triggers)
|
|
142
|
+
blockedBy.set(entry.name, triggerNames(triggers));
|
|
143
|
+
if (kind.type === 'table' &&
|
|
144
|
+
!folded(entry.name).startsWith('sqlite_') &&
|
|
145
|
+
!shadowed.has(folded(entry.name)) &&
|
|
146
|
+
// A generated column is read like any other; a virtual table's own columns (hidden 1) are its module's.
|
|
147
|
+
columns.every((column) => column.hidden !== 1))
|
|
148
|
+
readable.add(entry.name);
|
|
149
|
+
}
|
|
150
|
+
const byName = identities(tables);
|
|
151
|
+
const foreignKeys = [];
|
|
152
|
+
for (const table of tables) {
|
|
153
|
+
const rows = db.prepare(`PRAGMA main.foreign_key_list(${ANSI_STYLE.quote(table.name)})`).all();
|
|
154
|
+
const groups = new Map();
|
|
155
|
+
for (const row of rows) {
|
|
156
|
+
if (!Number.isInteger(row.id) ||
|
|
157
|
+
row.id < 0 ||
|
|
158
|
+
!Number.isInteger(row.seq) ||
|
|
159
|
+
row.seq < 0 ||
|
|
160
|
+
typeof row.table !== 'string' ||
|
|
161
|
+
typeof row.from !== 'string' ||
|
|
162
|
+
(row.to !== null && typeof row.to !== 'string'))
|
|
163
|
+
return refuse();
|
|
164
|
+
const group = groups.get(row.id) ?? [];
|
|
165
|
+
group.push(row);
|
|
166
|
+
groups.set(row.id, group);
|
|
167
|
+
}
|
|
168
|
+
const localColumns = identities(table.columns);
|
|
169
|
+
/* Every key a column sits in, judged together after the groups: one
|
|
170
|
+
column legitimately carries its own reference and its part in a
|
|
171
|
+
composite key (chooseColumnReference says which link a reader
|
|
172
|
+
follows, and which case is a contradiction). */
|
|
173
|
+
const candidates = new Map();
|
|
174
|
+
for (const [id, unordered] of groups) {
|
|
175
|
+
const group = unordered.sort((a, b) => a.seq - b.seq);
|
|
176
|
+
const first = group[0];
|
|
177
|
+
const parent = byName.get(folded(first.table));
|
|
178
|
+
const onDelete = deleteRuleOf(first.on_delete);
|
|
179
|
+
if (!parent ||
|
|
180
|
+
kinds.get(folded(parent.name))?.type !== 'table' ||
|
|
181
|
+
!onDelete ||
|
|
182
|
+
!deleteRuleOf(first.on_update) ||
|
|
183
|
+
group.some((row, i) => row.seq !== i ||
|
|
184
|
+
folded(row.table) !== folded(first.table) ||
|
|
185
|
+
row.on_delete !== first.on_delete ||
|
|
186
|
+
row.on_update !== first.on_update))
|
|
187
|
+
return refuse();
|
|
188
|
+
const parentColumns = identities(parent.columns);
|
|
189
|
+
const primary = columnsByTable
|
|
190
|
+
.get(parent.id)
|
|
191
|
+
.filter((column) => column.pk > 0)
|
|
192
|
+
.sort((a, b) => a.pk - b.pk);
|
|
193
|
+
const implicit = first.to === null;
|
|
194
|
+
if (group.some((row) => (row.to === null) !== implicit) || (implicit && primary.length !== group.length))
|
|
195
|
+
return refuse();
|
|
196
|
+
const from = group.map((row) => localColumns.get(folded(row.from))?.name ?? refuse());
|
|
197
|
+
const to = group.map((row, i) => (implicit ? primary[i].name : (parentColumns.get(folded(row.to))?.name ?? refuse())));
|
|
198
|
+
if (new Set(from).size !== from.length || new Set(to).size !== to.length)
|
|
199
|
+
return refuse();
|
|
200
|
+
foreignKeys.push({
|
|
201
|
+
name: `${table.name}_fk_${id}`,
|
|
202
|
+
from: { table: table.id, columns: from },
|
|
203
|
+
to: { table: parent.id, columns: to },
|
|
204
|
+
onDelete,
|
|
205
|
+
});
|
|
206
|
+
for (const [i, name] of from.entries()) {
|
|
207
|
+
const column = localColumns.get(folded(name));
|
|
208
|
+
const listed = candidates.get(column.name) ?? [];
|
|
209
|
+
listed.push({ reference: { table: parent.id, column: to[i] }, composite: group.length > 1 });
|
|
210
|
+
candidates.set(column.name, listed);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const [name, listed] of candidates) {
|
|
214
|
+
const chosen = chooseColumnReference(listed);
|
|
215
|
+
// This is the attesting path, so a contradiction refuses the catalog.
|
|
216
|
+
// The adapter's own describe keeps the same verdict and shows no link
|
|
217
|
+
// instead: see chooseColumnReference for why the two differ.
|
|
218
|
+
if (chosen.kind === 'conflict')
|
|
219
|
+
return refuse();
|
|
220
|
+
if (chosen.kind === 'one')
|
|
221
|
+
localColumns.get(folded(name)).references = chosen.reference;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
schema: { dialect: 'sqlite', label: 'SQLite scoped transaction', readAt: new Date().toISOString(), tables, foreignKeys },
|
|
226
|
+
writable,
|
|
227
|
+
readable,
|
|
228
|
+
blockedBy,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
if (error instanceof Refusal)
|
|
233
|
+
throw error;
|
|
234
|
+
// Missing/broken objects and unknown metadata cannot weaken the boundary.
|
|
235
|
+
// Refused either way; a busy or failing engine is kept as the cause.
|
|
236
|
+
const { code, errcode } = (error ?? {});
|
|
237
|
+
if (code === 'ERR_SQLITE_ERROR' && typeof errcode === 'number' && UNAVAILABLE.has(errcode & 0xff))
|
|
238
|
+
return refuse(error);
|
|
239
|
+
return refuse();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { type SqlFragment, type SqlStyle, type UpdateRequest } from '../adapter.js';
|
|
2
|
+
/** Appended to a complete primary-key WHERE, with values bound after its parameters. */
|
|
3
|
+
export declare function buildUpdateGuard(request: UpdateRequest, types: Record<string, string>, style: SqlStyle, dialect: 'sqlite' | 'postgres', startIndex: number): SqlFragment;
|