@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,472 @@
|
|
|
1
|
+
import { isIP } from 'node:net';
|
|
2
|
+
import { untrustedDatabaseRefusal } from '../../adapters/net/untrustedDatabase.js';
|
|
3
|
+
import { serverSettings } from '../../adapters/serverSettings.js';
|
|
4
|
+
import { envName, product } from '../product.js';
|
|
5
|
+
/** Split an `…_ALLOWED_HOSTS` value (`TABLEWALK_` or `MISTRI_`, product.ts). Commas or spaces, either way. */
|
|
6
|
+
export function parseHostList(value) {
|
|
7
|
+
return (value ?? '')
|
|
8
|
+
.split(/[,\s]+/)
|
|
9
|
+
.map((s) => s.trim())
|
|
10
|
+
.filter(Boolean);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Refuse a request that another site made on the user's behalf.
|
|
14
|
+
*
|
|
15
|
+
* Binding to loopback keeps tablewalk off the network. It does **not** keep it
|
|
16
|
+
* away from the browser: any page the user has open can `fetch` a localhost
|
|
17
|
+
* port, and while the same-origin policy stops it reading the reply, the
|
|
18
|
+
* request still runs. That was enough to matter here — a page could POST to
|
|
19
|
+
* /api/update and the write would land, on a connection the user never put
|
|
20
|
+
* into write mode, because write mode is a decision the client makes and the
|
|
21
|
+
* server was taking on trust.
|
|
22
|
+
*
|
|
23
|
+
* Three checks, each cheap and each closing a different door:
|
|
24
|
+
*
|
|
25
|
+
* - **Origin.** A cross-origin POST always carries one. Ours never differs
|
|
26
|
+
* from the host we are serving on. A request with no Origin at all is a
|
|
27
|
+
* tool like curl, which is not a confused-deputy risk and is allowed.
|
|
28
|
+
* - **Content-Type.** Requiring JSON makes a cross-origin request
|
|
29
|
+
* non-"simple", so the browser must preflight it — and we answer no CORS
|
|
30
|
+
* headers at all, so the preflight fails before the real request is sent.
|
|
31
|
+
* This is the check that holds even if a browser stops sending Origin.
|
|
32
|
+
* - **Host.** Only loopback names, or the interface actually bound. This is
|
|
33
|
+
* the DNS-rebinding case: an attacker's domain resolving to 127.0.0.1
|
|
34
|
+
* makes their page same-origin with us, and the Origin check would pass.
|
|
35
|
+
* It is the one of the three that runs on reads as well — see
|
|
36
|
+
* `hostRefusal` — because same-origin is exactly what makes a *read*
|
|
37
|
+
* worth doing, and the schema, the DDL and the saved SQL are all reads.
|
|
38
|
+
*
|
|
39
|
+
* No token, no session, no cookie. The browser already tells us everything we
|
|
40
|
+
* need, and inventing a ceremony on top would be more code guarding the same
|
|
41
|
+
* door.
|
|
42
|
+
*
|
|
43
|
+
* The Host check has one deliberate escape hatch, `allowedHosts`. In a
|
|
44
|
+
* container the server binds 0.0.0.0 while the browser sends whatever name the
|
|
45
|
+
* user typed — `tablewalk.internal`, or a reverse proxy's public name — and
|
|
46
|
+
* neither is loopback nor the bound interface, so nothing would be answered at
|
|
47
|
+
* all — a message about a host nobody set, on the page itself. Naming those
|
|
48
|
+
* hosts explicitly is the fix. It is opt-in and empty by default because
|
|
49
|
+
* each entry is a name whose DNS you are now trusting: if an attacker can make
|
|
50
|
+
* a name on this list resolve to your loopback, the rebinding defence is gone
|
|
51
|
+
* for that name. That is a fair trade for a name you chose and a bad one for a
|
|
52
|
+
* wildcard, which is why there is no wildcard.
|
|
53
|
+
*/
|
|
54
|
+
const LOOPBACK = /^(localhost|127\.0\.0\.1|\[::1\]|::1)(:\d+)?$/i;
|
|
55
|
+
/** A socket peer on this machine: the operator's terminal, a health check, or a proxy beside the server. */
|
|
56
|
+
function loopbackPeer(address) {
|
|
57
|
+
return /^(127\.0\.0\.1|::1|::ffff:127\.0\.0\.1)$/.test(address ?? '');
|
|
58
|
+
}
|
|
59
|
+
const MAX_FORWARDED_HOPS = 32;
|
|
60
|
+
/**
|
|
61
|
+
* An address as eight 16-bit groups, or undefined. A zone (`%eth0`) is
|
|
62
|
+
* dropped; a trailing dotted quad is the last two groups. Shared with the
|
|
63
|
+
* limiter, which keys an IPv6 client by its first four.
|
|
64
|
+
*/
|
|
65
|
+
export function ipv6Groups(address) {
|
|
66
|
+
let text = address.toLowerCase();
|
|
67
|
+
const zone = text.indexOf('%');
|
|
68
|
+
if (zone >= 0)
|
|
69
|
+
text = text.slice(0, zone);
|
|
70
|
+
if (isIP(text) !== 6)
|
|
71
|
+
return undefined;
|
|
72
|
+
const dotted = /(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(text);
|
|
73
|
+
if (dotted) {
|
|
74
|
+
const [a, b, c, d] = dotted.slice(1).map(Number);
|
|
75
|
+
text = `${text.slice(0, dotted.index)}${((a << 8) | b).toString(16)}:${((c << 8) | d).toString(16)}`;
|
|
76
|
+
}
|
|
77
|
+
const [head, tail] = text.includes('::') ? text.split('::') : [text, undefined];
|
|
78
|
+
const before = head ? head.split(':') : [];
|
|
79
|
+
const after = tail ? tail.split(':') : [];
|
|
80
|
+
const fill = tail === undefined ? 0 : 8 - before.length - after.length;
|
|
81
|
+
const groups = [...before, ...Array.from({ length: fill }, () => '0'), ...after].map((group) => parseInt(group, 16));
|
|
82
|
+
return groups.length === 8 && groups.every((group) => Number.isInteger(group) && group >= 0 && group <= 0xffff) ? groups : undefined;
|
|
83
|
+
}
|
|
84
|
+
/* IPv4 is held IPv4-mapped (`::ffff:a.b.c.d`), so a dual-stack socket's
|
|
85
|
+
mapped peer and the bare address are one address, and a v4 range is the
|
|
86
|
+
same comparison 96 bits further in. */
|
|
87
|
+
function addressGroups(address) {
|
|
88
|
+
if (isIP(address) === 4) {
|
|
89
|
+
const [a, b, c, d] = address.split('.').map(Number);
|
|
90
|
+
return [0, 0, 0, 0, 0, 0xffff, (a << 8) | b, (c << 8) | d];
|
|
91
|
+
}
|
|
92
|
+
return ipv6Groups(address);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Read `--trust-proxy` and `--auth-proxy-from` values: each an IPv4 or IPv6 address or CIDR range,
|
|
96
|
+
* several to a value when separated by commas or spaces. What is not one is
|
|
97
|
+
* returned by name, for startup to refuse — a list that silently dropped an
|
|
98
|
+
* entry would collapse that proxy's clients into one budget with no word.
|
|
99
|
+
* A zero-length prefix is refused too: it trusts every address, which makes
|
|
100
|
+
* the header anyone's to write.
|
|
101
|
+
*/
|
|
102
|
+
export function parseTrustedProxies(values) {
|
|
103
|
+
const ranges = [];
|
|
104
|
+
const invalid = [];
|
|
105
|
+
for (const value of values) {
|
|
106
|
+
const entries = value.split(/[,\s]+/).filter(Boolean);
|
|
107
|
+
if (!entries.length) {
|
|
108
|
+
invalid.push(value);
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
const [address, bits, extra] = entry.split('/');
|
|
113
|
+
const family = address.includes('%') || extra !== undefined ? 0 : isIP(address);
|
|
114
|
+
const groups = family ? addressGroups(address) : undefined;
|
|
115
|
+
const width = family === 4 ? 32 : 128;
|
|
116
|
+
const prefix = bits === undefined ? width : /^\d{1,3}$/.test(bits) ? Number(bits) : NaN;
|
|
117
|
+
if (!groups || !(prefix >= 1 && prefix <= width)) {
|
|
118
|
+
invalid.push(entry);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
ranges.push(Object.freeze({ text: entry, groups: Object.freeze(groups), prefix: family === 4 ? 96 + prefix : prefix }));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { ranges, invalid };
|
|
125
|
+
}
|
|
126
|
+
function inRange(groups, range) {
|
|
127
|
+
for (let i = 0, bits = range.prefix; i < 8 && bits > 0; i++, bits -= 16) {
|
|
128
|
+
const mask = (0xffff << (16 - Math.min(16, bits))) & 0xffff;
|
|
129
|
+
if ((groups[i] & mask) !== (range.groups[i] & mask))
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A peer on this machine (`loopbackPeer`, always), or inside a range in the list given. With the `--trust-proxy` list
|
|
136
|
+
* it is a peer whose `X-Forwarded-For` the limiter may read; with the `--auth-proxy-from` list (`Bound.identityProxies`)
|
|
137
|
+
* it is a peer whose `--auth-proxy-header` value `identityFor` believes. The two lists are never swapped: one
|
|
138
|
+
* grants a client address, the other a person.
|
|
139
|
+
*/
|
|
140
|
+
export function trustedProxyPeer(address, trusted = []) {
|
|
141
|
+
if (loopbackPeer(address))
|
|
142
|
+
return true;
|
|
143
|
+
if (!trusted.length || !address)
|
|
144
|
+
return false;
|
|
145
|
+
const groups = addressGroups(address);
|
|
146
|
+
return !!groups && trusted.some((range) => inRange(groups, range));
|
|
147
|
+
}
|
|
148
|
+
/** Whether a public address (MISTRI_PUBLIC_URL) is an https one: what `ServerSettings.httpsFront` is set from. */
|
|
149
|
+
export function httpsAddress(url) {
|
|
150
|
+
try {
|
|
151
|
+
return url !== undefined && new URL(url.trim()).protocol === 'https:';
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Whether a request reached this server over https: its socket is TLS; the
|
|
159
|
+
* server is published at an https address (`ServerSettings.httpsFront`), so
|
|
160
|
+
* a TLS front end stood before it; or a proxy it trusts (`trustedProxyPeer`)
|
|
161
|
+
* says in `X-Forwarded-Proto` that it forwarded https. The one rule for a
|
|
162
|
+
* Secure cookie, `Strict-Transport-Security` and the origin a page writes
|
|
163
|
+
* into a sitemap or a link preview (requestOrigin.ts, baselineHeaders.ts,
|
|
164
|
+
* forms/savedForms.ts `secureRequest`).
|
|
165
|
+
*/
|
|
166
|
+
export function arrivedOverHttps(req, trusted = []) {
|
|
167
|
+
if (req.socket.encrypted)
|
|
168
|
+
return true;
|
|
169
|
+
if (serverSettings().httpsFront)
|
|
170
|
+
return true;
|
|
171
|
+
const forwarded = String(req.headers['x-forwarded-proto'] ?? '')
|
|
172
|
+
.split(',')[0]
|
|
173
|
+
.trim()
|
|
174
|
+
.toLowerCase();
|
|
175
|
+
return forwarded === 'https' && trustedProxyPeer(req.socket.remoteAddress, trusted);
|
|
176
|
+
}
|
|
177
|
+
/*
|
|
178
|
+
* How to name the client behind a managed front end, whose own address cannot
|
|
179
|
+
* be written into `--trust-proxy` (Cloud Run's, Cloudflare's: a changing pool
|
|
180
|
+
* of Google or Cloudflare addresses). Both are the operator's statement about
|
|
181
|
+
* the deployment, off by default, and settled at startup. The limiters that
|
|
182
|
+
* ask `forwardedClient` are spread across every transport and tenant, so the
|
|
183
|
+
* policy is the server's setting (adapters/serverSettings.ts), read from the
|
|
184
|
+
* scope the request runs in: two handlers in one process each key by their
|
|
185
|
+
* own front end.
|
|
186
|
+
*/
|
|
187
|
+
/**
|
|
188
|
+
* Read `--trust-proxy-hops` and `--trust-proxy-cloudflare`. A hop count that
|
|
189
|
+
* is not a whole number from 1 to 32 comes back as an error for startup to
|
|
190
|
+
* print: a mistyped count would collapse every visitor into one budget, or
|
|
191
|
+
* believe a header the client wrote, with no word.
|
|
192
|
+
*/
|
|
193
|
+
export function parseClientAddressPolicy(input) {
|
|
194
|
+
let hops;
|
|
195
|
+
if (input.hops !== undefined) {
|
|
196
|
+
if (!/^\d{1,2}$/.test(input.hops.trim()) || Number(input.hops) < 1 || Number(input.hops) > MAX_FORWARDED_HOPS) {
|
|
197
|
+
return {
|
|
198
|
+
error: `--trust-proxy-hops takes a whole number from 1 to ${MAX_FORWARDED_HOPS} (the proxies in front of the server), not "${input.hops}". It comes from the flag or ${envName('TRUST_PROXY_HOPS')}.`,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
hops = Number(input.hops);
|
|
202
|
+
}
|
|
203
|
+
/* Two answers to one question: Cloudflare's header would win where it was sent and a hop count decide where it was
|
|
204
|
+
not, so a request that bypassed Cloudflare would be keyed by a rule the operator never chose for it. */
|
|
205
|
+
if (hops && input.cloudflare) {
|
|
206
|
+
return {
|
|
207
|
+
error: `--trust-proxy-hops and --trust-proxy-cloudflare are two ways to find the client: name one. Cloudflare in front alone, --trust-proxy-cloudflare; another front end, --trust-proxy-hops. They come from the flags or ${envName('TRUST_PROXY_HOPS')} and ${envName('TRUST_PROXY_CLOUDFLARE')}.`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
return { policy: { ...(hops ? { hops } : {}), ...(input.cloudflare ? { cloudflare: true } : {}) } };
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Which client a request is from, for keying a limit — never for authority.
|
|
214
|
+
*
|
|
215
|
+
* With `--trust-proxy-cloudflare`, the `CF-Connecting-IP` header, when it is
|
|
216
|
+
* one address: Cloudflare sets it and replaces any the client sent. Without
|
|
217
|
+
* the flag it is never read, because anyone can write it.
|
|
218
|
+
*
|
|
219
|
+
* With `--trust-proxy-hops N`, the address N places from the right of
|
|
220
|
+
* [socket peer, ...X-Forwarded-For read right to left]: the peer is the first
|
|
221
|
+
* proxy, and each appends the address it saw, so what is left of the Nth is
|
|
222
|
+
* the client's own claim and is dropped. Fewer entries than hops, the
|
|
223
|
+
* furthest. This does not look at who the peer is — that is the point, on a
|
|
224
|
+
* front end with no stable address — so it is for a server reachable only
|
|
225
|
+
* through those hops.
|
|
226
|
+
*
|
|
227
|
+
* Otherwise the socket peer, unless that peer is a trusted proxy (`trustedProxyPeer`:
|
|
228
|
+
* loopback, or an address `--trust-proxy` named): then the client is the
|
|
229
|
+
* nearest `X-Forwarded-For` hop that is not a trusted proxy too. Read from
|
|
230
|
+
* the right, because a proxy appends the address it saw and everything to
|
|
231
|
+
* its left is whatever the client chose to send; each trusted hop passed
|
|
232
|
+
* vouches for the one before it. When every hop is trusted, the furthest is
|
|
233
|
+
* the client. From any other peer the header is not read at all — anyone can
|
|
234
|
+
* write one — and a malformed header is ignored whole rather than
|
|
235
|
+
* half-believed. A proxy on another host that nobody listed is its own
|
|
236
|
+
* address, so its clients share one budget.
|
|
237
|
+
*/
|
|
238
|
+
export function forwardedClient(req, trusted = []) {
|
|
239
|
+
const peer = req.socket?.remoteAddress || 'unknown';
|
|
240
|
+
const clientPolicy = serverSettings().clientAddress;
|
|
241
|
+
if (clientPolicy.cloudflare) {
|
|
242
|
+
const cf = req.headers?.['cf-connecting-ip'];
|
|
243
|
+
const address = (Array.isArray(cf) ? cf[0] : cf)?.trim();
|
|
244
|
+
if (address && isIP(address) !== 0)
|
|
245
|
+
return address.toLowerCase();
|
|
246
|
+
}
|
|
247
|
+
if (clientPolicy.hops) {
|
|
248
|
+
const hops = forwardedHops(req.headers?.['x-forwarded-for']);
|
|
249
|
+
if (!hops)
|
|
250
|
+
return peer;
|
|
251
|
+
const chain = [peer, ...hops.reverse()];
|
|
252
|
+
return chain[Math.min(clientPolicy.hops, chain.length - 1)].toLowerCase();
|
|
253
|
+
}
|
|
254
|
+
if (!trustedProxyPeer(peer, trusted))
|
|
255
|
+
return peer;
|
|
256
|
+
const hops = forwardedHops(req.headers?.['x-forwarded-for']);
|
|
257
|
+
if (!hops)
|
|
258
|
+
return peer;
|
|
259
|
+
for (let i = hops.length - 1; i >= 0; i--)
|
|
260
|
+
if (!trustedProxyPeer(hops[i], trusted))
|
|
261
|
+
return hops[i].toLowerCase();
|
|
262
|
+
return hops[0].toLowerCase();
|
|
263
|
+
}
|
|
264
|
+
/** The addresses in an X-Forwarded-For value, or undefined when absent or any entry is not one. */
|
|
265
|
+
function forwardedHops(raw) {
|
|
266
|
+
const header = Array.isArray(raw) ? raw.join(',') : raw;
|
|
267
|
+
if (!header)
|
|
268
|
+
return undefined;
|
|
269
|
+
const hops = header.split(',').map((hop) => hop.trim());
|
|
270
|
+
return hops.length > MAX_FORWARDED_HOPS || hops.some((hop) => isIP(hop) === 0) ? undefined : hops;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Who is asking, when the operator has said which header carries it.
|
|
274
|
+
*
|
|
275
|
+
* `--auth-proxy-header X-Forwarded-User` means a proxy in front signs
|
|
276
|
+
* people in and writes the result into that header on the way through.
|
|
277
|
+
* A request without it did not come through the proxy — or came through
|
|
278
|
+
* one that is misconfigured — and is refused with the header named. So is
|
|
279
|
+
* a request that carries it from a peer that is not the proxy: the header
|
|
280
|
+
* is only a name anyone can type, so a value from an address neither on
|
|
281
|
+
* loopback nor in `--auth-proxy-from` is a forgery, not a sign-in. Loopback is
|
|
282
|
+
* exempt: the container's own health check, a curl on the box, the
|
|
283
|
+
* operator's terminal. Nothing here is authentication; it is the proxy's
|
|
284
|
+
* decision, carried to the journal.
|
|
285
|
+
*/
|
|
286
|
+
export function identityFor(req, bound) {
|
|
287
|
+
if (!bound.identityHeader)
|
|
288
|
+
return {};
|
|
289
|
+
const raw = req.headers[bound.identityHeader.toLowerCase()];
|
|
290
|
+
const who = (Array.isArray(raw) ? raw[0] : raw)?.trim();
|
|
291
|
+
const peer = req.socket?.remoteAddress;
|
|
292
|
+
if (who && trustedProxyPeer(peer, bound.identityProxies))
|
|
293
|
+
return { who };
|
|
294
|
+
if (loopbackPeer(peer))
|
|
295
|
+
return { who: 'local' };
|
|
296
|
+
return {
|
|
297
|
+
refusal: who
|
|
298
|
+
? `This server expects ${bound.identityHeader} only from its proxy, and this request did not come from the trusted proxy. ` +
|
|
299
|
+
"Reach it through the proxy that signs you in; the proxy's address belongs in --auth-proxy-from."
|
|
300
|
+
: `This server expects ${bound.identityHeader} from its proxy, and this request carried none. ` +
|
|
301
|
+
'Reach it through the proxy that signs you in.',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Whether the browser on this listener is the operator.
|
|
306
|
+
*
|
|
307
|
+
* On loopback with no extra hosts, whoever reaches the port started the
|
|
308
|
+
* process, and the connection form may do what the config file may: reference
|
|
309
|
+
* `${PROD_PASSWORD}`, look a password up in the keychain by name, open a
|
|
310
|
+
* SQLite file by path, choose where the config is saved. Bound wider — or
|
|
311
|
+
* answering to a proxy's name — the browser is anyone on the network, and each
|
|
312
|
+
* of those is a way out: an env reference sends this process's secrets to a
|
|
313
|
+
* host the caller names, a keychain lookup attaches a stored password to the
|
|
314
|
+
* caller's URL, a file path reads any database the server user can, and a
|
|
315
|
+
* save path writes JSON wherever it points. The routes that take a target
|
|
316
|
+
* from the client ask this first and refuse those forms when it says no.
|
|
317
|
+
*
|
|
318
|
+
* Nothing here is authentication. It is the line between "your own machine"
|
|
319
|
+
* and "the network" that the README already draws, applied to the one API
|
|
320
|
+
* that takes a database *address* rather than a query.
|
|
321
|
+
*/
|
|
322
|
+
export function clientTrusted(bound) {
|
|
323
|
+
return LOOPBACK.test(bound.host) && !bound.allowedHosts?.length;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Why a connection target from an untrusted client is refused, or null when
|
|
327
|
+
* it is a plain network URL that resolves nothing on this machine. A URL that
|
|
328
|
+
* passes is still dialled sealed, to public addresses only
|
|
329
|
+
* (adapters/net/untrustedDatabase.ts).
|
|
330
|
+
*/
|
|
331
|
+
export function untrustedTargetRefusal(target) {
|
|
332
|
+
if (/\$\{?[A-Za-z_]/.test(target)) {
|
|
333
|
+
return ('This server is reachable beyond this machine, so a connection added from the browser cannot ' +
|
|
334
|
+
'reference environment variables. Put it in the config file on the server, or type the password.');
|
|
335
|
+
}
|
|
336
|
+
if (!/^[a-z][\w+.-]*:\/\//i.test(target) || /^sqlite:/i.test(target)) {
|
|
337
|
+
return ('This server is reachable beyond this machine, so the browser cannot open a SQLite file by ' +
|
|
338
|
+
'path. Name the file in the config file or on the command line.');
|
|
339
|
+
}
|
|
340
|
+
return untrustedDatabaseRefusal(target);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* True when a `Host` header matches one of the extra names we were told to
|
|
344
|
+
* trust.
|
|
345
|
+
*
|
|
346
|
+
* An entry without a port matches the name on any port, because a request
|
|
347
|
+
* through a proxy on 80 or 443 arrives with no port at all and the person
|
|
348
|
+
* writing `--allowed-host tablewalk.internal` means the machine, not one
|
|
349
|
+
* listener on it. An entry *with* a port is taken literally.
|
|
350
|
+
*/
|
|
351
|
+
function hostAllowed(host, allowed) {
|
|
352
|
+
const lower = host.toLowerCase();
|
|
353
|
+
const withoutPort = lower.replace(/:\d+$/, '');
|
|
354
|
+
return allowed.some((entry) => {
|
|
355
|
+
const want = entry.trim().toLowerCase();
|
|
356
|
+
if (!want)
|
|
357
|
+
return false;
|
|
358
|
+
return /:\d+$/.test(want) ? lower === want : withoutPort === want;
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* The `Host` half, on its own, because it has to run on reads too.
|
|
363
|
+
*
|
|
364
|
+
* This was inside `crossOriginRefusal`, which only ever ran for methods that
|
|
365
|
+
* change something. The reasoning was that a read is harmless — and it is
|
|
366
|
+
* wrong, because the attack this check exists to stop is DNS rebinding, and
|
|
367
|
+
* rebinding's whole trick is to make the request *same-origin* so the
|
|
368
|
+
* response can be read. A page on a name the attacker controls, pointed at
|
|
369
|
+
* 127.0.0.1, could `fetch('/api/schema')` and read the answer: every table,
|
|
370
|
+
* every column, every foreign key. `/api/ddl` handed over `CREATE TABLE`.
|
|
371
|
+
* `/api/connections` named the hosts, users and databases. `/api/queries`
|
|
372
|
+
* gave up the saved SQL. All of it, from any page, on any machine where
|
|
373
|
+
* tablewalk happened to be running.
|
|
374
|
+
*
|
|
375
|
+
* Row data was never reachable — reads that take arguments are POSTs, and
|
|
376
|
+
* those were checked. The schema is not nothing.
|
|
377
|
+
*
|
|
378
|
+
* It runs on the static files too, so a rebinding attempt fails at the page
|
|
379
|
+
* rather than one request later. Reaching tablewalk by any name other than
|
|
380
|
+
* loopback has always needed `--allowed-host` for writes to work; this makes
|
|
381
|
+
* that one rule instead of two.
|
|
382
|
+
*
|
|
383
|
+
* It is the first part of `doorRefusal`, which is what a surface calls.
|
|
384
|
+
*/
|
|
385
|
+
export function hostRefusal(req, bound) {
|
|
386
|
+
const host = req.headers.host ?? '';
|
|
387
|
+
/* An IPv6 address arrives bracketed in a Host header — `[fe80::1]:4111` —
|
|
388
|
+
and is configured bare; compared as configured, a non-loopback IPv6
|
|
389
|
+
bind could never pass its own check. */
|
|
390
|
+
const expected = `${bound.host.includes(':') && !bound.host.startsWith('[') ? `[${bound.host}]` : bound.host}:${bound.port}`;
|
|
391
|
+
const allowed = bound.allowedHosts ?? [];
|
|
392
|
+
if (!LOOPBACK.test(host) && host !== expected && !hostAllowed(host, allowed)) {
|
|
393
|
+
return (`Refused: this request arrived for host "${host}". ${product()} only answers on ${expected}.` +
|
|
394
|
+
` Add --allowed-host, or set ${envName('ALLOWED_HOSTS')}, to trust another name.`);
|
|
395
|
+
}
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
/** Validate a serialized browser Origin, independently of JSON body policy.
|
|
399
|
+
* Missing Origin remains available to non-browser clients. This compares hosts,
|
|
400
|
+
* not the serving socket's scheme: TLS proxies may terminate HTTPS upstream.
|
|
401
|
+
* Full scheme equality needs explicitly trusted external-origin metadata.
|
|
402
|
+
* A part of `doorRefusal`, which is what a surface calls; `ownOrigin` asks it as a question. */
|
|
403
|
+
export function originRefusal(req) {
|
|
404
|
+
const origin = req.headers.origin;
|
|
405
|
+
if (origin !== undefined) {
|
|
406
|
+
let parsed;
|
|
407
|
+
try {
|
|
408
|
+
parsed = new URL(origin);
|
|
409
|
+
}
|
|
410
|
+
catch {
|
|
411
|
+
return `Refused: "${origin}" is not a valid origin.`;
|
|
412
|
+
}
|
|
413
|
+
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.origin !== origin) {
|
|
414
|
+
return `Refused: "${origin}" is not a valid origin.`;
|
|
415
|
+
}
|
|
416
|
+
if (parsed.host !== req.headers.host) {
|
|
417
|
+
return `Refused: a page at ${origin} tried to make this request. ${product()} only accepts requests from its own page.`;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
/* Requests a host's page on another origin made with the bearer it was given
|
|
423
|
+
(embed/crossOrigin.ts): that door has checked the Origin against the one
|
|
424
|
+
the bearer was issued to, and replaced the cookies with the bearer's
|
|
425
|
+
session. The Host and the JSON body are still required below. */
|
|
426
|
+
const vouched = new WeakSet();
|
|
427
|
+
/** Mark a request whose Origin the embed door admitted on its bearer. */
|
|
428
|
+
export function vouchForOrigin(req) {
|
|
429
|
+
vouched.add(req);
|
|
430
|
+
}
|
|
431
|
+
/** A write's door, as one sentence or null: `doorRefusal` with `{ writing: true }`. Kept for its unit tests; surfaces call `doorRefusal`. */
|
|
432
|
+
export function crossOriginRefusal(req, bound) {
|
|
433
|
+
return doorRefusal(req, bound, { writing: true })?.error ?? null;
|
|
434
|
+
}
|
|
435
|
+
/** Whether a request can change something: anything but a GET or a HEAD. */
|
|
436
|
+
export function writes(req) {
|
|
437
|
+
return req.method !== 'GET' && req.method !== 'HEAD';
|
|
438
|
+
}
|
|
439
|
+
/** Whether the request's Origin, if it sent one, is this page's (`originRefusal`): a question, not a door. */
|
|
440
|
+
export function ownOrigin(req) {
|
|
441
|
+
return originRefusal(req) === null;
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* The one door every HTTP surface holds a request to, before it reads
|
|
445
|
+
* anything else of it: the Host (`hostRefusal`, on reads too), then, where
|
|
446
|
+
* `rule` says, the browser's fetch site and the Origin, then a write's JSON
|
|
447
|
+
* body. The gate (gate.ts) asks it twice, either side of the proxy identity
|
|
448
|
+
* and the embed door, because that door decides whether an Origin is vouched
|
|
449
|
+
* for; every other surface asks once.
|
|
450
|
+
*/
|
|
451
|
+
export function doorRefusal(req, bound, rule) {
|
|
452
|
+
const wrongHost = hostRefusal(req, bound);
|
|
453
|
+
if (wrongHost)
|
|
454
|
+
return { check: 'host', error: wrongHost };
|
|
455
|
+
const origin = rule.origin ?? 'writes';
|
|
456
|
+
if (origin === 'always' || (origin === 'writes' && rule.writing)) {
|
|
457
|
+
if (rule.sameSite && req.headers['sec-fetch-site'] === 'cross-site')
|
|
458
|
+
return {
|
|
459
|
+
check: 'site',
|
|
460
|
+
error: 'Refused: another site made this request. ' + product() + ' only accepts requests from its own page.',
|
|
461
|
+
};
|
|
462
|
+
const wrongOrigin = origin === 'writes' && vouched.has(req) ? null : originRefusal(req);
|
|
463
|
+
if (wrongOrigin)
|
|
464
|
+
return { check: 'origin', error: wrongOrigin };
|
|
465
|
+
}
|
|
466
|
+
if (rule.writing && (rule.json ?? true) && !jsonBody(req))
|
|
467
|
+
return { check: 'json', error: 'Refused: this endpoint takes application/json.' };
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
function jsonBody(req) {
|
|
471
|
+
return (req.headers['content-type'] ?? '').split(';')[0].trim().toLowerCase() === 'application/json';
|
|
472
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a write is refused, apart from the routes that write: data writes
|
|
3
|
+
* (dataWriteRoutes.ts), a record's lines saved with it (formLinesWrite.ts) and
|
|
4
|
+
* an import (importRoutes.ts) all ask it, and the routes import the lines
|
|
5
|
+
* writer, so the question lives in a module of its own rather than closing a
|
|
6
|
+
* loop between them (test/repo/runtime-cycles).
|
|
7
|
+
*/
|
|
8
|
+
import type { Registry } from '../connections.js';
|
|
9
|
+
import type { ColumnGrant } from '../columnGrants.js';
|
|
10
|
+
/**
|
|
11
|
+
* How a product says a refused write in its own words: an App answers for
|
|
12
|
+
* the connections it serves (app/apps.ts `appWriteWording`), and says
|
|
13
|
+
* nothing of the config file to the person using it. Absent, or undefined
|
|
14
|
+
* for a connection, the database browser's words are said.
|
|
15
|
+
*/
|
|
16
|
+
export type WriteWording = (connection: string, name: string | undefined) => {
|
|
17
|
+
readonly readOnly: string;
|
|
18
|
+
denied(tableId: string | undefined, readable: boolean): string;
|
|
19
|
+
} | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* Why a write to `connection` (and `tableId`, when the write names one) is
|
|
22
|
+
* refused, or null. Two gates in one sentence: the connection's `writable`
|
|
23
|
+
* flag, then the grant's write scope — narrower than the connection's, never
|
|
24
|
+
* wider. The message names the file and the field, because the person who
|
|
25
|
+
* hits this is usually the person who can change it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeRefusal(registry: Registry, wording: WriteWording | undefined, connection: string, tableId: string | undefined, resources?: {
|
|
28
|
+
connectionFor(table: string): string;
|
|
29
|
+
}, access?: ColumnGrant & {
|
|
30
|
+
readonly actions?: ReadonlyMap<string, unknown>;
|
|
31
|
+
},
|
|
32
|
+
/** An update (or its Undo) may reach a table where a role runs fixed actions without write; the facade judges which. */
|
|
33
|
+
update?: boolean): string | null;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { configFileName } from '../product.js';
|
|
2
|
+
/**
|
|
3
|
+
* Why a write to `connection` (and `tableId`, when the write names one) is
|
|
4
|
+
* refused, or null. Two gates in one sentence: the connection's `writable`
|
|
5
|
+
* flag, then the grant's write scope — narrower than the connection's, never
|
|
6
|
+
* wider. The message names the file and the field, because the person who
|
|
7
|
+
* hits this is usually the person who can change it.
|
|
8
|
+
*/
|
|
9
|
+
export function writeRefusal(registry, wording, connection, tableId, resources, access,
|
|
10
|
+
/** An update (or its Undo) may reach a table where a role runs fixed actions without write; the facade judges which. */
|
|
11
|
+
update = false) {
|
|
12
|
+
const owner = tableId && resources ? resources.connectionFor(tableId) : connection;
|
|
13
|
+
const config = registry.configOf(owner);
|
|
14
|
+
const said = wording?.(connection, registry.configOf(connection)?.name);
|
|
15
|
+
if (!config?.writable) {
|
|
16
|
+
if (said)
|
|
17
|
+
return said.readOnly;
|
|
18
|
+
return (`"${config?.name ?? connection}" is read-only. ` + `Add "writable": true to its entry in your ${configFileName()} to allow edits.`);
|
|
19
|
+
}
|
|
20
|
+
if (access && !access.writable.has(tableId ?? '') && !(update && access.actions?.has(tableId ?? ''))) {
|
|
21
|
+
return said ? said.denied(tableId, tableId !== undefined && access.readable.has(tableId)) : 'Access to this resource is denied.';
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { HttpAdapter } from '../adapters/http/httpAdapter.js';
|
|
2
|
+
import { type HttpDescription } from '../adapters/http/httpDescription.js';
|
|
3
|
+
import type { CacheStore } from '../adapters/cache/cacheStore.js';
|
|
4
|
+
import type { SecretSource } from '../shared/connection.js';
|
|
5
|
+
/** As written in the config file: either a bearer token or one named header, `${VAR}` references intact. */
|
|
6
|
+
export type HttpAuthConfig = {
|
|
7
|
+
bearer: string;
|
|
8
|
+
} | {
|
|
9
|
+
header: string;
|
|
10
|
+
value: string;
|
|
11
|
+
};
|
|
12
|
+
export interface HttpConnectionConfig {
|
|
13
|
+
/** The description file as written, relative to the config file, or the description itself; written back as it is on save. */
|
|
14
|
+
description: string | HttpDescription;
|
|
15
|
+
/** A description file, resolved against the config file's directory. */
|
|
16
|
+
descriptionPath?: string;
|
|
17
|
+
auth?: HttpAuthConfig;
|
|
18
|
+
/** Plain `http:` to the API. */
|
|
19
|
+
insecure?: boolean;
|
|
20
|
+
/** Link-local addresses, where cloud metadata answers. */
|
|
21
|
+
allowLinkLocal?: boolean;
|
|
22
|
+
/** Reads answered from the cache for `ttl` seconds after the API answered them. */
|
|
23
|
+
cache?: {
|
|
24
|
+
ttl: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* An entry's HTTP fields, checked: the resolved block for an `http`
|
|
29
|
+
* connection, undefined for a database. Throws naming the field, because a
|
|
30
|
+
* config someone wrote is refused rather than half-read. Service kinds
|
|
31
|
+
* (`cache`, `webhook`, …) are read by serviceConnections.ts before this is asked.
|
|
32
|
+
*/
|
|
33
|
+
export declare function httpConnectionOf(entry: Record<string, unknown>, path: string): HttpConnectionConfig | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Open an HTTP connection: read its description, resolve its credential and
|
|
36
|
+
* hand both to the adapter. `secrets: false` — a URL a browser supplied —
|
|
37
|
+
* opens without the credential rather than sending it to that URL, and
|
|
38
|
+
* without the cache: an answer fetched for one URL is not kept under a name
|
|
39
|
+
* another URL could be given.
|
|
40
|
+
*/
|
|
41
|
+
export declare function openHttp(name: string, url: string, http: HttpConnectionConfig, options: {
|
|
42
|
+
secrets: boolean;
|
|
43
|
+
cache?: CacheStore;
|
|
44
|
+
environment?: NodeJS.ProcessEnv;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
adapter: HttpAdapter;
|
|
47
|
+
secret: SecretSource;
|
|
48
|
+
warning?: string;
|
|
49
|
+
}>;
|