@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,730 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A grant over a catalog, and every check that holds a request to it: which
|
|
3
|
+
* tables and columns a reader reads and writes, the catalog pruned to them
|
|
4
|
+
* (its edges too, since a relationship can disclose a table), and each
|
|
5
|
+
* query, view, breakdown, filter and write refused where it reaches past
|
|
6
|
+
* them. The engine's, because two products hold requests to one: an App
|
|
7
|
+
* role's grant (app/appAccess.ts, which adds its rules over writes through
|
|
8
|
+
* `WriteRules`) and a connection's hidden columns (hiddenColumns.ts), which
|
|
9
|
+
* has no App in it to ask.
|
|
10
|
+
*/
|
|
11
|
+
import { ANSI_STYLE, Refusal, } from '../adapters/adapter.js';
|
|
12
|
+
import { freezeDerivedCatalog, isCapturedScopeCatalog, isFixedScopeCatalog } from '../shared/scopeCatalog.js';
|
|
13
|
+
import { compileView, resolveAggregate, tryResolvePath } from '../shared/view.js';
|
|
14
|
+
import { rollupReads } from '../shared/rollups.js';
|
|
15
|
+
import { compileBreakdown, measureConditions } from '../shared/breakdown.js';
|
|
16
|
+
import { textToView } from '../shared/viewtext.js';
|
|
17
|
+
import { textToBreakdown } from '../shared/breakdowntext.js';
|
|
18
|
+
import { isJsonType } from '../shared/schema.js';
|
|
19
|
+
import { impliedEdges } from '../shared/declaredReferences.js';
|
|
20
|
+
import { unformedColumns } from '../shared/form.js';
|
|
21
|
+
import { isServerRequest } from './serverRequests.js';
|
|
22
|
+
/**
|
|
23
|
+
* A reader reaching past its grant. Its name is the App's, where most
|
|
24
|
+
* refusals are met; a connection's hidden columns (hiddenColumns.ts) say
|
|
25
|
+
* which boundary answered in a refusal of their own.
|
|
26
|
+
*/
|
|
27
|
+
export class AppAccessDenied extends Refusal {
|
|
28
|
+
constructor(message = 'Access to this resource is denied.') {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = 'AppAccessDenied';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Preserve changes made before invocation, never changes during its awaits.
|
|
34
|
+
* These copied sets stay private; freezing a Set alone would not freeze grants. */
|
|
35
|
+
export function captureGrant(grant) {
|
|
36
|
+
return {
|
|
37
|
+
...grant,
|
|
38
|
+
readable: new Set(grant.readable),
|
|
39
|
+
writable: new Set(grant.writable),
|
|
40
|
+
...(grant.creatable ? { creatable: new Set(grant.creatable) } : {}),
|
|
41
|
+
...(grant.fields
|
|
42
|
+
? {
|
|
43
|
+
fields: new Map([...grant.fields].map(([table, fields]) => [table, { read: new Set(fields.read), write: new Set(fields.write) }])),
|
|
44
|
+
}
|
|
45
|
+
: {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const withoutCarriedReferences = (def) => {
|
|
49
|
+
if (!def || typeof def !== 'object' || !('references' in def))
|
|
50
|
+
return def;
|
|
51
|
+
const { references: _requested, ...rest } = def;
|
|
52
|
+
return rest;
|
|
53
|
+
};
|
|
54
|
+
/* Pruning a catalog to a grant allocated a fresh catalog on every request (9%
|
|
55
|
+
of a busy server's allocation), though the answer is the same for the same
|
|
56
|
+
catalog and the same grant. Only a grant its owner says is never changed
|
|
57
|
+
(markStableGrant: the HTTP source keeps one per App, catalog and role set)
|
|
58
|
+
is remembered, by the identity of its sets, so a grant built and edited by
|
|
59
|
+
a caller is judged afresh every time as before. */
|
|
60
|
+
const stableGrants = new WeakSet();
|
|
61
|
+
export function markStableGrant(grant) {
|
|
62
|
+
stableGrants.add(grant.readable);
|
|
63
|
+
return grant;
|
|
64
|
+
}
|
|
65
|
+
const prunedCatalogs = new WeakMap();
|
|
66
|
+
/* A tenant request resolves its grant afresh each time, so its sets are never
|
|
67
|
+
the same objects twice, and every read in it pruned the catalog again
|
|
68
|
+
(G118). A captured catalog is frozen, and pruning reads nothing of the grant
|
|
69
|
+
but the five things in this key, so the answer for a catalog and a key is
|
|
70
|
+
kept — bounded, oldest out first — and frozen, so no caller can change the
|
|
71
|
+
copy another one is handed. */
|
|
72
|
+
const prunedByContent = new WeakMap();
|
|
73
|
+
const PRUNED = 256;
|
|
74
|
+
const sorted = (names) => [...names].sort();
|
|
75
|
+
function grantKey(access) {
|
|
76
|
+
return JSON.stringify([
|
|
77
|
+
sorted(access.readable),
|
|
78
|
+
sorted(access.writable),
|
|
79
|
+
access.creatable ? sorted(access.creatable) : null,
|
|
80
|
+
access.fields
|
|
81
|
+
? [...access.fields].map(([table, { read, write }]) => [table, sorted(read), sorted(write)]).sort((a, b) => (a[0] < b[0] ? -1 : 1))
|
|
82
|
+
: null,
|
|
83
|
+
access.rows !== undefined,
|
|
84
|
+
access.internal
|
|
85
|
+
? [...access.internal].map(([table, columns]) => [table, sorted(columns)]).sort((a, b) => (a[0] < b[0] ? -1 : 1))
|
|
86
|
+
: null,
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
/** Test-only: how many times a catalog was pruned rather than reused. */
|
|
90
|
+
export const pruneCounters = { pruned: 0 };
|
|
91
|
+
/** Hide graph edges as well as tables: a relationship can disclose another resource. */
|
|
92
|
+
export function accessibleSchema(schema, access) {
|
|
93
|
+
if (!stableGrants.has(access.readable)) {
|
|
94
|
+
if (!isCapturedScopeCatalog(schema))
|
|
95
|
+
return pruneCatalog(schema, access);
|
|
96
|
+
let byKey = prunedByContent.get(schema);
|
|
97
|
+
if (!byKey)
|
|
98
|
+
prunedByContent.set(schema, (byKey = new Map()));
|
|
99
|
+
const key = grantKey(access);
|
|
100
|
+
const known = byKey.get(key);
|
|
101
|
+
if (known)
|
|
102
|
+
return known;
|
|
103
|
+
const pruned = freezeDerivedCatalog(pruneCatalog(schema, access));
|
|
104
|
+
if (byKey.size >= PRUNED)
|
|
105
|
+
byKey.delete(byKey.keys().next().value);
|
|
106
|
+
byKey.set(key, pruned);
|
|
107
|
+
return pruned;
|
|
108
|
+
}
|
|
109
|
+
let byGrant = prunedCatalogs.get(schema);
|
|
110
|
+
if (!byGrant)
|
|
111
|
+
prunedCatalogs.set(schema, (byGrant = new WeakMap()));
|
|
112
|
+
const kept = byGrant.get(access.readable);
|
|
113
|
+
const scoped = access.rows !== undefined;
|
|
114
|
+
if (kept &&
|
|
115
|
+
kept.writable === access.writable &&
|
|
116
|
+
kept.creatable === access.creatable &&
|
|
117
|
+
kept.fields === access.fields &&
|
|
118
|
+
kept.internal === access.internal &&
|
|
119
|
+
kept.scoped === scoped)
|
|
120
|
+
return kept.pruned;
|
|
121
|
+
// Frozen over a captured catalog, as the content path's is: what reads it later (resourceChecks) may then keep by its identity.
|
|
122
|
+
const made = pruneCatalog(schema, access);
|
|
123
|
+
const pruned = isCapturedScopeCatalog(schema) ? freezeDerivedCatalog(made) : made;
|
|
124
|
+
const { writable, creatable, fields, internal } = access;
|
|
125
|
+
byGrant.set(access.readable, { writable, creatable, fields, internal, scoped, pruned });
|
|
126
|
+
return pruned;
|
|
127
|
+
}
|
|
128
|
+
function pruneCatalog(schema, access) {
|
|
129
|
+
pruneCounters.pruned++;
|
|
130
|
+
const scoped = access.rows !== undefined;
|
|
131
|
+
const readable = (table, column) => access.readable.has(table) && (access.fields?.get(table)?.read.has(column) ?? true);
|
|
132
|
+
/* Read by the server, never listed: an internal column is left out of what the catalog names, but not out of what the grant allows. */
|
|
133
|
+
const listed = (table, column) => readable(table, column) && !access.internal?.get(table)?.has(column);
|
|
134
|
+
const reaches = (fk) => access.readable.has(fk.from.table) &&
|
|
135
|
+
access.readable.has(fk.to.table) &&
|
|
136
|
+
fk.from.columns.every((column) => readable(fk.from.table, column)) &&
|
|
137
|
+
fk.to.columns.every((column) => readable(fk.to.table, column));
|
|
138
|
+
const kept = schema.foreignKeys.filter(reaches);
|
|
139
|
+
const keptSet = new Set(kept);
|
|
140
|
+
/* A composite key this reader cannot follow whole — a tenant's
|
|
141
|
+
`(tenant_id, group_id)`, whose ownership column no tenant reads — is still
|
|
142
|
+
followed by a leg that names one row alone (declaredReferences.ts
|
|
143
|
+
impliedEdges), so `group_id` keeps its link and its label.
|
|
144
|
+
Only where the composite is dropped: a reader who reads it whole has it. */
|
|
145
|
+
const dropped = schema.foreignKeys.filter((fk) => !fk.declared && fk.from.columns.length > 1 && !keptSet.has(fk));
|
|
146
|
+
// The single-column keys kept, by table: a leg is added only where none already leaves its column.
|
|
147
|
+
const keptSingle = new Map();
|
|
148
|
+
for (const fk of kept)
|
|
149
|
+
if (fk.from.columns.length === 1) {
|
|
150
|
+
const columns = keptSingle.get(fk.from.table);
|
|
151
|
+
if (columns)
|
|
152
|
+
columns.add(fk.from.columns[0]);
|
|
153
|
+
else
|
|
154
|
+
keptSingle.set(fk.from.table, new Set(fk.from.columns));
|
|
155
|
+
}
|
|
156
|
+
const legs = dropped.length
|
|
157
|
+
? impliedEdges({ ...schema, foreignKeys: dropped }).filter((edge) => reaches(edge) && !keptSingle.get(edge.from.table)?.has(edge.from.columns[0]))
|
|
158
|
+
: [];
|
|
159
|
+
const foreignKeys = legs.length ? [...kept, ...legs] : kept;
|
|
160
|
+
/* Each edge by the table it leaves, and the tables an inferred edge points
|
|
161
|
+
at: asked below for every column of every table, a scan of the whole
|
|
162
|
+
graph each time made one pruning of a few hundred tables cost
|
|
163
|
+
milliseconds, and startup prunes once per command and role. */
|
|
164
|
+
const served = new Set(foreignKeys);
|
|
165
|
+
const leaving = (edges) => {
|
|
166
|
+
const by = new Map();
|
|
167
|
+
for (const edge of edges) {
|
|
168
|
+
const list = by.get(edge.from.table);
|
|
169
|
+
if (list)
|
|
170
|
+
list.push(edge);
|
|
171
|
+
else
|
|
172
|
+
by.set(edge.from.table, [edge]);
|
|
173
|
+
}
|
|
174
|
+
return by;
|
|
175
|
+
};
|
|
176
|
+
const servedFrom = leaving(foreignKeys), allFrom = leaving(schema.foreignKeys);
|
|
177
|
+
const inferredTo = new Set(schema.foreignKeys.flatMap((fk) => (fk.declared ? [] : [fk.to.table])));
|
|
178
|
+
return {
|
|
179
|
+
...schema,
|
|
180
|
+
tables: schema.tables
|
|
181
|
+
.filter((table) => access.readable.has(table.id))
|
|
182
|
+
.map((table) => {
|
|
183
|
+
const { approxRows: _approxRows, ...withoutEstimate } = table;
|
|
184
|
+
return {
|
|
185
|
+
...(scoped ? withoutEstimate : table),
|
|
186
|
+
...(access.fields?.has(table.id)
|
|
187
|
+
? {
|
|
188
|
+
deletable: access.writable.has(table.id) &&
|
|
189
|
+
table.columns.every((column) => readable(table.id, column.name) && access.fields.get(table.id).write.has(column.name)),
|
|
190
|
+
}
|
|
191
|
+
: {}),
|
|
192
|
+
/* A write grant whose columns leave out one a new row needs (a key,
|
|
193
|
+
a NOT NULL without a default) can edit rows and never make one:
|
|
194
|
+
New is not offered rather than refused on every save. */
|
|
195
|
+
...(access.fields?.has(table.id) &&
|
|
196
|
+
access.writable.has(table.id) &&
|
|
197
|
+
!table.isView &&
|
|
198
|
+
unformedColumns(table, (column) => access.fields.get(table.id).write.has(column)).length
|
|
199
|
+
? { insertable: false }
|
|
200
|
+
: {}),
|
|
201
|
+
// A role that edits and never creates (`create`): the same answer, said by the author.
|
|
202
|
+
...(access.creatable && access.writable.has(table.id) && !access.creatable.has(table.id) ? { insertable: false } : {}),
|
|
203
|
+
...(scoped
|
|
204
|
+
? {
|
|
205
|
+
deletable: access.writable.has(table.id) &&
|
|
206
|
+
!table.isView &&
|
|
207
|
+
table.columns.some((column) => column.primaryKey) &&
|
|
208
|
+
!inferredTo.has(table.id) &&
|
|
209
|
+
table.columns.every((column) => readable(table.id, column.name) && (access.fields?.get(table.id)?.write.has(column.name) ?? true)),
|
|
210
|
+
}
|
|
211
|
+
: {}),
|
|
212
|
+
...(table.indexed ? { indexed: table.indexed.filter((column) => listed(table.id, column)) } : {}),
|
|
213
|
+
// Like `indexed`: a hidden column is not named, even as unique.
|
|
214
|
+
...(table.unique ? { unique: table.unique.filter((column) => listed(table.id, column)) } : {}),
|
|
215
|
+
...(table.uniquePairs
|
|
216
|
+
? {
|
|
217
|
+
uniquePairs: table.uniquePairs.filter((pair) => pair.every((column) => listed(table.id, column))),
|
|
218
|
+
}
|
|
219
|
+
: {}),
|
|
220
|
+
...(table.trigram
|
|
221
|
+
? {
|
|
222
|
+
trigram: table.trigram.filter((column) => listed(table.id, column)),
|
|
223
|
+
}
|
|
224
|
+
: {}),
|
|
225
|
+
columns: table.columns
|
|
226
|
+
.filter((column) => listed(table.id, column.name))
|
|
227
|
+
.map((column) => {
|
|
228
|
+
const scope = access.fields?.get(table.id);
|
|
229
|
+
const metadata = {
|
|
230
|
+
...column,
|
|
231
|
+
// A column the database computes stays unwritable whatever the grant says.
|
|
232
|
+
...(scope ? { writable: column.writable !== false && access.writable.has(table.id) && scope.write.has(column.name) } : {}),
|
|
233
|
+
...(scoped
|
|
234
|
+
? {
|
|
235
|
+
writable: column.writable !== false &&
|
|
236
|
+
access.writable.has(table.id) &&
|
|
237
|
+
!table.isView &&
|
|
238
|
+
(scope?.write.has(column.name) ?? true),
|
|
239
|
+
}
|
|
240
|
+
: {}),
|
|
241
|
+
};
|
|
242
|
+
const reference = column.references;
|
|
243
|
+
// A surviving component of a hidden composite FK must not advertise
|
|
244
|
+
// a relationship whose other join columns cannot be read — unless the
|
|
245
|
+
// column is a key of its own this reader follows: its own single key, or
|
|
246
|
+
// the leg the composite implies. Beside a tenant's composite, a single key
|
|
247
|
+
// lost its link here and its cells drew the raw id.
|
|
248
|
+
const followed = (servedFrom.get(table.id) ?? []).some((edge) => edge.from.columns.length === 1 &&
|
|
249
|
+
edge.from.columns[0] === column.name &&
|
|
250
|
+
edge.to.table === reference?.table &&
|
|
251
|
+
edge.to.columns[0] === reference?.column);
|
|
252
|
+
const hiddenEdge = !followed && (allFrom.get(table.id) ?? []).some((fk) => fk.from.columns.includes(column.name) && !served.has(fk));
|
|
253
|
+
if (!reference || (readable(reference.table, reference.column) && !hiddenEdge))
|
|
254
|
+
return metadata;
|
|
255
|
+
const { references: _references, ...visible } = metadata;
|
|
256
|
+
return visible;
|
|
257
|
+
}),
|
|
258
|
+
};
|
|
259
|
+
}),
|
|
260
|
+
foreignKeys,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
/** Capture data before checking it: queued adapters must execute what we authorized.
|
|
264
|
+
* Getters, inherited properties and coercible objects are not structured requests. */
|
|
265
|
+
export function requestData(value, ancestors = new Set()) {
|
|
266
|
+
if (value === null ||
|
|
267
|
+
value === undefined ||
|
|
268
|
+
typeof value === 'string' ||
|
|
269
|
+
typeof value === 'boolean' ||
|
|
270
|
+
(typeof value === 'number' && Number.isFinite(value)))
|
|
271
|
+
return value;
|
|
272
|
+
// Bytes are data too (a file upload's content), copied so no caller keeps a handle on what is written.
|
|
273
|
+
if (value instanceof Uint8Array && (Object.getPrototypeOf(value) === Uint8Array.prototype || Buffer.isBuffer(value)))
|
|
274
|
+
return Buffer.from(value);
|
|
275
|
+
if (typeof value !== 'object' || ancestors.has(value))
|
|
276
|
+
throw new AppAccessDenied();
|
|
277
|
+
const array = Array.isArray(value);
|
|
278
|
+
const proto = Object.getPrototypeOf(value);
|
|
279
|
+
if (array ? proto !== Array.prototype : proto !== Object.prototype && proto !== null)
|
|
280
|
+
throw new AppAccessDenied();
|
|
281
|
+
ancestors.add(value);
|
|
282
|
+
const entries = [];
|
|
283
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
284
|
+
if (array && key === 'length')
|
|
285
|
+
continue;
|
|
286
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
287
|
+
if (typeof key !== 'string' || !descriptor.enumerable || !('value' in descriptor) || (array && !/^(0|[1-9][0-9]*)$/.test(key)))
|
|
288
|
+
throw new AppAccessDenied();
|
|
289
|
+
entries.push([key, requestData(descriptor.value, ancestors)]);
|
|
290
|
+
}
|
|
291
|
+
ancestors.delete(value);
|
|
292
|
+
if (array) {
|
|
293
|
+
if (entries.length !== value.length)
|
|
294
|
+
throw new AppAccessDenied();
|
|
295
|
+
return entries.map(([, item]) => item);
|
|
296
|
+
}
|
|
297
|
+
return Object.fromEntries(entries);
|
|
298
|
+
}
|
|
299
|
+
/* A pruned catalog's full-catalog comparison (resourceChecks), kept per pruned catalog nothing can change. */
|
|
300
|
+
const comparisons = new WeakMap();
|
|
301
|
+
/** Every read and write check against one grant, over the catalog it prunes: an App role's (app/appAccess.ts) and a connection's hidden columns (hiddenColumns.ts) alike. */
|
|
302
|
+
export function resourceChecks(full, access) {
|
|
303
|
+
const schema = accessibleSchema(full, access);
|
|
304
|
+
/* What the pruned compile is compared with: every table and edge of the
|
|
305
|
+
full catalog, so a hop that pruning disambiguated differently still
|
|
306
|
+
shows, but each table's columns as this reader reads them. A relation
|
|
307
|
+
written out whole (a rollup's filtered rows, a row scope) lists its
|
|
308
|
+
columns, and that list is meant to differ by the ones cut. */
|
|
309
|
+
/* With the legs pruning followed in place of a composite key the reader
|
|
310
|
+
cannot read whole (pruneCatalog): the full catalog joins on the composite
|
|
311
|
+
only under its own hop name, so `group_id` is the same join on both. */
|
|
312
|
+
/* Built when a view or breakdown is compiled, not for every request: the
|
|
313
|
+
scan of every edge against every edge was among a tenant read's largest
|
|
314
|
+
costs in a field report's profile, for a catalog most requests never
|
|
315
|
+
compare. Kept per pruned catalog where both catalogs are fixed: such a
|
|
316
|
+
pruning is the answer for one grant's content (accessibleSchema), and
|
|
317
|
+
the columns cut here are that grant's `fields`, so the same pruned
|
|
318
|
+
catalog always stands beside the same comparison. */
|
|
319
|
+
let compared;
|
|
320
|
+
const unprunedCatalog = () => {
|
|
321
|
+
if (compared)
|
|
322
|
+
return compared;
|
|
323
|
+
const fixed = isCapturedScopeCatalog(full) && isFixedScopeCatalog(schema);
|
|
324
|
+
const kept = fixed ? comparisons.get(schema) : undefined;
|
|
325
|
+
if (kept)
|
|
326
|
+
return (compared = kept);
|
|
327
|
+
const legs = schema.foreignKeys.filter((fk) => !full.foreignKeys.some((other) => other.name === fk.name && other.from.table === fk.from.table));
|
|
328
|
+
const followed = legs.length ? { ...full, foreignKeys: [...full.foreignKeys, ...legs] } : full;
|
|
329
|
+
const made = access.fields
|
|
330
|
+
? {
|
|
331
|
+
...followed,
|
|
332
|
+
tables: full.tables.map((table) => {
|
|
333
|
+
const read = access.fields.get(table.id)?.read;
|
|
334
|
+
return read ? { ...table, columns: table.columns.filter((column) => read.has(column.name)) } : table;
|
|
335
|
+
}),
|
|
336
|
+
}
|
|
337
|
+
: followed;
|
|
338
|
+
/* Frozen where it is kept: a catalog nothing can change, so what is compiled against it — every view and
|
|
339
|
+
breakdown of an enter is compiled here a second time — is kept with it too (breakdown.ts compileBreakdown). */
|
|
340
|
+
if (fixed)
|
|
341
|
+
comparisons.set(schema, freezeDerivedCatalog(made));
|
|
342
|
+
return (compared = made);
|
|
343
|
+
};
|
|
344
|
+
const assertRead = (table) => {
|
|
345
|
+
if (!access.readable.has(table))
|
|
346
|
+
throw new AppAccessDenied();
|
|
347
|
+
};
|
|
348
|
+
const assertWrite = (table) => {
|
|
349
|
+
assertRead(table);
|
|
350
|
+
if (!access.writable.has(table))
|
|
351
|
+
throw new AppAccessDenied();
|
|
352
|
+
};
|
|
353
|
+
/* An `internal` column is the server's: a request that names it — a
|
|
354
|
+
column, a filter, a sort, a value — is refused as one naming a column it
|
|
355
|
+
may not have, unless the server built the request itself. */
|
|
356
|
+
const assertFields = (table, columns, mode = 'read', server = false) => {
|
|
357
|
+
assertRead(table);
|
|
358
|
+
const declared = full.tables.find((item) => item.id === table);
|
|
359
|
+
const allowed = access.fields?.get(table);
|
|
360
|
+
const internal = server ? undefined : access.internal?.get(table);
|
|
361
|
+
for (const column of columns) {
|
|
362
|
+
if (!declared?.columns.some((item) => item.name === column) ||
|
|
363
|
+
internal?.has(column) ||
|
|
364
|
+
(allowed && (!allowed.read.has(column) || (mode === 'write' && !allowed.write.has(column))))) {
|
|
365
|
+
throw new AppAccessDenied();
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
/* Every column of a table: a delete's before-image, a cascade's dependents,
|
|
370
|
+
a profile. A removal takes the server's columns with the row, so `owned`
|
|
371
|
+
leaves them out of what the principal must hold; a read of them never does. */
|
|
372
|
+
const assertAllFields = (table, mode = 'read', owned = false) => assertFields(table, full.tables
|
|
373
|
+
.find((item) => item.id === table)
|
|
374
|
+
?.columns.map((column) => column.name)
|
|
375
|
+
.filter((column) => !owned || !access.internal?.get(table)?.has(column)) ?? [], mode);
|
|
376
|
+
const recordFields = (table, record, mode, server = false) => {
|
|
377
|
+
if (!record || typeof record !== 'object' || Array.isArray(record))
|
|
378
|
+
throw new AppAccessDenied();
|
|
379
|
+
assertFields(table, Object.keys(record), mode, server);
|
|
380
|
+
// A written value may also be bytes (a file's content, already copied by requestData); a key or a read never is.
|
|
381
|
+
if (Object.values(record).some((value) => value !== null &&
|
|
382
|
+
typeof value !== 'string' &&
|
|
383
|
+
typeof value !== 'number' &&
|
|
384
|
+
typeof value !== 'boolean' &&
|
|
385
|
+
!(mode === 'write' && value instanceof Uint8Array)))
|
|
386
|
+
throw new AppAccessDenied();
|
|
387
|
+
};
|
|
388
|
+
const keyFields = (table, key) => {
|
|
389
|
+
recordFields(table, key, 'read');
|
|
390
|
+
const primary = full.tables.find((item) => item.id === table)?.columns.filter((column) => column.primaryKey) ?? [];
|
|
391
|
+
if (!primary.length ||
|
|
392
|
+
Object.keys(key).length !== primary.length ||
|
|
393
|
+
primary.some((column) => !Object.hasOwn(key, column.name)) ||
|
|
394
|
+
Object.values(key).some((value) => typeof value !== 'string' && typeof value !== 'number'))
|
|
395
|
+
throw new AppAccessDenied();
|
|
396
|
+
};
|
|
397
|
+
const filterFor = (table, input, server = false) => {
|
|
398
|
+
assertRead(table);
|
|
399
|
+
const filter = requestData(input);
|
|
400
|
+
if (filter)
|
|
401
|
+
for (const group of filter.groups)
|
|
402
|
+
for (const condition of group)
|
|
403
|
+
assertFields(table, [condition.column], 'read', server);
|
|
404
|
+
return filter;
|
|
405
|
+
};
|
|
406
|
+
const queryFor = (input) => {
|
|
407
|
+
const server = isServerRequest(input);
|
|
408
|
+
const request = requestData(input);
|
|
409
|
+
assertRead(request.table);
|
|
410
|
+
const columns = request.columns?.length
|
|
411
|
+
? request.columns
|
|
412
|
+
: (schema.tables.find((table) => table.id === request.table)?.columns.map((column) => column.name) ?? []);
|
|
413
|
+
// Never pass an empty projection to an adapter: it means SELECT *.
|
|
414
|
+
if (!columns.length)
|
|
415
|
+
throw new AppAccessDenied();
|
|
416
|
+
assertFields(request.table, columns, 'read', server);
|
|
417
|
+
for (const order of request.orderBy ?? [])
|
|
418
|
+
assertFields(request.table, [order.column], 'read', server);
|
|
419
|
+
return { ...request, columns, filter: filterFor(request.table, request.filter, server) };
|
|
420
|
+
};
|
|
421
|
+
/* A rollup reads its child rows: the child, the reference, the column it
|
|
422
|
+
sums and the columns its filter narrows by must all be readable here —
|
|
423
|
+
an inner rollup's too — or the number would be built from values this
|
|
424
|
+
reader may not see. A view's and a breakdown's alike. */
|
|
425
|
+
const assertRollups = (computed) => {
|
|
426
|
+
for (const rollup of Object.values(computed ?? {})) {
|
|
427
|
+
for (const { table, fields } of rollupReads(rollup)) {
|
|
428
|
+
assertRead(table);
|
|
429
|
+
assertFields(table, fields);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
/* A computed column reads its record's columns (computed.ts): each must be
|
|
434
|
+
one this reader is served, through readable references, or the value
|
|
435
|
+
would say what its inputs may not. A pinned expression is the catalog's,
|
|
436
|
+
never a request's, and is held to the reader here all the same. */
|
|
437
|
+
const assertExpressions = (base, expressions, rollups) => {
|
|
438
|
+
for (const expressed of Object.values(expressions ?? {})) {
|
|
439
|
+
for (const read of expressed.reads) {
|
|
440
|
+
const path = expressed.on ? `${expressed.on}.${read}` : read;
|
|
441
|
+
/* A rollup it reads is pinned beside it (viewLabels.ts withViewComputed),
|
|
442
|
+
not on this catalog, and assertRollups has held it to the reader. */
|
|
443
|
+
if (rollups && Object.hasOwn(rollups, path))
|
|
444
|
+
continue;
|
|
445
|
+
const { resolved } = tryResolvePath(schema, base, path);
|
|
446
|
+
if (!resolved)
|
|
447
|
+
throw new AppAccessDenied();
|
|
448
|
+
for (const hop of resolved.hops) {
|
|
449
|
+
assertRead(hop.from.table);
|
|
450
|
+
assertRead(hop.to.table);
|
|
451
|
+
}
|
|
452
|
+
assertRead(resolved.table.id);
|
|
453
|
+
if (!resolved.rollup && !resolved.computed)
|
|
454
|
+
assertFields(resolved.table.id, [resolved.column.name]);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
/* A record's tags read their link rows and the tags themselves
|
|
459
|
+
(tagLists.ts): both tables, and the columns read there, must be this
|
|
460
|
+
reader's, or a chip would say what the reader may not read. Pinned from
|
|
461
|
+
the catalog, never a request, and held to the reader here all the same. */
|
|
462
|
+
const assertTags = (tagged) => {
|
|
463
|
+
for (const tags of Object.values(tagged ?? {})) {
|
|
464
|
+
assertFields(tags.through, [tags.from, tags.via]);
|
|
465
|
+
assertFields(tags.to, [tags.tagKey, tags.label, ...(tags.tone ? [tags.tone] : [])]);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
/* `any opportunity where stage = won` reads the rows that point at the record (view.ts): their table, the
|
|
469
|
+
reference they point through, and every column their conditions name — through readable references, as a
|
|
470
|
+
path anywhere is — must be this reader's, or the list would answer a question about rows they cannot read.
|
|
471
|
+
Their scope is the statement's (rowScope.ts), so a row policy on them narrows the answer as it narrows them. */
|
|
472
|
+
const assertAnyRows = (filter) => {
|
|
473
|
+
for (const condition of filter?.groups.flat() ?? []) {
|
|
474
|
+
if (!condition.any)
|
|
475
|
+
continue;
|
|
476
|
+
if (typeof condition.any !== 'object' || typeof condition.any.via !== 'string')
|
|
477
|
+
throw new AppAccessDenied();
|
|
478
|
+
const { via } = condition.any;
|
|
479
|
+
const child = via.slice(0, via.lastIndexOf('.'));
|
|
480
|
+
assertRead(child);
|
|
481
|
+
assertFields(child, via.slice(child.length + 1).split('+'));
|
|
482
|
+
for (const inner of condition.any.filter?.groups.flat() ?? []) {
|
|
483
|
+
for (const path of [inner.column, ...(inner.other === undefined ? [] : [inner.other])]) {
|
|
484
|
+
const { resolved } = tryResolvePath(schema, child, path);
|
|
485
|
+
if (!resolved || resolved.rollup || resolved.computed || resolved.tags)
|
|
486
|
+
throw new AppAccessDenied();
|
|
487
|
+
for (const hop of resolved.hops) {
|
|
488
|
+
assertFields(hop.from.table, hop.from.columns);
|
|
489
|
+
assertFields(hop.to.table, hop.to.columns);
|
|
490
|
+
}
|
|
491
|
+
assertFields(resolved.table.id, [resolved.column.name]);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
const viewFor = (input) => {
|
|
497
|
+
// The soft keys a compiler below may walk are the App's to attach, never a request's.
|
|
498
|
+
let view = withoutCarriedReferences(requestData(input));
|
|
499
|
+
if (view.text !== undefined) {
|
|
500
|
+
if (typeof view.text !== 'string')
|
|
501
|
+
throw new AppAccessDenied();
|
|
502
|
+
const parsed = textToView(schema, view.text, { id: view.id, name: view.name });
|
|
503
|
+
if (!parsed.view || parsed.errors.length)
|
|
504
|
+
throw new AppAccessDenied();
|
|
505
|
+
// Text is authoritative until materialized. Never forward it alongside
|
|
506
|
+
// checked columns for another layer to parse against its full catalog.
|
|
507
|
+
// Pagination is invocation-owned, not part of text authority over paths.
|
|
508
|
+
// Keep the caller's normalized cap when materializing against this graph.
|
|
509
|
+
view = { ...parsed.view, limit: view.limit ?? parsed.view.limit, source: view.source, connection: view.connection };
|
|
510
|
+
}
|
|
511
|
+
assertRead(view.base);
|
|
512
|
+
if (!view.columns?.length)
|
|
513
|
+
view.columns = schema.tables.find((table) => table.id === view.base).columns.map((column) => ({ path: column.name }));
|
|
514
|
+
if (!view.columns.length)
|
|
515
|
+
throw new AppAccessDenied();
|
|
516
|
+
for (const aggregate of view.aggregates ?? []) {
|
|
517
|
+
if (aggregate.column !== undefined)
|
|
518
|
+
assertFields(resolveAggregate(schema, view.base, aggregate).child.id, [aggregate.column]);
|
|
519
|
+
}
|
|
520
|
+
assertRollups(view.computed);
|
|
521
|
+
assertExpressions(view.base, view.expressions, view.computed);
|
|
522
|
+
assertTags(view.tagged);
|
|
523
|
+
assertAnyRows(view.filter);
|
|
524
|
+
// Validate against the pruned graph before the underlying full catalog can
|
|
525
|
+
// resolve a forbidden forward path, filter, sort or reverse aggregate.
|
|
526
|
+
const compiled = compileView(schema, view, ANSI_STYLE);
|
|
527
|
+
// Pruning can change hop disambiguation. Refuse if the full compiler would
|
|
528
|
+
// execute a different relationship from the one checked above.
|
|
529
|
+
if (compiled.text !== compileView(unprunedCatalog(), view, ANSI_STYLE).text)
|
|
530
|
+
throw new AppAccessDenied();
|
|
531
|
+
return view;
|
|
532
|
+
};
|
|
533
|
+
const breakdownFor = (input) => {
|
|
534
|
+
let breakdown = withoutCarriedReferences(requestData(input));
|
|
535
|
+
if (breakdown.text !== undefined) {
|
|
536
|
+
if (typeof breakdown.text !== 'string')
|
|
537
|
+
throw new AppAccessDenied();
|
|
538
|
+
const parsed = textToBreakdown(schema, breakdown.text);
|
|
539
|
+
if (!parsed.breakdown || parsed.errors.length)
|
|
540
|
+
throw new AppAccessDenied();
|
|
541
|
+
const { text: _text, ...materialized } = parsed.breakdown;
|
|
542
|
+
breakdown = { ...materialized, limit: breakdown.limit ?? materialized.limit };
|
|
543
|
+
}
|
|
544
|
+
assertRead(breakdown.base);
|
|
545
|
+
assertRollups(breakdown.computed);
|
|
546
|
+
assertExpressions(breakdown.base, breakdown.expressions, breakdown.computed);
|
|
547
|
+
assertTags(breakdown.tagged);
|
|
548
|
+
assertAnyRows(breakdown.filter);
|
|
549
|
+
for (const where of measureConditions(breakdown.measures))
|
|
550
|
+
assertAnyRows(where);
|
|
551
|
+
const compiled = compileBreakdown(schema, breakdown, ANSI_STYLE);
|
|
552
|
+
if (compiled.text !== compileBreakdown(unprunedCatalog(), breakdown, ANSI_STYLE).text)
|
|
553
|
+
throw new AppAccessDenied();
|
|
554
|
+
return breakdown;
|
|
555
|
+
};
|
|
556
|
+
const assertRelatedWrites = (table, fields) => {
|
|
557
|
+
const visited = new Set();
|
|
558
|
+
const visit = (target) => {
|
|
559
|
+
if (visited.has(target))
|
|
560
|
+
return;
|
|
561
|
+
visited.add(target);
|
|
562
|
+
// The database's keys only: a declared reference is never authority for a write (declaredReferences.ts).
|
|
563
|
+
for (const fk of full.foreignKeys.filter((fk) => fk.to.table === target && !fk.declared)) {
|
|
564
|
+
if (fields && target === table && !fk.to.columns.some((column) => fields.includes(column)))
|
|
565
|
+
continue;
|
|
566
|
+
// Unknown actions and hidden dependents must not be treated as harmless.
|
|
567
|
+
assertRead(fk.from.table);
|
|
568
|
+
assertFields(fk.from.table, fk.from.columns);
|
|
569
|
+
assertFields(fk.to.table, fk.to.columns);
|
|
570
|
+
if (!fields && (fk.onDelete === 'restrict' || fk.onDelete === 'no action'))
|
|
571
|
+
continue;
|
|
572
|
+
assertWrite(fk.from.table);
|
|
573
|
+
// Cascaded deletion removes every field. ON UPDATE is not introspected,
|
|
574
|
+
// so only a fully writable dependent is safe for key changes/unknowns.
|
|
575
|
+
if (fields || fk.onDelete === 'cascade' || !fk.onDelete)
|
|
576
|
+
assertAllFields(fk.from.table, 'write', true);
|
|
577
|
+
else
|
|
578
|
+
assertFields(fk.from.table, fk.from.columns, 'write');
|
|
579
|
+
visit(fk.from.table);
|
|
580
|
+
}
|
|
581
|
+
};
|
|
582
|
+
visit(table);
|
|
583
|
+
};
|
|
584
|
+
/* A write may point a row only at a parent the principal reads: an
|
|
585
|
+
insert at every parent it names (its values and its key), an update at
|
|
586
|
+
those whose columns it sets. A reference the write does not touch is not
|
|
587
|
+
its to vouch for; checking it refused a borrower's phone edit over the
|
|
588
|
+
applicant row their profile points at, which the App never declares. */
|
|
589
|
+
const assertParentReads = (table, values, insert) => {
|
|
590
|
+
const meta = full.tables.find((item) => item.id === table);
|
|
591
|
+
const set = new Set([
|
|
592
|
+
...Object.keys(values ?? {}),
|
|
593
|
+
...(insert ? (meta?.columns.filter((column) => column.primaryKey).map((column) => column.name) ?? []) : []),
|
|
594
|
+
]);
|
|
595
|
+
for (const fk of full.foreignKeys.filter((fk) => fk.from.table === table && !fk.declared && fk.from.columns.some((column) => set.has(column)))) {
|
|
596
|
+
assertFields(fk.to.table, fk.to.columns);
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
return {
|
|
600
|
+
schema,
|
|
601
|
+
assertRead,
|
|
602
|
+
assertWrite,
|
|
603
|
+
assertAllFields,
|
|
604
|
+
assertParentReads,
|
|
605
|
+
recordFields,
|
|
606
|
+
keyFields,
|
|
607
|
+
queryFor,
|
|
608
|
+
filterFor,
|
|
609
|
+
viewFor,
|
|
610
|
+
breakdownFor,
|
|
611
|
+
assertRelatedWrites,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
/** Pure catalog validation through the same graph boundary as runtime reads. */
|
|
615
|
+
export function validateResourceView(schema, access, view) {
|
|
616
|
+
return resourceChecks(schema, access).viewFor(view);
|
|
617
|
+
}
|
|
618
|
+
/* A JSON column's object or array is written as its JSON text: every door
|
|
619
|
+
(a form, a command, the API, an import) passes here, so the checks below
|
|
620
|
+
and the adapters, the tenant's managed transaction among them, stay scalar. */
|
|
621
|
+
function jsonWritten(full, request) {
|
|
622
|
+
const values = request.values;
|
|
623
|
+
if (!values || typeof values !== 'object' || Array.isArray(values))
|
|
624
|
+
return request;
|
|
625
|
+
const json = jsonColumns(full, request.table);
|
|
626
|
+
const objects = Object.keys(values).filter((column) => json.has(column) && values[column] !== null && typeof values[column] === 'object');
|
|
627
|
+
if (!objects.length)
|
|
628
|
+
return request;
|
|
629
|
+
return { ...request, values: { ...values, ...Object.fromEntries(objects.map((column) => [column, JSON.stringify(values[column])])) } };
|
|
630
|
+
}
|
|
631
|
+
function jsonColumns(full, table) {
|
|
632
|
+
return new Set(full.tables
|
|
633
|
+
.find((item) => item.id === table)
|
|
634
|
+
?.columns.filter((column) => isJsonType(column.type))
|
|
635
|
+
.map((column) => column.name) ?? []);
|
|
636
|
+
}
|
|
637
|
+
/* An update's `expected` names each written column but a JSON one, which no
|
|
638
|
+
engine compares (updateGuard.ts) and which a stale write cannot hide in:
|
|
639
|
+
it is written whole. */
|
|
640
|
+
export function expectedCovers(full, request) {
|
|
641
|
+
const json = jsonColumns(full, request.table);
|
|
642
|
+
const expected = request.expected;
|
|
643
|
+
const columns = Object.keys(request.values).filter((column) => !json.has(column) || Object.hasOwn(expected, column));
|
|
644
|
+
return Object.keys(expected).length === columns.length && columns.every((column) => Object.hasOwn(expected, column));
|
|
645
|
+
}
|
|
646
|
+
/** Apply the same graph-aware checks to an already-owned transaction handle. */
|
|
647
|
+
export function authorizeColumnWrites(full, grant, writer, rules) {
|
|
648
|
+
// An arbitrary transaction handle has no proof of row enforcement. Only
|
|
649
|
+
// authorizeResources may bind the explicit capability before composing this.
|
|
650
|
+
if (grant.rows !== undefined)
|
|
651
|
+
throw new AppAccessDenied();
|
|
652
|
+
const checks = resourceChecks(full, grant);
|
|
653
|
+
const { assertRead, assertWrite, assertAllFields, recordFields, keyFields, queryFor, filterFor, assertRelatedWrites } = checks;
|
|
654
|
+
const product = rules?.(checks);
|
|
655
|
+
// Keep each call's rules stable across its awaits; the next call reads current grants.
|
|
656
|
+
const capture = (current) => (product ? product.capture(current) : captureGrant(current));
|
|
657
|
+
return {
|
|
658
|
+
...(writer.lockRecord
|
|
659
|
+
? {
|
|
660
|
+
lockRecord: async (input) => {
|
|
661
|
+
const request = requestData(input);
|
|
662
|
+
if (grant.writable.has(request.table) || !product?.locksUnwritable?.(request.table, grant))
|
|
663
|
+
assertWrite(request.table);
|
|
664
|
+
else
|
|
665
|
+
assertRead(request.table);
|
|
666
|
+
keyFields(request.table, request.key);
|
|
667
|
+
await writer.lockRecord(request);
|
|
668
|
+
},
|
|
669
|
+
}
|
|
670
|
+
: {}),
|
|
671
|
+
// A flow's notify step tells of a record the principal reads; it writes nothing.
|
|
672
|
+
...(writer.tell
|
|
673
|
+
? {
|
|
674
|
+
tell: async (input) => {
|
|
675
|
+
const request = requestData(input);
|
|
676
|
+
assertRead(request.table);
|
|
677
|
+
keyFields(request.table, request.key);
|
|
678
|
+
await writer.tell(request);
|
|
679
|
+
},
|
|
680
|
+
}
|
|
681
|
+
: {}),
|
|
682
|
+
query: (request) => writer.query(queryFor(request)),
|
|
683
|
+
count: (table, filter) => writer.count(table, filterFor(table, filter)),
|
|
684
|
+
insert: (input) => {
|
|
685
|
+
const server = isServerRequest(input);
|
|
686
|
+
const request = jsonWritten(full, requestData(input));
|
|
687
|
+
const operation = capture(grant);
|
|
688
|
+
assertWrite(request.table);
|
|
689
|
+
if (operation.creatable && !operation.creatable.has(request.table))
|
|
690
|
+
throw new AppAccessDenied();
|
|
691
|
+
recordFields(request.table, request.values, 'write', server);
|
|
692
|
+
const written = product?.insert ? product.insert(request, operation, server) : (async () => writer.insert(request))();
|
|
693
|
+
return written.then((result) => {
|
|
694
|
+
const readable = operation.fields?.get(request.table)?.read;
|
|
695
|
+
if (!readable || !result.row)
|
|
696
|
+
return result;
|
|
697
|
+
return { ...result, row: Object.fromEntries(Object.entries(result.row).filter(([column]) => readable.has(column))) };
|
|
698
|
+
});
|
|
699
|
+
},
|
|
700
|
+
update: (input) => {
|
|
701
|
+
const request = jsonWritten(full, requestData(input));
|
|
702
|
+
const operation = capture(grant);
|
|
703
|
+
if (!operation.writable.has(request.table)) {
|
|
704
|
+
const answered = product?.unwritable?.(request, operation);
|
|
705
|
+
if (answered)
|
|
706
|
+
return answered;
|
|
707
|
+
}
|
|
708
|
+
assertWrite(request.table);
|
|
709
|
+
recordFields(request.table, request.values, 'write', isServerRequest(input));
|
|
710
|
+
keyFields(request.table, request.key);
|
|
711
|
+
if (request.expected !== undefined) {
|
|
712
|
+
recordFields(request.table, request.expected, 'read');
|
|
713
|
+
if (!expectedCovers(full, request))
|
|
714
|
+
throw new AppAccessDenied();
|
|
715
|
+
}
|
|
716
|
+
assertRelatedWrites(request.table, Object.keys(request.values));
|
|
717
|
+
return product?.update ? product.update(request, operation) : (async () => writer.update(request))();
|
|
718
|
+
},
|
|
719
|
+
remove: (input) => {
|
|
720
|
+
const request = requestData(input);
|
|
721
|
+
assertWrite(request.table);
|
|
722
|
+
keyFields(request.table, request.key);
|
|
723
|
+
// Delete/Undo requires a complete readable before-image and permission
|
|
724
|
+
// to remove all fields, including those protected from ordinary updates.
|
|
725
|
+
assertAllFields(request.table, 'write', true);
|
|
726
|
+
assertRelatedWrites(request.table);
|
|
727
|
+
return product?.remove ? product.remove(request, grant) : writer.remove(request);
|
|
728
|
+
},
|
|
729
|
+
};
|
|
730
|
+
}
|