@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,351 @@
|
|
|
1
|
+
import { CacheUnavailable, scopedCache } from '../../adapters/cache/cacheStore.js';
|
|
2
|
+
import { forwardedClient, ipv6Groups } from './trust.js';
|
|
3
|
+
const MAX_CLIENTS = 10_000;
|
|
4
|
+
function assertWindows(windows) {
|
|
5
|
+
if (!windows.length || windows.some((w) => !Number.isSafeInteger(w.limit) || w.limit < 1 || !Number.isFinite(w.ms) || w.ms <= 0)) {
|
|
6
|
+
throw new Error('A rate limiter needs at least one window of a positive limit over a positive span.');
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** One budget over a set of windows: where each window started and what it has counted. */
|
|
10
|
+
class Budget {
|
|
11
|
+
starts;
|
|
12
|
+
counts;
|
|
13
|
+
seen;
|
|
14
|
+
constructor(windows, now) {
|
|
15
|
+
this.starts = windows.map(() => now);
|
|
16
|
+
this.counts = windows.map(() => 0);
|
|
17
|
+
this.seen = now;
|
|
18
|
+
}
|
|
19
|
+
/** Roll over the windows that have elapsed; the seconds until the longest exceeded one reopens, or 0. */
|
|
20
|
+
wait(windows, now) {
|
|
21
|
+
let wait = 0;
|
|
22
|
+
windows.forEach((window, i) => {
|
|
23
|
+
if (now - this.starts[i] >= window.ms) {
|
|
24
|
+
this.starts[i] = now;
|
|
25
|
+
this.counts[i] = 0;
|
|
26
|
+
}
|
|
27
|
+
if (this.counts[i] >= window.limit)
|
|
28
|
+
wait = Math.max(wait, Math.ceil((this.starts[i] + window.ms - now) / 1000), 1);
|
|
29
|
+
});
|
|
30
|
+
return wait;
|
|
31
|
+
}
|
|
32
|
+
/** Count one request; what is left in the tightest window. */
|
|
33
|
+
count(windows) {
|
|
34
|
+
let remaining = Infinity;
|
|
35
|
+
windows.forEach((window, i) => {
|
|
36
|
+
this.counts[i]++;
|
|
37
|
+
remaining = Math.min(remaining, window.limit - this.counts[i]);
|
|
38
|
+
});
|
|
39
|
+
return remaining;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The key a client's budget is kept under. IPv4 as it is; an IPv4-mapped
|
|
44
|
+
* IPv6 address (`::ffff:192.0.2.1`, how a dual-stack socket reports an IPv4
|
|
45
|
+
* peer) as that IPv4 address; any other IPv6 address as its /64, the
|
|
46
|
+
* smallest network one subscriber is routinely given — one host can
|
|
47
|
+
* otherwise draw a fresh budget from each of its 2^64 addresses. Anything
|
|
48
|
+
* that is not an address (`unknown`) is its own key.
|
|
49
|
+
*/
|
|
50
|
+
export function limitKey(address) {
|
|
51
|
+
if (!address.includes(':'))
|
|
52
|
+
return address;
|
|
53
|
+
const groups = ipv6Groups(address);
|
|
54
|
+
if (!groups)
|
|
55
|
+
return address;
|
|
56
|
+
if (groups.slice(0, 5).every((group) => group === 0) && groups[5] === 0xffff) {
|
|
57
|
+
return `${groups[6] >> 8}.${groups[6] & 255}.${groups[7] >> 8}.${groups[7] & 255}`;
|
|
58
|
+
}
|
|
59
|
+
return `${groups
|
|
60
|
+
.slice(0, 4)
|
|
61
|
+
.map((group) => group.toString(16))
|
|
62
|
+
.join(':')}::/64`;
|
|
63
|
+
}
|
|
64
|
+
export class FixedWindowLimiter {
|
|
65
|
+
windows;
|
|
66
|
+
now;
|
|
67
|
+
/* Least recently seen first. Named `hits` still: the review's probe reads its size. */
|
|
68
|
+
hits = new Map();
|
|
69
|
+
longest;
|
|
70
|
+
maxClients;
|
|
71
|
+
whenFull;
|
|
72
|
+
totalWindows;
|
|
73
|
+
total;
|
|
74
|
+
constructor(windows, now = Date.now, options = {}) {
|
|
75
|
+
this.windows = windows;
|
|
76
|
+
this.now = now;
|
|
77
|
+
assertWindows(windows);
|
|
78
|
+
if (options.total)
|
|
79
|
+
assertWindows(options.total);
|
|
80
|
+
const maxClients = options.maxClients ?? MAX_CLIENTS;
|
|
81
|
+
if (!Number.isSafeInteger(maxClients) || maxClients < 1)
|
|
82
|
+
throw new Error('A rate limiter tracks at least one client.');
|
|
83
|
+
this.longest = Math.max(...windows.map((window) => window.ms));
|
|
84
|
+
this.maxClients = maxClients;
|
|
85
|
+
this.whenFull = options.whenFull ?? 'refuse';
|
|
86
|
+
this.totalWindows = options.total;
|
|
87
|
+
}
|
|
88
|
+
/** Count one request for the client, or say how long it waits. */
|
|
89
|
+
take(address, part = 'both') {
|
|
90
|
+
const now = this.now();
|
|
91
|
+
this.expire(now);
|
|
92
|
+
if (part === 'all')
|
|
93
|
+
return this.takeShared(now) ?? { ok: true, remaining: Infinity };
|
|
94
|
+
const key = limitKey(address);
|
|
95
|
+
const entry = this.hits.get(key);
|
|
96
|
+
if (entry) {
|
|
97
|
+
// Seen again, refused or not: a client still knocking keeps what it spent.
|
|
98
|
+
this.hits.delete(key);
|
|
99
|
+
this.hits.set(key, entry);
|
|
100
|
+
entry.seen = now;
|
|
101
|
+
const wait = entry.wait(this.windows, now);
|
|
102
|
+
if (wait > 0)
|
|
103
|
+
return { ok: false, retryAfterSeconds: wait, scope: 'client' };
|
|
104
|
+
}
|
|
105
|
+
const shared = part === 'both' && this.totalWindows ? (this.total ??= new Budget(this.totalWindows, now)) : undefined;
|
|
106
|
+
const sharedWait = shared ? shared.wait(this.totalWindows, now) : 0;
|
|
107
|
+
if (sharedWait > 0)
|
|
108
|
+
return { ok: false, retryAfterSeconds: sharedWait, scope: 'all' };
|
|
109
|
+
let budget = entry;
|
|
110
|
+
if (!budget) {
|
|
111
|
+
if (this.hits.size >= this.maxClients) {
|
|
112
|
+
const [stalest, oldest] = this.hits.entries().next().value;
|
|
113
|
+
/* Every slot holds a budget still in force. Evicting one forgets
|
|
114
|
+
what that client spent, so a client rotating addresses past the
|
|
115
|
+
cap would come back to a fresh budget — the one thing a limiter
|
|
116
|
+
exists to withhold. `refuse` keeps every budget and turns the
|
|
117
|
+
newcomer away until the stalest slot expires on its own (at most
|
|
118
|
+
the longest window): the fail-safe direction, and the submit
|
|
119
|
+
limiter's. It refuses no one a flood would not already have
|
|
120
|
+
refused, since reaching the cap there needs more admitted
|
|
121
|
+
submissions than the shared ceiling allows in a window. `evict`
|
|
122
|
+
is for reads, where a forgotten budget costs a page of
|
|
123
|
+
presentation and a refusal would let a flood of addresses blank
|
|
124
|
+
the form for everyone arriving after it. */
|
|
125
|
+
if (this.whenFull === 'refuse') {
|
|
126
|
+
return { ok: false, retryAfterSeconds: Math.max(1, Math.ceil((oldest.seen + this.longest - now) / 1000)), scope: 'all' };
|
|
127
|
+
}
|
|
128
|
+
this.hits.delete(stalest);
|
|
129
|
+
}
|
|
130
|
+
budget = new Budget(this.windows, now);
|
|
131
|
+
this.hits.set(key, budget);
|
|
132
|
+
}
|
|
133
|
+
const remaining = budget.count(this.windows);
|
|
134
|
+
shared?.count(this.totalWindows);
|
|
135
|
+
return { ok: true, remaining };
|
|
136
|
+
}
|
|
137
|
+
/* The shared ceiling alone: a refusal, or undefined once counted (or where there is none). */
|
|
138
|
+
takeShared(now) {
|
|
139
|
+
if (!this.totalWindows)
|
|
140
|
+
return undefined;
|
|
141
|
+
const shared = (this.total ??= new Budget(this.totalWindows, now));
|
|
142
|
+
const wait = shared.wait(this.totalWindows, now);
|
|
143
|
+
if (wait > 0)
|
|
144
|
+
return { ok: false, retryAfterSeconds: wait, scope: 'all' };
|
|
145
|
+
shared.count(this.totalWindows);
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
/** How many clients are tracked; tests read it. */
|
|
149
|
+
get size() {
|
|
150
|
+
return this.hits.size;
|
|
151
|
+
}
|
|
152
|
+
/** Forget every client and the shared count; tests and a policy reload use it. */
|
|
153
|
+
reset() {
|
|
154
|
+
this.hits.clear();
|
|
155
|
+
this.total = undefined;
|
|
156
|
+
}
|
|
157
|
+
/* A budget last seen a longest-window ago has nothing left in force:
|
|
158
|
+
each take rolls over what elapsed, so every window then began after
|
|
159
|
+
`seen - longest`. The Map is in seen order, so the sweep stops at the
|
|
160
|
+
first entry still live. */
|
|
161
|
+
expire(now) {
|
|
162
|
+
for (const [key, entry] of this.hits) {
|
|
163
|
+
if (entry.seen + this.longest > now)
|
|
164
|
+
break;
|
|
165
|
+
this.hits.delete(key);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The same windows, counted in the deployment's cache so every instance
|
|
171
|
+
* spends one budget. Each window is a key per client (and one for everyone)
|
|
172
|
+
* counted by the cache's atomic windowed count, which starts the window on
|
|
173
|
+
* the first request; a refused request is counted too, which only keeps an
|
|
174
|
+
* exceeded window exceeded until it ends on its own. There is no cap on
|
|
175
|
+
* tracked clients to keep: every key expires with its window.
|
|
176
|
+
*
|
|
177
|
+
* When the cache cannot count, the process's own limiter decides — the
|
|
178
|
+
* budget degrades to one instance's, never to none.
|
|
179
|
+
*/
|
|
180
|
+
export class SharedWindowLimiter {
|
|
181
|
+
name;
|
|
182
|
+
windows;
|
|
183
|
+
cache;
|
|
184
|
+
fallback;
|
|
185
|
+
totalWindows;
|
|
186
|
+
constructor(name, windows, cache, fallback, totalWindows) {
|
|
187
|
+
this.name = name;
|
|
188
|
+
this.windows = windows;
|
|
189
|
+
this.cache = cache;
|
|
190
|
+
this.fallback = fallback;
|
|
191
|
+
this.totalWindows = totalWindows;
|
|
192
|
+
assertWindows(windows);
|
|
193
|
+
if (totalWindows)
|
|
194
|
+
assertWindows(totalWindows);
|
|
195
|
+
}
|
|
196
|
+
async take(address, part = 'both') {
|
|
197
|
+
try {
|
|
198
|
+
let remaining = Infinity;
|
|
199
|
+
if (part !== 'all') {
|
|
200
|
+
const client = await this.count(this.windows, `${this.name}:${limitKey(address)}`);
|
|
201
|
+
if (client.wait > 0)
|
|
202
|
+
return { ok: false, retryAfterSeconds: client.wait, scope: 'client' };
|
|
203
|
+
remaining = client.remaining;
|
|
204
|
+
}
|
|
205
|
+
if (part !== 'client' && this.totalWindows) {
|
|
206
|
+
const all = await this.count(this.totalWindows, `${this.name}:*`);
|
|
207
|
+
if (all.wait > 0)
|
|
208
|
+
return { ok: false, retryAfterSeconds: all.wait, scope: 'all' };
|
|
209
|
+
}
|
|
210
|
+
return { ok: true, remaining };
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
if (!(error instanceof CacheUnavailable))
|
|
214
|
+
throw error;
|
|
215
|
+
return this.fallback.take(address, part);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
reset() {
|
|
219
|
+
this.fallback.reset();
|
|
220
|
+
}
|
|
221
|
+
async count(windows, key) {
|
|
222
|
+
let wait = 0;
|
|
223
|
+
let remaining = Infinity;
|
|
224
|
+
for (const [i, window] of windows.entries()) {
|
|
225
|
+
const { count, ttlMs } = await this.cache.incr(`${key}:${i}`, window.ms);
|
|
226
|
+
if (count > window.limit)
|
|
227
|
+
wait = Math.max(wait, Math.max(1, Math.ceil(ttlMs / 1000)));
|
|
228
|
+
else
|
|
229
|
+
remaining = Math.min(remaining, window.limit - count);
|
|
230
|
+
}
|
|
231
|
+
return { wait, remaining };
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/** The client a limit counts against; a forwarded header only from a trusted peer (loopback, or `--trust-proxy`). */
|
|
235
|
+
export function clientAddress(req, trusted = []) {
|
|
236
|
+
return forwardedClient(req, trusted);
|
|
237
|
+
}
|
|
238
|
+
/* The public forms' defaults, across every public form of the App: five
|
|
239
|
+
submissions a minute and thirty an hour per client, three hundred an hour
|
|
240
|
+
across every client, sixty reads a minute per client. One instance per
|
|
241
|
+
process, shared by every request; `resetPublicFormLimits` is for tests
|
|
242
|
+
that run several servers in one. */
|
|
243
|
+
export const PUBLIC_FORM_SUBMIT_WINDOWS = Object.freeze([
|
|
244
|
+
{ limit: 5, ms: 60_000 },
|
|
245
|
+
{ limit: 30, ms: 3_600_000 },
|
|
246
|
+
]);
|
|
247
|
+
export const PUBLIC_FORM_SUBMIT_TOTAL = Object.freeze([{ limit: 300, ms: 3_600_000 }]);
|
|
248
|
+
export const PUBLIC_FORM_READ_WINDOWS = Object.freeze([{ limit: 60, ms: 60_000 }]);
|
|
249
|
+
/* An account-creating invitation's claim is shaped like a submission — the
|
|
250
|
+
same per-client windows and ceiling, the same refusal — but counted in a
|
|
251
|
+
budget of its own: a busy public form must not use up the claims a dealer's
|
|
252
|
+
new staff can make, nor the other way about. */
|
|
253
|
+
export const INVITATION_CLAIM_WINDOWS = PUBLIC_FORM_SUBMIT_WINDOWS;
|
|
254
|
+
export const INVITATION_CLAIM_TOTAL = PUBLIC_FORM_SUBMIT_TOTAL;
|
|
255
|
+
/* A form saved on every step opens a draft in the App's real tables on its
|
|
256
|
+
first step, so the drafts one client may open are capped apart from its
|
|
257
|
+
submissions: ten an hour — an applicant who starts over a few times is never
|
|
258
|
+
near it, and a script cannot fill the root table with abandoned drafts. A
|
|
259
|
+
status check is a guess at a receipt and its confirmations, so it is
|
|
260
|
+
counted tighter still, per client here and per receipt in its own lockout
|
|
261
|
+
(forms/statusCheck.ts). */
|
|
262
|
+
const SAVED_FORM_START_WINDOWS = Object.freeze([{ limit: 10, ms: 3_600_000 }]);
|
|
263
|
+
const SAVED_FORM_START_TOTAL = Object.freeze([{ limit: 500, ms: 3_600_000 }]);
|
|
264
|
+
const STATUS_CHECK_WINDOWS = Object.freeze([
|
|
265
|
+
{ limit: 5, ms: 60_000 },
|
|
266
|
+
{ limit: 20, ms: 3_600_000 },
|
|
267
|
+
]);
|
|
268
|
+
const STATUS_CHECK_TOTAL = Object.freeze([{ limit: 600, ms: 3_600_000 }]);
|
|
269
|
+
/* Adding an application to an account — spending the browser's claim, or
|
|
270
|
+
linking after a status check — is counted on its own too: a person links
|
|
271
|
+
the one application they made, so a handful a minute is already generous. */
|
|
272
|
+
const LINK_WINDOWS = Object.freeze([
|
|
273
|
+
{ limit: 5, ms: 60_000 },
|
|
274
|
+
{ limit: 20, ms: 3_600_000 },
|
|
275
|
+
]);
|
|
276
|
+
const LINK_TOTAL = Object.freeze([{ limit: 300, ms: 3_600_000 }]);
|
|
277
|
+
/* A request that makes the server send an email — a code to prove an
|
|
278
|
+
address — is counted apart and tightest: a person asks for one or two. */
|
|
279
|
+
const CODE_WINDOWS = Object.freeze([
|
|
280
|
+
{ limit: 3, ms: 60_000 },
|
|
281
|
+
{ limit: 10, ms: 3_600_000 },
|
|
282
|
+
]);
|
|
283
|
+
const CODE_TOTAL = Object.freeze([{ limit: 300, ms: 3_600_000 }]);
|
|
284
|
+
/* A step's check: a form's own pages asking the server's word as someone goes on, so a page's worth a minute from one
|
|
285
|
+
client, and never counted against what that client may submit. */
|
|
286
|
+
const CHECK_WINDOWS = Object.freeze([{ limit: 60, ms: 60_000 }]);
|
|
287
|
+
const CHECK_TOTAL = Object.freeze([{ limit: 6_000, ms: 60_000 }]);
|
|
288
|
+
const check = new FixedWindowLimiter(CHECK_WINDOWS, Date.now, { total: CHECK_TOTAL, whenFull: 'evict' });
|
|
289
|
+
const submit = new FixedWindowLimiter(PUBLIC_FORM_SUBMIT_WINDOWS, Date.now, { total: PUBLIC_FORM_SUBMIT_TOTAL, whenFull: 'refuse' });
|
|
290
|
+
const read = new FixedWindowLimiter(PUBLIC_FORM_READ_WINDOWS, Date.now, { whenFull: 'evict' });
|
|
291
|
+
const claim = new FixedWindowLimiter(INVITATION_CLAIM_WINDOWS, Date.now, { total: INVITATION_CLAIM_TOTAL, whenFull: 'refuse' });
|
|
292
|
+
const start = new FixedWindowLimiter(SAVED_FORM_START_WINDOWS, Date.now, { total: SAVED_FORM_START_TOTAL, whenFull: 'refuse' });
|
|
293
|
+
const status = new FixedWindowLimiter(STATUS_CHECK_WINDOWS, Date.now, { total: STATUS_CHECK_TOTAL, whenFull: 'refuse' });
|
|
294
|
+
const link = new FixedWindowLimiter(LINK_WINDOWS, Date.now, { total: LINK_TOTAL, whenFull: 'refuse' });
|
|
295
|
+
const code = new FixedWindowLimiter(CODE_WINDOWS, Date.now, { total: CODE_TOTAL, whenFull: 'refuse' });
|
|
296
|
+
let shared;
|
|
297
|
+
export function publicFormLimiter(kind) {
|
|
298
|
+
if (shared)
|
|
299
|
+
return shared[kind];
|
|
300
|
+
if (kind === 'check')
|
|
301
|
+
return check;
|
|
302
|
+
return kind === 'submit'
|
|
303
|
+
? submit
|
|
304
|
+
: kind === 'read'
|
|
305
|
+
? read
|
|
306
|
+
: kind === 'start'
|
|
307
|
+
? start
|
|
308
|
+
: kind === 'link'
|
|
309
|
+
? link
|
|
310
|
+
: kind === 'code'
|
|
311
|
+
? code
|
|
312
|
+
: status;
|
|
313
|
+
}
|
|
314
|
+
/** The invitation claims' own budget (tenantInvitationHttp.ts), never the forms'. */
|
|
315
|
+
export function invitationClaimLimiter() {
|
|
316
|
+
return shared ? shared.claim : claim;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Count the public forms' windows in the deployment's cache, under the
|
|
320
|
+
* deployment's namespace (`tw:<app>:form:`), or — with no cache, or the
|
|
321
|
+
* process's own — in this process, as before.
|
|
322
|
+
*/
|
|
323
|
+
export function configurePublicFormLimits(cache, namespace) {
|
|
324
|
+
if (!cache || cache.kind === 'memory') {
|
|
325
|
+
shared = undefined;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const scoped = scopedCache(cache, `tw:${namespace}:form:`);
|
|
329
|
+
shared = {
|
|
330
|
+
submit: new SharedWindowLimiter('submit', PUBLIC_FORM_SUBMIT_WINDOWS, scoped, submit, PUBLIC_FORM_SUBMIT_TOTAL),
|
|
331
|
+
read: new SharedWindowLimiter('read', PUBLIC_FORM_READ_WINDOWS, scoped, read),
|
|
332
|
+
// Its own key prefix, so the claims' windows are counted apart from the forms'.
|
|
333
|
+
claim: new SharedWindowLimiter('claim', INVITATION_CLAIM_WINDOWS, scoped, claim, INVITATION_CLAIM_TOTAL),
|
|
334
|
+
start: new SharedWindowLimiter('start', SAVED_FORM_START_WINDOWS, scoped, start, SAVED_FORM_START_TOTAL),
|
|
335
|
+
status: new SharedWindowLimiter('status', STATUS_CHECK_WINDOWS, scoped, status, STATUS_CHECK_TOTAL),
|
|
336
|
+
link: new SharedWindowLimiter('link', LINK_WINDOWS, scoped, link, LINK_TOTAL),
|
|
337
|
+
code: new SharedWindowLimiter('code', CODE_WINDOWS, scoped, code, CODE_TOTAL),
|
|
338
|
+
check: new SharedWindowLimiter('check', CHECK_WINDOWS, scoped, check, CHECK_TOTAL),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
/** Forgets the forms' budgets and the claims'; tests and a policy reload use it. */
|
|
342
|
+
export function resetPublicFormLimits() {
|
|
343
|
+
submit.reset();
|
|
344
|
+
read.reset();
|
|
345
|
+
claim.reset();
|
|
346
|
+
start.reset();
|
|
347
|
+
status.reset();
|
|
348
|
+
link.reset();
|
|
349
|
+
code.reset();
|
|
350
|
+
check.reset();
|
|
351
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type RequestContext, type ResolvedSource } from './context.js';
|
|
2
|
+
/** What a product serves among the reads. */
|
|
3
|
+
export interface ReadHooks {
|
|
4
|
+
/** What the client draws around the data: the App, its pages, the stored layouts and filters. True when answered. */
|
|
5
|
+
projection(ctx: RequestContext, source: ResolvedSource): Promise<boolean>;
|
|
6
|
+
/** One file's bytes, from a resource whose rows are files. False where the resource holds none. */
|
|
7
|
+
file?(ctx: RequestContext, source: ResolvedSource): Promise<boolean>;
|
|
8
|
+
}
|
|
9
|
+
export declare function readRoutes(ctx: RequestContext, source: ResolvedSource, hooks: ReadHooks): Promise<boolean>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The GET reads that need a resolved database: the schema and the
|
|
3
|
+
* connection-scoped halves of the config file — views, saved SQL, a table's
|
|
4
|
+
* DDL. What a product draws around the data (`ReadHooks.projection`: an
|
|
5
|
+
* App's payload and pages, or the database browser's) sits between them in
|
|
6
|
+
* the same chain, and a resource whose rows are files is the App's
|
|
7
|
+
* (`ReadHooks.file`, appFileRead.ts).
|
|
8
|
+
*/
|
|
9
|
+
import { validateView } from '../../shared/view.js';
|
|
10
|
+
import { textToView, viewToTextWithSchema } from '../../shared/viewtext.js';
|
|
11
|
+
import { queriesFor } from '../../shared/sqlsaved.js';
|
|
12
|
+
import { findTable, renderDDL } from '../../shared/schema.js';
|
|
13
|
+
import { send, sendRevalidated } from './reply.js';
|
|
14
|
+
import { principalOf } from './context.js';
|
|
15
|
+
/** The `Sec-Fetch-Dest` values that load what they fetch as code. */
|
|
16
|
+
const SCRIPT_DESTINATIONS = new Set(['script', 'style', 'worker', 'sharedworker', 'serviceworker', 'audioworklet', 'paintworklet', 'xslt']);
|
|
17
|
+
export async function readRoutes(ctx, source, hooks) {
|
|
18
|
+
const { res, url, path, registry, configViews, configQueries } = ctx;
|
|
19
|
+
const { adapter, schema, access, connectionId, publicScope, publicDefinition } = source;
|
|
20
|
+
/**
|
|
21
|
+
* Views defined in the config file, for the connection in hand.
|
|
22
|
+
*
|
|
23
|
+
* Saved views live in the browser, per connection — they are one person's
|
|
24
|
+
* working set, and writing them into a file the server owns would make a
|
|
25
|
+
* page silently edit something on disk. Config views are the shared half:
|
|
26
|
+
* checked into a repo, the same for everyone, and read-only from here.
|
|
27
|
+
*
|
|
28
|
+
* A view whose base table does not exist on this connection is returned
|
|
29
|
+
* *with its reason* rather than dropped. Dropping it shows a shorter list
|
|
30
|
+
* and no explanation, which is the failure this whole tool argues against.
|
|
31
|
+
*/
|
|
32
|
+
if (path === '/api/views') {
|
|
33
|
+
const name = registry.list().find((c) => c.id === connectionId)?.name;
|
|
34
|
+
const mine = configViews.filter((v) => !v.connection || v.connection === connectionId || v.connection === name);
|
|
35
|
+
send(res, 200, {
|
|
36
|
+
...publicScope,
|
|
37
|
+
views: access ? [] : mine.map((v) => publicDefinition(materialise(schema, v))),
|
|
38
|
+
});
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (path === '/api/schema') {
|
|
42
|
+
sendRevalidated(res, schema, principalOf(ctx.identity));
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* One file's bytes, from a resource whose rows are files (`file` on the
|
|
47
|
+
* resource). The read is the ordinary query, so the App's table and field
|
|
48
|
+
* grants apply to it, asked for the bytes instead of their label. A
|
|
49
|
+
* download, unless `inline=1` asks for a picture and the bytes are one: a
|
|
50
|
+
* stored type is only the uploader's claim, so it never decides either.
|
|
51
|
+
* `variant=thumb` asks for the resource's smaller copy (`file.thumbnail`),
|
|
52
|
+
* which a card draws at a third of the width; a resource or a row without
|
|
53
|
+
* one answers with the file itself.
|
|
54
|
+
*/
|
|
55
|
+
if (path === '/api/file') {
|
|
56
|
+
/* An upload is never code to run. A page that loads one as a script or a
|
|
57
|
+
stylesheet (`<script src="/api/file?…">`) asks with its destination,
|
|
58
|
+
and is refused before anything is read: with nosniff and a download's
|
|
59
|
+
type the browser would refuse it too, but only if it honours both. */
|
|
60
|
+
if (SCRIPT_DESTINATIONS.has(String(ctx.req.headers['sec-fetch-dest'] ?? '').toLowerCase())) {
|
|
61
|
+
send(res, 403, { error: 'A file is not served as a script or a stylesheet.' });
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
if (!(await hooks.file?.(ctx, source)))
|
|
65
|
+
send(res, 404, { error: 'This resource does not hold files.' });
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
if (await hooks.projection(ctx, source))
|
|
69
|
+
return true;
|
|
70
|
+
/**
|
|
71
|
+
* SQL statements the config file names, for the connection in hand.
|
|
72
|
+
*
|
|
73
|
+
* The committable half of saved SQL. The other half lives in the browser,
|
|
74
|
+
* and the client merges them — a statement someone checked in and one you
|
|
75
|
+
* saved this morning are the same kind of thing to whoever is looking for
|
|
76
|
+
* one, and only differ in whether you may delete it.
|
|
77
|
+
*/
|
|
78
|
+
if (path === '/api/queries') {
|
|
79
|
+
const name = registry.list().find((c) => c.id === connectionId)?.name;
|
|
80
|
+
send(res, 200, {
|
|
81
|
+
connection: connectionId,
|
|
82
|
+
queries: queriesFor(configQueries, connectionId, name),
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* A table's definition. `exact` tells the client whether it is looking at
|
|
88
|
+
* the database's own stored DDL or a reconstruction from the catalog, so
|
|
89
|
+
* the page can label it rather than implying an authority it lacks.
|
|
90
|
+
*/
|
|
91
|
+
if (path === '/api/ddl') {
|
|
92
|
+
const tableId = url.searchParams.get('table') ?? '';
|
|
93
|
+
if (!findTable(schema, tableId)) {
|
|
94
|
+
send(res, 404, { error: `Unknown table "${tableId}"` });
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
const stored = adapter.ddl ? await adapter.ddl(tableId) : undefined;
|
|
98
|
+
send(res, 200, {
|
|
99
|
+
table: tableId,
|
|
100
|
+
sql: stored ?? renderDDL(schema, tableId),
|
|
101
|
+
exact: Boolean(stored),
|
|
102
|
+
});
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Expand a config view against a live schema, and say what is wrong with it.
|
|
109
|
+
*
|
|
110
|
+
* A view written as text cannot be expanded when the config file is read —
|
|
111
|
+
* there is no connection open yet, and the text names tables. So it is
|
|
112
|
+
* carried through and resolved here, where a schema exists. Both halves are
|
|
113
|
+
* returned: the structure, so the composer can edit it by clicking, and the
|
|
114
|
+
* text, so it can be read and edited as a statement.
|
|
115
|
+
*
|
|
116
|
+
* A view that does not resolve comes back *with its reason*, never dropped.
|
|
117
|
+
* Dropping it gives someone a shorter list and no explanation, which is the
|
|
118
|
+
* failure this whole tool argues against.
|
|
119
|
+
*/
|
|
120
|
+
function materialise(schema, view) {
|
|
121
|
+
if (!view.text) {
|
|
122
|
+
const error = validateView(schema, view);
|
|
123
|
+
return { ...view, error, text: error ? undefined : viewToTextWithSchema(schema, view) };
|
|
124
|
+
}
|
|
125
|
+
const parsed = textToView(schema, view.text, { id: view.id, name: view.name });
|
|
126
|
+
if (!parsed.view || parsed.errors.length) {
|
|
127
|
+
return { ...view, error: parsed.errors[0]?.message ?? `"${view.name}" could not be read.` };
|
|
128
|
+
}
|
|
129
|
+
const expanded = { ...parsed.view, source: view.source, connection: view.connection, text: view.text };
|
|
130
|
+
return { ...expanded, error: validateView(schema, expanded) };
|
|
131
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { type ReadContext } from '../../adapters/replicas.js';
|
|
3
|
+
/** Whether the POST `path` with `body` may read from a replica. */
|
|
4
|
+
export declare function toleratesLag(path: string, body: Readonly<Record<string, unknown>>): boolean;
|
|
5
|
+
/** The cookie a write sets so the same browser's next reads go to the primary, in the running product's name. */
|
|
6
|
+
export declare const freshCookie: () => string;
|
|
7
|
+
/**
|
|
8
|
+
* The read-your-write context for one request (replicas.ts `readsAs`).
|
|
9
|
+
*
|
|
10
|
+
* A browser carries the marker: a write sets `<product>_fresh=<write time>` (`mistri_fresh`, or `tablewalk_fresh` in the browser)
|
|
11
|
+
* for FRESH_READ_MS on the App's base path, and a request that brings one
|
|
12
|
+
* back within that time reads the primary. It needs no shared state, so it
|
|
13
|
+
* holds whichever pod the next request reaches, and each visitor — signed
|
|
14
|
+
* in or not — has their own. It is HttpOnly and SameSite=Lax like the
|
|
15
|
+
* other cookies here, and never Secure-only: it holds a time, not a
|
|
16
|
+
* secret, and a forged or stolen one buys nothing but primary reads for
|
|
17
|
+
* ten seconds. A caller sending a bearer token has no cookie jar, so its
|
|
18
|
+
* writes are remembered in this process under a digest of the token.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readFreshness(request: Pick<IncomingMessage, 'headers'>, response: Pick<ServerResponse, 'headersSent' | 'getHeader' | 'setHeader'>, base: string, now?: () => number): ReadContext;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which body routes may read from a replica (packages/engine/src/adapters/replicas.ts).
|
|
3
|
+
*
|
|
4
|
+
* Lag-tolerant: a list's page and its counts (`/api/query`, `/api/run`,
|
|
5
|
+
* `/api/view/run`, `/api/count`, `/api/counts`), a dashboard's breakdowns
|
|
6
|
+
* (`/api/breakdown`, `/api/breakdowns`) and an export (`/api/export`) —
|
|
7
|
+
* the Workbench grid and an App's lists, search and charts all arrive as
|
|
8
|
+
* these. A row a moment old is what anyone reading a list already accepts:
|
|
9
|
+
* it was a moment old by the time it was drawn.
|
|
10
|
+
*
|
|
11
|
+
* Everything else reads the primary, and so does a read of one row
|
|
12
|
+
* (`limit: 1`): that is a record's own read — the record page, the
|
|
13
|
+
* Workbench record, a section's probe — and a record is where a person
|
|
14
|
+
* looks for what they just saved. A composed record page is pinned whole
|
|
15
|
+
* (handler.ts), related lists and all. Commands, writes, the catalog,
|
|
16
|
+
* the stores, raw SQL and the MCP tools never ask for a replica.
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from 'node:crypto';
|
|
19
|
+
import { FRESH_READ_MS } from '../../adapters/replicas.js';
|
|
20
|
+
import { product } from '../product.js';
|
|
21
|
+
const LAG_TOLERANT = new Set([
|
|
22
|
+
'/api/query',
|
|
23
|
+
'/api/run',
|
|
24
|
+
'/api/view/run',
|
|
25
|
+
'/api/count',
|
|
26
|
+
'/api/counts',
|
|
27
|
+
'/api/breakdown',
|
|
28
|
+
'/api/breakdowns',
|
|
29
|
+
'/api/export',
|
|
30
|
+
]);
|
|
31
|
+
/** Whether the POST `path` with `body` may read from a replica. */
|
|
32
|
+
export function toleratesLag(path, body) {
|
|
33
|
+
if (!LAG_TOLERANT.has(path))
|
|
34
|
+
return false;
|
|
35
|
+
const view = body.view && typeof body.view === 'object' ? body.view : undefined;
|
|
36
|
+
return body.limit !== 1 && view?.limit !== 1;
|
|
37
|
+
}
|
|
38
|
+
/** The cookie a write sets so the same browser's next reads go to the primary, in the running product's name. */
|
|
39
|
+
export const freshCookie = () => `${product()}_fresh`;
|
|
40
|
+
/**
|
|
41
|
+
* The read-your-write context for one request (replicas.ts `readsAs`).
|
|
42
|
+
*
|
|
43
|
+
* A browser carries the marker: a write sets `<product>_fresh=<write time>` (`mistri_fresh`, or `tablewalk_fresh` in the browser)
|
|
44
|
+
* for FRESH_READ_MS on the App's base path, and a request that brings one
|
|
45
|
+
* back within that time reads the primary. It needs no shared state, so it
|
|
46
|
+
* holds whichever pod the next request reaches, and each visitor — signed
|
|
47
|
+
* in or not — has their own. It is HttpOnly and SameSite=Lax like the
|
|
48
|
+
* other cookies here, and never Secure-only: it holds a time, not a
|
|
49
|
+
* secret, and a forged or stolen one buys nothing but primary reads for
|
|
50
|
+
* ten seconds. A caller sending a bearer token has no cookie jar, so its
|
|
51
|
+
* writes are remembered in this process under a digest of the token.
|
|
52
|
+
*/
|
|
53
|
+
export function readFreshness(request, response, base, now = Date.now) {
|
|
54
|
+
const bearer = /^Bearer\s+(\S+)/i.exec(request.headers.authorization ?? '')?.[1];
|
|
55
|
+
if (bearer)
|
|
56
|
+
return { fresh: false, key: `bearer:${createHash('sha256').update(bearer).digest('base64url').slice(0, 22)}` };
|
|
57
|
+
const at = now();
|
|
58
|
+
const fresh = cookieValues(request.headers.cookie, freshCookie()).some((value) => {
|
|
59
|
+
const wrote = /^\d{1,16}$/.test(value) ? Number(value) : NaN;
|
|
60
|
+
// A time ahead of this clock by more than a little is not one this server wrote.
|
|
61
|
+
return wrote <= at + 1_000 && at - wrote < FRESH_READ_MS;
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
fresh,
|
|
65
|
+
onWrite: () => {
|
|
66
|
+
if (response.headersSent)
|
|
67
|
+
return;
|
|
68
|
+
const cookie = `${freshCookie()}=${now()}; Path=${base || '/'}; Max-Age=${FRESH_READ_MS / 1000}; HttpOnly; SameSite=Lax`;
|
|
69
|
+
const prior = response.getHeader('set-cookie');
|
|
70
|
+
response.setHeader('set-cookie', prior === undefined ? cookie : [...(Array.isArray(prior) ? prior : [String(prior)]), cookie]);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function cookieValues(header, name) {
|
|
75
|
+
return (header ?? '')
|
|
76
|
+
.split(';')
|
|
77
|
+
.map((part) => part.trim())
|
|
78
|
+
.filter((part) => part.startsWith(`${name}=`))
|
|
79
|
+
.map((part) => part.slice(name.length + 1));
|
|
80
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A record page in one request (shared/recordReads.ts says why).
|
|
3
|
+
*
|
|
4
|
+
* `POST /api/pages/compose` takes what `/api/pages/plan` takes — the page,
|
|
5
|
+
* by name or as itself, and the record's key — with the reader's page size
|
|
6
|
+
* for related lists (`rows`) and the tab it opens on (`tab`). It plans the
|
|
7
|
+
* page, then asks every read the sections it draws will send: the record's
|
|
8
|
+
* row once for every section that reads it, each related list's first page
|
|
9
|
+
* and totals, each figure, each timeline and custom read, each `when`, the
|
|
10
|
+
* record's history and which of its commands are available. Each is asked
|
|
11
|
+
* of the route the section's own request reaches, on this request's source —
|
|
12
|
+
* one admission, one session, one row-policy context, the same grants, row
|
|
13
|
+
* scope and hidden columns — through `post`, which is the dispatcher's own,
|
|
14
|
+
* into a reply held in memory. What a signed-out visitor or a row policy may
|
|
15
|
+
* not ask on its own is not asked here either.
|
|
16
|
+
*
|
|
17
|
+
* The answer is the plan, as `/api/pages/plan` answers it, and beside it
|
|
18
|
+
* each read that succeeded with the request it answers. A read that failed
|
|
19
|
+
* is left out: the section asks it itself, and is refused in its own words.
|
|
20
|
+
*/
|
|
21
|
+
import type { ServerResponse } from 'node:http';
|
|
22
|
+
import { type PageDef } from '../../shared/page.js';
|
|
23
|
+
import type { RequestContext, ResolvedSource } from './context.js';
|
|
24
|
+
export declare const COMPOSE_PATH = "/api/pages/compose";
|
|
25
|
+
/** The resources of the App a page is drawn in, as far as composing its reads needs them. */
|
|
26
|
+
export type ComposeResources = Record<string, {
|
|
27
|
+
recordLabel?: string | false;
|
|
28
|
+
defined?: Record<string, {
|
|
29
|
+
per?: string;
|
|
30
|
+
}>;
|
|
31
|
+
actions?: Array<{
|
|
32
|
+
when?: string;
|
|
33
|
+
command?: {
|
|
34
|
+
id: string;
|
|
35
|
+
version: number;
|
|
36
|
+
target: string;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
}>;
|
|
40
|
+
/** One availability check of a record's command, as `/api/commands/availability` takes it. */
|
|
41
|
+
interface CommandCheck {
|
|
42
|
+
readonly command: string;
|
|
43
|
+
readonly commandVersion: number;
|
|
44
|
+
readonly keys: Record<string, unknown>[];
|
|
45
|
+
}
|
|
46
|
+
/** What a product decides of a composed page. The database browser asks every part, has no App, and runs no commands. */
|
|
47
|
+
export interface ComposeHooks {
|
|
48
|
+
/** Whether this request's reader may ask `path` on its own: the gate's and the row policy's rule for that route. */
|
|
49
|
+
mayAsk(ctx: RequestContext, path: string): boolean;
|
|
50
|
+
/** The App's resources as this reader is served them, or none. */
|
|
51
|
+
resources(ctx: RequestContext, source: ResolvedSource): ComposeResources;
|
|
52
|
+
/** A table's suggested page (`suggested:<table>`). */
|
|
53
|
+
suggested(source: ResolvedSource, table: string): PageDef | null;
|
|
54
|
+
/** Which of the record's commands are available to this reader: the availability route's answer, or none. */
|
|
55
|
+
availability?(ctx: RequestContext, checks: CommandCheck[]): Promise<{
|
|
56
|
+
status: number;
|
|
57
|
+
body: unknown;
|
|
58
|
+
} | undefined>;
|
|
59
|
+
}
|
|
60
|
+
export declare function composeRecordPage({ ctx, source, body, post, hooks, }: {
|
|
61
|
+
ctx: RequestContext;
|
|
62
|
+
source: ResolvedSource;
|
|
63
|
+
body: Record<string, unknown>;
|
|
64
|
+
post: (reply: ServerResponse, path: string, body: Record<string, unknown>) => Promise<void>;
|
|
65
|
+
hooks: ComposeHooks;
|
|
66
|
+
}): Promise<void>;
|
|
67
|
+
export {};
|