@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,71 @@
|
|
|
1
|
+
import type { DatabaseSchema } from '../../shared/schema.js';
|
|
2
|
+
import type { ViewDef } from '../../shared/viewPaths.js';
|
|
3
|
+
import type { HttpOperationReply, HttpOperations } from '../../shared/httpOperations.js';
|
|
4
|
+
import { type Adapter, type QueryRequest, type QueryResult, type ViewResult } from '../adapter.js';
|
|
5
|
+
import { type HttpDescription } from './httpDescription.js';
|
|
6
|
+
import { type EgressPolicy } from './egress.js';
|
|
7
|
+
import type { CacheStore } from '../cache/cacheStore.js';
|
|
8
|
+
/** A resolved credential, attached as one header. It lives only here and in egress, never in a result or an error. */
|
|
9
|
+
export interface HttpAuth {
|
|
10
|
+
header: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
13
|
+
interface HttpConnection {
|
|
14
|
+
/** The API's base URL, ending in "/", under which every description path is resolved. */
|
|
15
|
+
base: URL;
|
|
16
|
+
description: HttpDescription;
|
|
17
|
+
label: string;
|
|
18
|
+
auth?: HttpAuth;
|
|
19
|
+
policy: EgressPolicy;
|
|
20
|
+
/**
|
|
21
|
+
* Opt-in, per connection (`"cache": { "ttl": 60 }`): reads are answered
|
|
22
|
+
* from this store for `ttlMs` after the API answered them. Only a 2xx is
|
|
23
|
+
* kept; the explorer's operations are never answered from it.
|
|
24
|
+
*/
|
|
25
|
+
cache?: {
|
|
26
|
+
store: CacheStore;
|
|
27
|
+
ttlMs: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare class HttpAdapter implements Adapter {
|
|
31
|
+
private readonly connection;
|
|
32
|
+
readonly dialect: 'http';
|
|
33
|
+
private readonly walks;
|
|
34
|
+
private catalog?;
|
|
35
|
+
constructor(connection: HttpConnection);
|
|
36
|
+
introspect(): Promise<DatabaseSchema>;
|
|
37
|
+
count(table: string): Promise<number>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
/** A resource's two documented reads, for the explorer's API tab: the list with its declared parameters, and the item by its key. */
|
|
40
|
+
operations(table: string): HttpOperations;
|
|
41
|
+
/**
|
|
42
|
+
* One documented read as the explorer tries it, answered whole: the point is
|
|
43
|
+
* to see what the API says. The request is still built from the description
|
|
44
|
+
* alone — a parameter it does not declare, a sort it does not document, or a
|
|
45
|
+
* paging value that is not a whole number is refused by name before anything
|
|
46
|
+
* is sent — and the reply says the path and query string, never a header,
|
|
47
|
+
* which is where the credential is. A status that is not 2xx comes back as
|
|
48
|
+
* the status, its body unread; a failed call throws HttpSourceError.
|
|
49
|
+
*/
|
|
50
|
+
operation(table: string, kind: 'list' | 'item', params: Record<string, string>): Promise<HttpOperationReply>;
|
|
51
|
+
runView(view: ViewDef, offset?: number): Promise<ViewResult>;
|
|
52
|
+
private lookUp;
|
|
53
|
+
query(request: QueryRequest): Promise<QueryResult>;
|
|
54
|
+
private resource;
|
|
55
|
+
private columns;
|
|
56
|
+
/** What the API can be asked, said in the refusal so the reader knows what does work. */
|
|
57
|
+
private offer;
|
|
58
|
+
private conditions;
|
|
59
|
+
private sort;
|
|
60
|
+
private page;
|
|
61
|
+
private withCursor;
|
|
62
|
+
private remember;
|
|
63
|
+
private rows;
|
|
64
|
+
private row;
|
|
65
|
+
/** One GET under the base URL. `missing` turns a 404 into "no row" — a record read's answer, never a list's. */
|
|
66
|
+
private cachedRequest;
|
|
67
|
+
private get;
|
|
68
|
+
private request;
|
|
69
|
+
private result;
|
|
70
|
+
}
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An API read as a connection: the adapter behind an `http` connection.
|
|
3
|
+
*
|
|
4
|
+
* Its catalog is the description (httpDescription.ts). A list is one GET with
|
|
5
|
+
* the filters, sort and paging the description declares; a record is the item
|
|
6
|
+
* GET by its key. It answers only what the API can: an undeclared filter, an
|
|
7
|
+
* "or", a second sort or a count is refused by name rather than computed here
|
|
8
|
+
* over rows the API was never asked for, because a list that silently showed
|
|
9
|
+
* more or other rows than asked is the one wrong answer. It never writes —
|
|
10
|
+
* writes arrive with their own slice — and every request leaves through
|
|
11
|
+
* egress.ts, which is where the network is trusted or not.
|
|
12
|
+
*/
|
|
13
|
+
import { Refusal } from '../../shared/refusal.js';
|
|
14
|
+
import { compileView } from '../../shared/view.js';
|
|
15
|
+
import { ANSI_STYLE } from '../adapter.js';
|
|
16
|
+
import { describedSchema } from './httpDescription.js';
|
|
17
|
+
import { HttpSourceError, send } from './egress.js';
|
|
18
|
+
/* How far a cursor-paged list is walked to reach a deep page, and how many
|
|
19
|
+
walks are remembered so the next read of the same list jumps straight to
|
|
20
|
+
its page. Deeper than the walk is refused, with the way round it. */
|
|
21
|
+
const MAX_REQUESTS_PER_READ = 8;
|
|
22
|
+
const REMEMBERED_WALKS = 64;
|
|
23
|
+
/* A followed reference reads each distinct target once: at most this many per
|
|
24
|
+
read, this many at a time. */
|
|
25
|
+
const MAX_LOOKUPS_PER_READ = 50;
|
|
26
|
+
const LOOKUP_BATCH = 4;
|
|
27
|
+
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
28
|
+
/** RFC 6901: the value a JSON Pointer names, or undefined. */
|
|
29
|
+
function at(value, pointer) {
|
|
30
|
+
if (!pointer)
|
|
31
|
+
return value;
|
|
32
|
+
let current = value;
|
|
33
|
+
for (const raw of pointer.slice(1).split('/')) {
|
|
34
|
+
const key = raw.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
35
|
+
current = isObject(current) && Object.hasOwn(current, key) ? current[key] : undefined;
|
|
36
|
+
}
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
/* A value the row did not carry, or carried as another type than the
|
|
40
|
+
description says, is shown as empty rather than as something it is not. */
|
|
41
|
+
function cell(column, value) {
|
|
42
|
+
if (value === undefined || value === null)
|
|
43
|
+
return null;
|
|
44
|
+
switch (column.type) {
|
|
45
|
+
case 'json':
|
|
46
|
+
return value;
|
|
47
|
+
case 'boolean':
|
|
48
|
+
return typeof value === 'boolean' ? value : null;
|
|
49
|
+
case 'integer':
|
|
50
|
+
case 'number':
|
|
51
|
+
return typeof value === 'number' ? value : null;
|
|
52
|
+
// Past 2^53 egress hands the digits over as text, which every display path keeps whole.
|
|
53
|
+
case 'bigint':
|
|
54
|
+
return typeof value === 'number' || (typeof value === 'string' && /^-?\d+$/.test(value)) ? value : null;
|
|
55
|
+
default:
|
|
56
|
+
return typeof value === 'string' ? value : null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function appendFilter(query, filter, values) {
|
|
60
|
+
if (filter.serialization !== 'csv') {
|
|
61
|
+
for (const value of values)
|
|
62
|
+
query.append(filter.param, String(value));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const members = values.map((value) => {
|
|
66
|
+
if (!['string', 'number', 'boolean'].includes(typeof value) ||
|
|
67
|
+
(typeof value === 'number' && (!Number.isFinite(value) || /e/i.test(String(value))))) {
|
|
68
|
+
throw new Refusal('This filter value cannot be represented as a CSV member.');
|
|
69
|
+
}
|
|
70
|
+
const text = String(value);
|
|
71
|
+
if (!text.trim() ||
|
|
72
|
+
text.includes(',') ||
|
|
73
|
+
text.includes('..') ||
|
|
74
|
+
[...text].some((char) => char.charCodeAt(0) < 32 || char.charCodeAt(0) === 127)) {
|
|
75
|
+
throw new Refusal('This filter value cannot be represented as a CSV member.');
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
encodeURIComponent(text);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new Refusal('This filter value cannot be represented as a CSV member.');
|
|
82
|
+
}
|
|
83
|
+
return text;
|
|
84
|
+
});
|
|
85
|
+
if (!members.length)
|
|
86
|
+
throw new Refusal('A CSV filter needs at least one value.');
|
|
87
|
+
query.set(filter.param, members.join(','));
|
|
88
|
+
}
|
|
89
|
+
export class HttpAdapter {
|
|
90
|
+
connection;
|
|
91
|
+
dialect = 'http';
|
|
92
|
+
walks = new Map();
|
|
93
|
+
catalog;
|
|
94
|
+
constructor(connection) {
|
|
95
|
+
this.connection = connection;
|
|
96
|
+
}
|
|
97
|
+
async introspect() {
|
|
98
|
+
return describedSchema(this.connection.description, this.connection.label);
|
|
99
|
+
}
|
|
100
|
+
/* No API in the description reports a total, and counting by walking every
|
|
101
|
+
page would spend someone else's rate budget to answer a question nobody
|
|
102
|
+
asked; lists page by "is there another row" instead. */
|
|
103
|
+
async count(table) {
|
|
104
|
+
throw new Refusal(`${table} comes from an API that reports no totals, so it cannot be counted.`);
|
|
105
|
+
}
|
|
106
|
+
async close() { }
|
|
107
|
+
/** A resource's two documented reads, for the explorer's API tab: the list with its declared parameters, and the item by its key. */
|
|
108
|
+
operations(table) {
|
|
109
|
+
const resource = this.resource(table);
|
|
110
|
+
const params = Object.entries(resource.filters).map(([column, filter]) => ({
|
|
111
|
+
name: filter.param,
|
|
112
|
+
kind: 'filter',
|
|
113
|
+
column,
|
|
114
|
+
...(filter.ops.includes('in') ? { many: true } : {}),
|
|
115
|
+
}));
|
|
116
|
+
if (resource.search)
|
|
117
|
+
params.push({ name: resource.search, kind: 'search' });
|
|
118
|
+
if (resource.sort)
|
|
119
|
+
params.push({ name: resource.sort.param, kind: 'sort', values: resource.sort.values });
|
|
120
|
+
const paging = resource.paging;
|
|
121
|
+
if (paging.kind === 'offset')
|
|
122
|
+
params.push({ name: paging.offset, kind: 'offset' }, { name: paging.limit, kind: 'limit' });
|
|
123
|
+
if (paging.kind === 'page')
|
|
124
|
+
params.push({ name: paging.page, kind: 'page' }, { name: paging.size, kind: 'size' });
|
|
125
|
+
if (paging.kind === 'cursor') {
|
|
126
|
+
if (paging.limit)
|
|
127
|
+
params.push({ name: paging.limit, kind: 'limit' });
|
|
128
|
+
params.push({ name: paging.param, kind: 'cursor' });
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
resource: resource.name,
|
|
132
|
+
list: { path: resource.list, params, ...(paging.kind === 'cursor' ? { next: paging.next } : {}) },
|
|
133
|
+
item: { path: resource.item, key: resource.key },
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* One documented read as the explorer tries it, answered whole: the point is
|
|
138
|
+
* to see what the API says. The request is still built from the description
|
|
139
|
+
* alone — a parameter it does not declare, a sort it does not document, or a
|
|
140
|
+
* paging value that is not a whole number is refused by name before anything
|
|
141
|
+
* is sent — and the reply says the path and query string, never a header,
|
|
142
|
+
* which is where the credential is. A status that is not 2xx comes back as
|
|
143
|
+
* the status, its body unread; a failed call throws HttpSourceError.
|
|
144
|
+
*/
|
|
145
|
+
async operation(table, kind, params) {
|
|
146
|
+
const started = performance.now();
|
|
147
|
+
const resource = this.resource(table);
|
|
148
|
+
const query = new URLSearchParams();
|
|
149
|
+
let path;
|
|
150
|
+
if (kind === 'item') {
|
|
151
|
+
const stray = Object.keys(params).find((name) => name !== resource.key);
|
|
152
|
+
if (stray !== undefined)
|
|
153
|
+
throw new Refusal(`${resource.name}'s item read takes only its key, "${resource.key}"; not "${stray}".`);
|
|
154
|
+
const key = (params[resource.key] ?? '').trim();
|
|
155
|
+
// Encoded, like a record read, so a key cannot add a path segment; "." and ".." would step up from the item.
|
|
156
|
+
if (!key || key === '.' || key === '..')
|
|
157
|
+
throw new Refusal(`${resource.name}'s item read needs its key, "${resource.key}".`);
|
|
158
|
+
path = resource.item.replace(`{${resource.key}}`, encodeURIComponent(key));
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
const declared = new Map(this.operations(table).list.params.map((param) => [param.name, param]));
|
|
162
|
+
for (const [name, raw] of Object.entries(params)) {
|
|
163
|
+
const param = declared.get(name);
|
|
164
|
+
if (!param)
|
|
165
|
+
throw new Refusal(`${resource.name}'s list takes ${[...declared.keys()].join(', ') || 'no parameters'}; not "${name}".`);
|
|
166
|
+
const filter = Object.values(resource.filters).find((filter) => filter.param === name);
|
|
167
|
+
const value = raw.trim();
|
|
168
|
+
if (!value && filter?.serialization !== 'csv')
|
|
169
|
+
continue;
|
|
170
|
+
if (param.kind === 'sort' && !param.values.includes(value)) {
|
|
171
|
+
throw new Refusal(`${resource.name} sorts by ${param.values.join(', ')}; not "${value}".`);
|
|
172
|
+
}
|
|
173
|
+
if (['offset', 'limit', 'page', 'size'].includes(param.kind) && !/^\d+$/.test(value)) {
|
|
174
|
+
throw new Refusal(`"${name}" takes a whole number, not "${value}".`);
|
|
175
|
+
}
|
|
176
|
+
if (filter?.serialization === 'csv')
|
|
177
|
+
appendFilter(query, filter, param.many ? raw.split(',') : [raw]);
|
|
178
|
+
else if (param.many)
|
|
179
|
+
for (const each of value
|
|
180
|
+
.split(',')
|
|
181
|
+
.map((part) => part.trim())
|
|
182
|
+
.filter(Boolean))
|
|
183
|
+
query.append(name, each);
|
|
184
|
+
else
|
|
185
|
+
query.set(name, value);
|
|
186
|
+
}
|
|
187
|
+
path = resource.list;
|
|
188
|
+
}
|
|
189
|
+
const reply = await this.request(path, query);
|
|
190
|
+
const answered = reply.status >= 200 && reply.status <= 299;
|
|
191
|
+
return {
|
|
192
|
+
request: { method: 'GET', path, query: query.toString() },
|
|
193
|
+
status: reply.status,
|
|
194
|
+
...(answered ? { json: reply.json } : {}),
|
|
195
|
+
ms: Math.round(performance.now() - started),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/* A view is a list or a record read by another name: a record page, a peek,
|
|
199
|
+
a list that shows a reference's name. `compileView` still resolves its
|
|
200
|
+
paths, for the names and types every surface draws by, but its SQL goes
|
|
201
|
+
nowhere. The read is `query`, under the same declared-filter rules, and a
|
|
202
|
+
reference is followed one step by reading each target it names once, from
|
|
203
|
+
its item path: a LEFT JOIN, done by GET. What would need the targets inside
|
|
204
|
+
the API's own query — a filter or a sort on them, a second step, a summary
|
|
205
|
+
of the records pointing here — is refused. No `countView`: an API reports
|
|
206
|
+
no totals, and a view's total is then absent, as a list's is. */
|
|
207
|
+
async runView(view, offset = 0) {
|
|
208
|
+
const started = performance.now();
|
|
209
|
+
this.catalog ??= describedSchema(this.connection.description, this.connection.label);
|
|
210
|
+
const compiled = compileView(this.catalog, view, ANSI_STYLE, offset);
|
|
211
|
+
const refuse = (why) => {
|
|
212
|
+
throw new Refusal(`${view.base} comes from an API, so a view over it ${why}`);
|
|
213
|
+
};
|
|
214
|
+
if (view.aggregates?.length || view.orderBy?.some((order) => order.alias))
|
|
215
|
+
refuse('cannot summarise the records that point at it.');
|
|
216
|
+
if (view.filter?.groups.some((group) => group.some((condition) => condition.any)))
|
|
217
|
+
refuse('cannot ask about the records that point at it.');
|
|
218
|
+
const deep = compiled.columns.find((column) => column.hops > 1);
|
|
219
|
+
if (deep)
|
|
220
|
+
refuse(`follows one reference at most; "${deep.path}" follows ${deep.hops}.`);
|
|
221
|
+
const elsewhere = [...(view.filter?.groups.flat() ?? []), ...(view.orderBy ?? [])].find((each) => each.path.includes('.'));
|
|
222
|
+
if (elsewhere)
|
|
223
|
+
refuse(`filters and sorts by its own columns only; "${elsewhere.path}" is on another resource.`);
|
|
224
|
+
const via = (column) => column.path.split('.')[0];
|
|
225
|
+
const own = compiled.columns.filter((column) => column.hops === 0).map((column) => column.column);
|
|
226
|
+
const followed = [...new Set(compiled.columns.filter((column) => column.hops === 1).map(via))];
|
|
227
|
+
const result = await this.query({
|
|
228
|
+
table: view.base,
|
|
229
|
+
columns: [...new Set([...own, ...followed])],
|
|
230
|
+
...(view.filter
|
|
231
|
+
? { filter: { groups: view.filter.groups.map((group) => group.map(({ path, op, value }) => ({ column: path, op, value }))) } }
|
|
232
|
+
: {}),
|
|
233
|
+
...(view.orderBy?.length ? { orderBy: view.orderBy.map(({ path, direction }) => ({ column: path, direction })) } : {}),
|
|
234
|
+
limit: compiled.limit,
|
|
235
|
+
offset,
|
|
236
|
+
});
|
|
237
|
+
const targets = new Map();
|
|
238
|
+
const reads = [];
|
|
239
|
+
for (const column of followed) {
|
|
240
|
+
const { rows, read } = await this.lookUp(view.base, column, result.rows);
|
|
241
|
+
targets.set(column, rows);
|
|
242
|
+
if (read)
|
|
243
|
+
reads.push(read);
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
...result,
|
|
247
|
+
// Under the view's names, which an alias may have changed.
|
|
248
|
+
columns: compiled.columns.map((column) => column.name),
|
|
249
|
+
rows: result.rows.map((row) => Object.fromEntries(compiled.columns.map((column) => [
|
|
250
|
+
column.name,
|
|
251
|
+
column.hops === 0 ? row[column.column] : (targets.get(via(column))?.get(String(row[via(column)]))?.[column.column] ?? null),
|
|
252
|
+
]))),
|
|
253
|
+
// The list read and every lookup, since the page waited on all of them.
|
|
254
|
+
ms: Math.round(performance.now() - started),
|
|
255
|
+
sql: { text: [result.sql.text, ...reads].join('\n'), params: [] },
|
|
256
|
+
resolved: compiled.columns,
|
|
257
|
+
joins: compiled.joins,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
/* Each distinct target a page names, read once from its item path, a few at
|
|
261
|
+
a time. A target the API no longer has reads as no row: a LEFT JOIN's
|
|
262
|
+
answer. Bounded, because every name is a request against someone else's
|
|
263
|
+
rate budget; a page that names more is refused with how to narrow it. */
|
|
264
|
+
async lookUp(base, column, rows) {
|
|
265
|
+
const fk = this.catalog.foreignKeys.find((each) => each.from.table === base && each.from.columns[0] === column);
|
|
266
|
+
const target = this.resource(fk.to.table);
|
|
267
|
+
const values = [
|
|
268
|
+
...new Set(rows
|
|
269
|
+
.map((row) => row[column])
|
|
270
|
+
.filter((value) => value !== null && value !== undefined && value !== '')
|
|
271
|
+
.map(String)),
|
|
272
|
+
];
|
|
273
|
+
if (values.length > MAX_LOOKUPS_PER_READ) {
|
|
274
|
+
throw new Refusal(`${base} names ${values.length} different ${target.name} on this page, more than the ${MAX_LOOKUPS_PER_READ} an API is asked for at once. Show fewer rows.`);
|
|
275
|
+
}
|
|
276
|
+
const found = new Map();
|
|
277
|
+
for (let start = 0; start < values.length; start += LOOKUP_BATCH) {
|
|
278
|
+
await Promise.all(values.slice(start, start + LOOKUP_BATCH).map(async (value) => {
|
|
279
|
+
const { rows: [row], } = await this.query({ table: target.name, filter: { groups: [[{ column: target.key, op: '=', value }]] }, limit: 1, offset: 0 });
|
|
280
|
+
if (row)
|
|
281
|
+
found.set(value, row);
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
return { rows: found, ...(values.length ? { read: `GET ${target.item} for ${values.length} ${target.name}` } : {}) };
|
|
285
|
+
}
|
|
286
|
+
async query(request) {
|
|
287
|
+
const started = performance.now();
|
|
288
|
+
const resource = this.resource(request.table);
|
|
289
|
+
const columns = this.columns(resource, request.columns);
|
|
290
|
+
const conditions = this.conditions(resource, request.filter);
|
|
291
|
+
const byKey = conditions.length === 1 && conditions[0].column === resource.key && conditions[0].op === '=' ? conditions[0] : undefined;
|
|
292
|
+
if (byKey) {
|
|
293
|
+
const key = String(byKey.value);
|
|
294
|
+
/* Encoded, so a key cannot add a path segment; and never "." or "..",
|
|
295
|
+
which URL resolution would read as a step up from the item. */
|
|
296
|
+
if (!key || key === '.' || key === '..')
|
|
297
|
+
return this.result(columns, [], started, resource.item, new URLSearchParams());
|
|
298
|
+
const path = resource.item.replace(`{${resource.key}}`, encodeURIComponent(key));
|
|
299
|
+
const found = await this.get(path, new URLSearchParams(), true);
|
|
300
|
+
const rows = found === undefined ? [] : [this.row(resource, found, columns)];
|
|
301
|
+
return this.result(columns, rows, started, path, new URLSearchParams());
|
|
302
|
+
}
|
|
303
|
+
const query = new URLSearchParams();
|
|
304
|
+
for (const condition of conditions) {
|
|
305
|
+
appendFilter(query, resource.filters[condition.column], Array.isArray(condition.value) ? condition.value : [condition.value]);
|
|
306
|
+
}
|
|
307
|
+
const sort = this.sort(resource, request.orderBy);
|
|
308
|
+
if (sort !== undefined)
|
|
309
|
+
query.set(resource.sort.param, sort);
|
|
310
|
+
const { rows, sent } = await this.page(resource, query, request.limit, request.offset);
|
|
311
|
+
return this.result(columns, rows.map((row) => this.row(resource, row, columns)), started, resource.list, sent);
|
|
312
|
+
}
|
|
313
|
+
resource(table) {
|
|
314
|
+
const resource = this.connection.description.resources.find((each) => each.name === table);
|
|
315
|
+
if (!resource)
|
|
316
|
+
throw new Refusal(`No resource called "${table}" in this API's description.`);
|
|
317
|
+
return resource;
|
|
318
|
+
}
|
|
319
|
+
columns(resource, requested) {
|
|
320
|
+
if (!requested?.length)
|
|
321
|
+
return resource.columns.map((column) => column.name);
|
|
322
|
+
const unknown = requested.find((name) => !resource.columns.some((column) => column.name === name));
|
|
323
|
+
if (unknown !== undefined)
|
|
324
|
+
throw new Refusal(`No column called "${unknown}" on ${resource.name}.`);
|
|
325
|
+
return requested;
|
|
326
|
+
}
|
|
327
|
+
/** What the API can be asked, said in the refusal so the reader knows what does work. */
|
|
328
|
+
offer(resource) {
|
|
329
|
+
const filters = Object.entries(resource.filters).map(([column, filter]) => `${column} ${filter.ops.join(' or ')}`);
|
|
330
|
+
return filters.length ? `It can filter by ${filters.join(', ')}.` : 'It takes no filters.';
|
|
331
|
+
}
|
|
332
|
+
conditions(resource, filter) {
|
|
333
|
+
const groups = filter?.groups ?? [];
|
|
334
|
+
if (!groups.length)
|
|
335
|
+
return [];
|
|
336
|
+
// Each refusal names the columns it quotes, so an App can say them in its captions.
|
|
337
|
+
const filterable = Object.keys(resource.filters);
|
|
338
|
+
if (groups.length > 1)
|
|
339
|
+
throw new Refusal(`${resource.name} comes from an API that cannot answer "or". ${this.offer(resource)}`, { columns: filterable });
|
|
340
|
+
const [group] = groups;
|
|
341
|
+
if (group.length === 1 && group[0].column === resource.key && group[0].op === '=')
|
|
342
|
+
return group;
|
|
343
|
+
const seen = new Set();
|
|
344
|
+
for (const condition of group) {
|
|
345
|
+
const declared = resource.filters[condition.column];
|
|
346
|
+
const op = condition.op;
|
|
347
|
+
if (!declared || (op !== '=' && op !== 'in') || !declared.ops.includes(op)) {
|
|
348
|
+
throw new Refusal(`${resource.name}: the API cannot filter by "${condition.column} ${op}". ${this.offer(resource)}`, {
|
|
349
|
+
columns: [condition.column, ...filterable],
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (op === '=' && (Array.isArray(condition.value) || condition.value === undefined)) {
|
|
353
|
+
throw new Refusal(`${resource.name}: "${condition.column} =" needs one value.`, { columns: [condition.column] });
|
|
354
|
+
}
|
|
355
|
+
if (op === 'in' && !Array.isArray(condition.value))
|
|
356
|
+
throw new Refusal(`${resource.name}: "${condition.column} in" needs a list.`, { columns: [condition.column] });
|
|
357
|
+
// Two conditions on one parameter would reach the API as one list it may read as "either".
|
|
358
|
+
if (seen.has(condition.column))
|
|
359
|
+
throw new Refusal(`${resource.name} can take ${condition.column} once; combine the values with "in".`, {
|
|
360
|
+
columns: [condition.column],
|
|
361
|
+
});
|
|
362
|
+
seen.add(condition.column);
|
|
363
|
+
}
|
|
364
|
+
return group;
|
|
365
|
+
}
|
|
366
|
+
sort(resource, orderBy) {
|
|
367
|
+
const order = [...(orderBy ?? [])];
|
|
368
|
+
// The reader breaks ties by the key; an API answers in its own order, so that tiebreak is not something to send.
|
|
369
|
+
if (order.length > 1 && order.at(-1)?.column === resource.key && order.at(-1)?.direction === 'asc')
|
|
370
|
+
order.pop();
|
|
371
|
+
if (!order.length)
|
|
372
|
+
return undefined;
|
|
373
|
+
const choices = resource.sort ? `It sorts by ${resource.sort.values.join(', ')}.` : 'It takes no sort.';
|
|
374
|
+
if (order.length > 1 || !resource.sort)
|
|
375
|
+
throw new Refusal(`${resource.name}: the API sorts by one documented value at most. ${choices}`);
|
|
376
|
+
const { column, direction } = order[0];
|
|
377
|
+
const spellings = direction === 'asc'
|
|
378
|
+
? [column, `+${column}`, `${column}:asc`, `${column} asc`, `${column}_asc`]
|
|
379
|
+
: [`-${column}`, `${column}:desc`, `${column} desc`, `${column}_desc`];
|
|
380
|
+
const value = resource.sort.values.find((each) => spellings.includes(each));
|
|
381
|
+
// The documented values stay as the API spells them: they are parameter values, not columns.
|
|
382
|
+
if (value === undefined)
|
|
383
|
+
throw new Refusal(`${resource.name}: the API cannot sort by ${column} ${direction === 'asc' ? 'ascending' : 'descending'}. ${choices}`, { columns: [column] });
|
|
384
|
+
return value;
|
|
385
|
+
}
|
|
386
|
+
async page(resource, query, limit, offset) {
|
|
387
|
+
const paging = resource.paging;
|
|
388
|
+
switch (paging.kind) {
|
|
389
|
+
case 'none':
|
|
390
|
+
// One response is the whole list; the page is cut from it.
|
|
391
|
+
return { rows: this.rows(resource, await this.get(resource.list, query)).slice(offset, offset + limit), sent: query };
|
|
392
|
+
case 'offset': {
|
|
393
|
+
query.set(paging.offset, String(offset));
|
|
394
|
+
query.set(paging.limit, String(limit));
|
|
395
|
+
return { rows: this.rows(resource, await this.get(resource.list, query)).slice(0, limit), sent: query };
|
|
396
|
+
}
|
|
397
|
+
case 'page': {
|
|
398
|
+
if (offset % limit)
|
|
399
|
+
throw new Refusal(`${resource.name} is paged in whole pages of ${limit}; row ${offset + 1} does not start one.`);
|
|
400
|
+
query.set(paging.page, String(offset / limit + 1));
|
|
401
|
+
query.set(paging.size, String(limit));
|
|
402
|
+
return { rows: this.rows(resource, await this.get(resource.list, query)).slice(0, limit), sent: query };
|
|
403
|
+
}
|
|
404
|
+
case 'cursor': {
|
|
405
|
+
if (offset % limit)
|
|
406
|
+
throw new Refusal(`${resource.name} is paged in whole pages of ${limit}; row ${offset + 1} does not start one.`);
|
|
407
|
+
if (paging.limit)
|
|
408
|
+
query.set(paging.limit, String(limit));
|
|
409
|
+
const target = offset / limit;
|
|
410
|
+
const walkKey = `${resource.name}?${query}`;
|
|
411
|
+
const walk = this.walks.get(walkKey) ?? [''];
|
|
412
|
+
let index = Math.min(target, walk.length - 1);
|
|
413
|
+
for (let requests = 0; index < target; requests++) {
|
|
414
|
+
if (requests >= MAX_REQUESTS_PER_READ) {
|
|
415
|
+
throw new Refusal(`${resource.name} is paged by cursor, and page ${target + 1} is more than ${MAX_REQUESTS_PER_READ} requests away. Filter the list to reach it.`);
|
|
416
|
+
}
|
|
417
|
+
const next = at(await this.get(resource.list, this.withCursor(query, paging.param, walk[index])), paging.next);
|
|
418
|
+
if (typeof next !== 'string' || !next)
|
|
419
|
+
return { rows: [], sent: query };
|
|
420
|
+
walk[++index] = next;
|
|
421
|
+
}
|
|
422
|
+
this.remember(walkKey, walk);
|
|
423
|
+
const sent = this.withCursor(query, paging.param, walk[target]);
|
|
424
|
+
return { rows: this.rows(resource, await this.get(resource.list, sent)).slice(0, limit), sent };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
withCursor(query, param, cursor) {
|
|
429
|
+
const next = new URLSearchParams(query);
|
|
430
|
+
if (cursor)
|
|
431
|
+
next.set(param, cursor);
|
|
432
|
+
return next;
|
|
433
|
+
}
|
|
434
|
+
remember(key, walk) {
|
|
435
|
+
this.walks.delete(key);
|
|
436
|
+
this.walks.set(key, walk);
|
|
437
|
+
if (this.walks.size > REMEMBERED_WALKS)
|
|
438
|
+
this.walks.delete(this.walks.keys().next().value);
|
|
439
|
+
}
|
|
440
|
+
rows(resource, body) {
|
|
441
|
+
const found = at(body, resource.rows);
|
|
442
|
+
if (!Array.isArray(found) || !found.every(isObject)) {
|
|
443
|
+
throw new HttpSourceError('shape', `${resource.list} did not answer a list of rows at ${resource.rows || 'its top level'}.`);
|
|
444
|
+
}
|
|
445
|
+
return found;
|
|
446
|
+
}
|
|
447
|
+
row(resource, raw, columns) {
|
|
448
|
+
if (!isObject(raw))
|
|
449
|
+
throw new HttpSourceError('shape', `${resource.item} did not answer a row.`);
|
|
450
|
+
const out = {};
|
|
451
|
+
for (const name of columns)
|
|
452
|
+
out[name] = cell(resource.columns.find((column) => column.name === name), raw[name]);
|
|
453
|
+
return out;
|
|
454
|
+
}
|
|
455
|
+
/** One GET under the base URL. `missing` turns a 404 into "no row" — a record read's answer, never a list's. */
|
|
456
|
+
/* A read the connection opted into caching is answered from the cache while
|
|
457
|
+
its copy is fresh. Only a 2xx is kept, under the URL's path and query
|
|
458
|
+
alone: the credential is the connection's, and so is the cache's prefix.
|
|
459
|
+
A copy that no longer parses is no copy. */
|
|
460
|
+
async cachedRequest(path, query) {
|
|
461
|
+
const cache = this.connection.cache;
|
|
462
|
+
if (!cache)
|
|
463
|
+
return this.request(path, query);
|
|
464
|
+
const url = new URL(path.slice(1), this.connection.base);
|
|
465
|
+
url.search = query.toString();
|
|
466
|
+
const key = `${url.pathname}${url.search}`;
|
|
467
|
+
const kept = await cache.store.get(key);
|
|
468
|
+
if (kept !== undefined) {
|
|
469
|
+
try {
|
|
470
|
+
return { status: 200, json: JSON.parse(kept) };
|
|
471
|
+
}
|
|
472
|
+
catch {
|
|
473
|
+
/* fetch it again */
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
const reply = await this.request(path, query);
|
|
477
|
+
if (reply.status >= 200 && reply.status <= 299)
|
|
478
|
+
await cache.store.set(key, JSON.stringify(reply.json), cache.ttlMs);
|
|
479
|
+
return reply;
|
|
480
|
+
}
|
|
481
|
+
async get(path, query, missing = false) {
|
|
482
|
+
const reply = await this.cachedRequest(path, query);
|
|
483
|
+
if (reply.status === 404 && missing)
|
|
484
|
+
return undefined;
|
|
485
|
+
if (reply.status < 200 || reply.status > 299)
|
|
486
|
+
throw new HttpSourceError('status', `GET ${path} answered ${reply.status}.`, reply.status);
|
|
487
|
+
return reply.json;
|
|
488
|
+
}
|
|
489
|
+
/* The one place a request is built: under the base URL, with the credential
|
|
490
|
+
as its one header, and sent through egress. Reads and the explorer's
|
|
491
|
+
operations both come through here, so neither can send what the other
|
|
492
|
+
would not. */
|
|
493
|
+
async request(path, query) {
|
|
494
|
+
const url = new URL(path.slice(1), this.connection.base);
|
|
495
|
+
url.search = query.toString();
|
|
496
|
+
const headers = { accept: 'application/json' };
|
|
497
|
+
if (this.connection.auth)
|
|
498
|
+
headers[this.connection.auth.header] = this.connection.auth.value;
|
|
499
|
+
return send(url, headers, this.connection.policy);
|
|
500
|
+
}
|
|
501
|
+
/* What ran, in the place a database shows its SQL: the method, the path and
|
|
502
|
+
the query string — never a header, which is where the credential is. */
|
|
503
|
+
result(columns, rows, started, path, query) {
|
|
504
|
+
const search = query.toString();
|
|
505
|
+
return {
|
|
506
|
+
columns,
|
|
507
|
+
rows,
|
|
508
|
+
ms: Math.round(performance.now() - started),
|
|
509
|
+
sql: { text: `GET ${path}${search ? `?${search}` : ''}`, params: [] },
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The description an HTTP connection is browsed through.
|
|
3
|
+
*
|
|
4
|
+
* The OpenAPI import drafts it; the author confirms and corrects it; the
|
|
5
|
+
* adapter reads it — and only it — at runtime. The spec is not read again
|
|
6
|
+
* after the import, so a guess corrected here stays corrected, and a spec
|
|
7
|
+
* that changed shows up as a changed hash rather than as silently different
|
|
8
|
+
* columns. It is data, not code: plain JSON a person, a script or an agent
|
|
9
|
+
* can edit, and never part of an App's source.
|
|
10
|
+
*/
|
|
11
|
+
import type { DatabaseSchema } from '../../shared/schema.js';
|
|
12
|
+
import type { HttpCatalog, HttpResource } from './openapi.js';
|
|
13
|
+
export declare const DESCRIPTION_FORMAT = "mistri.http/1";
|
|
14
|
+
export interface HttpDescription {
|
|
15
|
+
format: typeof DESCRIPTION_FORMAT;
|
|
16
|
+
title: string;
|
|
17
|
+
version: string;
|
|
18
|
+
/** What it was drafted from: the spec's file name and sha256, so a changed spec is visible. */
|
|
19
|
+
spec?: {
|
|
20
|
+
file: string;
|
|
21
|
+
sha256: string;
|
|
22
|
+
};
|
|
23
|
+
resources: HttpResource[];
|
|
24
|
+
}
|
|
25
|
+
export declare function draftDescription(catalog: HttpCatalog, spec?: {
|
|
26
|
+
file: string;
|
|
27
|
+
sha256: string;
|
|
28
|
+
}): HttpDescription;
|
|
29
|
+
/** A description as written in a file, checked field by field; `where` names the file in every message. */
|
|
30
|
+
export declare function readDescription(raw: unknown, where: string): HttpDescription;
|
|
31
|
+
/** The description as the catalog every App, grant, type export and scaffold already reads. */
|
|
32
|
+
export declare function describedSchema(description: HttpDescription, label: string, readAt?: Date): DatabaseSchema;
|