@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,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counting things by something: GROUP BY, as a shape rather than as text.
|
|
3
|
+
*
|
|
4
|
+
* This was the last real hole in the query language. "How many invoices per
|
|
5
|
+
* month", "which countries have the most customers", "what does each status
|
|
6
|
+
* add up to" are the questions anybody asks of a database in the first ten
|
|
7
|
+
* minutes, and until now the only way to ask them here was to drop into `sql`
|
|
8
|
+
* — which means an agent leaves the checked path exactly where the question
|
|
9
|
+
* gets interesting, and a person leaves the tool.
|
|
10
|
+
*
|
|
11
|
+
* A breakdown is deliberately *not* a view with a `groupBy` bolted on. A view
|
|
12
|
+
* promises one row per base record, and every part of it — the correlated
|
|
13
|
+
* aggregates, the LEFT JOIN rule, the row-click that opens a record — leans on
|
|
14
|
+
* that promise. Grouping breaks it: the rows that come back are not records
|
|
15
|
+
* and cannot be opened, and a column that is neither a key nor an aggregate is
|
|
16
|
+
* a bug on two engines and a silent wrong answer on the third. So it is a
|
|
17
|
+
* sibling type with its own compiler, sharing the parts that are genuinely the
|
|
18
|
+
* same: path resolution, the join planner, and `buildWhere`.
|
|
19
|
+
*
|
|
20
|
+
* invoice count by status
|
|
21
|
+
* invoice sum total by month invoice_date
|
|
22
|
+
* invoice count, avg total by customer_id.country_code.name sort count desc
|
|
23
|
+
*
|
|
24
|
+
* Two rules the shape enforces, both of which SQL leaves to the author:
|
|
25
|
+
*
|
|
26
|
+
* - **Every selected expression is a key or a measure.** There is nowhere to
|
|
27
|
+
* put a third thing, so `SELECT status, name, COUNT(*) … GROUP BY status`
|
|
28
|
+
* — which a lenient engine would answer with an arbitrary `name` — cannot be
|
|
29
|
+
* written.
|
|
30
|
+
* - **The result is bounded like every other read.** A breakdown over a
|
|
31
|
+
* high-cardinality column is a million-row answer, and `sort count desc
|
|
32
|
+
* limit 20` is what somebody meant anyway.
|
|
33
|
+
*/
|
|
34
|
+
import { buildWhere } from './filter.js';
|
|
35
|
+
import { clampLimit, clampOffset, countStatement } from './reads.js';
|
|
36
|
+
import { aggregateInput, columnKind, findTable, labelPath } from './schema.js';
|
|
37
|
+
import { JoinPlan, PathError, sqlLiteral } from './view.js';
|
|
38
|
+
import { ScopedRelations } from './rowScope.js';
|
|
39
|
+
import { readsOf } from './query.js';
|
|
40
|
+
import { isFixedScopeCatalog } from './scopeCatalog.js';
|
|
41
|
+
import { viewPaths, withViewComputed } from './viewLabels.js';
|
|
42
|
+
import { tableAtPrefix, tryResolvePath } from './viewPaths.js';
|
|
43
|
+
import { withViewReferences } from './declaredReferences.js';
|
|
44
|
+
import { LABEL_SEGMENT, labelPrefix } from './recordLabel.js';
|
|
45
|
+
export const MEASURE_FUNCTIONS = ['count', 'sum', 'avg', 'min', 'max', 'median'];
|
|
46
|
+
export const MEASURE_OPS = ['+', '-', '*', '/'];
|
|
47
|
+
/** Why a dialect cannot answer a measure function, or undefined when it can. */
|
|
48
|
+
export function measureRefusal(fn, dialect) {
|
|
49
|
+
if (fn !== 'median')
|
|
50
|
+
return undefined;
|
|
51
|
+
if (dialect === 'sqlite')
|
|
52
|
+
return '`median` is not available on SQLite: it has no median aggregate (no percentile_cont). Use `avg`, or a PostgreSQL source.';
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** The periods a date key can be cut into. */
|
|
56
|
+
export const DATE_UNITS = ['day', 'week', 'month', 'quarter', 'year', 'weekday', 'hour'];
|
|
57
|
+
const termText = (t) => typeof t === 'number'
|
|
58
|
+
? String(t)
|
|
59
|
+
: [t.fn, ...(t.distinct ? ['distinct'] : []), ...(t.path ? [t.path] : []), ...(t.when ? ['when', `[${t.when}]`] : [])].join(' ');
|
|
60
|
+
/** A measure as it is written: `sum paid / sum total as rate`. */
|
|
61
|
+
export function measureToText(m) {
|
|
62
|
+
return [termText(m), ...(m.steps ?? []).flatMap(({ op, term }) => [op, termText(term)]), ...(m.alias ? ['as', m.alias] : [])].join(' ');
|
|
63
|
+
}
|
|
64
|
+
const termName = (t, path = typeof t === 'number' ? undefined : t.path) => typeof t === 'number' ? String(t) : t.fn === 'count' ? (path ? `count(${path})` : 'count') : `${t.fn}(${path ?? ''})`;
|
|
65
|
+
/** The name a measure answers under when it has no alias: `sum(total)`, `sum(paid) / sum(total)`. */
|
|
66
|
+
export function measureName(m, path = m.path) {
|
|
67
|
+
return m.alias?.trim() || [termName(m, path), ...(m.steps ?? []).flatMap(({ op, term }) => [op, termName(term)])].join(' ');
|
|
68
|
+
}
|
|
69
|
+
/** Every column a measure reads. */
|
|
70
|
+
export function measurePaths(m) {
|
|
71
|
+
return [m, ...(m.steps ?? []).map(({ term }) => term)].flatMap((t) => (typeof t !== 'number' && t.path ? [t.path] : []));
|
|
72
|
+
}
|
|
73
|
+
/** Every `when` among a breakdown's measures, in order: what a reader's grants and the stitched-read check must also hold. */
|
|
74
|
+
export const measureConditions = (measures) => (measures ?? []).flatMap((m) => (m.where?.groups.length ? [m.where] : []));
|
|
75
|
+
/** The paths a filter's conditions walk, and the paths they compare against. */
|
|
76
|
+
export const filterPaths = (filter) => (filter?.groups ?? []).flatMap((group) => group.flatMap((c) => [c.path, ...(c.other === undefined ? [] : [c.other])]));
|
|
77
|
+
/** How many group keys are worth having. */
|
|
78
|
+
const MAX_KEYS = 3;
|
|
79
|
+
/**
|
|
80
|
+
* Is this column something a date bucket can be taken of?
|
|
81
|
+
*
|
|
82
|
+
* Read off the declared type rather than the values, because the answer has
|
|
83
|
+
* to be the same before and after the table has any rows in it. SQLite's
|
|
84
|
+
* `DATE` is a convention rather than a type, which is exactly why the check is
|
|
85
|
+
* on the declaration: a column somebody called `DATE` is one they intend to
|
|
86
|
+
* hold dates, and `strftime` over text that is not one returns null — a group
|
|
87
|
+
* of its own rather than an error, which is the honest outcome.
|
|
88
|
+
*/
|
|
89
|
+
const isDateish = (type) => /date|time/i.test(type ?? '');
|
|
90
|
+
/**
|
|
91
|
+
* Compile a breakdown to one parameterised SELECT.
|
|
92
|
+
*
|
|
93
|
+
* The order of work matches `compileView`'s and for the same reason: keys,
|
|
94
|
+
* then measures, then the filter, then the sort — so aliases are handed out in
|
|
95
|
+
* first-use order and the same breakdown always compiles to the same text.
|
|
96
|
+
*/
|
|
97
|
+
export function compileBreakdown(schema, def, style, offset = 0, scope) {
|
|
98
|
+
/* A breakdown compiles to the same statement against the same catalog, and an App's enter asks for each of its
|
|
99
|
+
rail's and dashboard's breakdowns several times — against the reader's pruned catalog and the full one for
|
|
100
|
+
the access check (columnGrants.ts), under the reader's scope to run it, and again for the SQL pane — each a
|
|
101
|
+
full compile (a field report: a large tenant App spent a twelfth of an enter's CPU here). Kept only against a catalog nothing can change (fixedCatalog, below), for a definition
|
|
102
|
+
whose text says all of it (breakdownText), and only where compiling read neither the clock nor the reader
|
|
103
|
+
(query.ts readsOf): a statement that wrote a request's instant, day or email is that request's, and is never
|
|
104
|
+
handed to another. */
|
|
105
|
+
const text = typeof offset === 'number' && fixedCatalog(schema) ? breakdownText(def) : undefined;
|
|
106
|
+
if (text === undefined) {
|
|
107
|
+
breakdownsCompiled++;
|
|
108
|
+
return compileBreakdownFresh(schema, def, style, offset, scope);
|
|
109
|
+
}
|
|
110
|
+
let byStyle = breakdownsKept.get(schema);
|
|
111
|
+
if (!byStyle)
|
|
112
|
+
breakdownsKept.set(schema, (byStyle = new WeakMap()));
|
|
113
|
+
let kept = byStyle.get(style);
|
|
114
|
+
if (!kept)
|
|
115
|
+
byStyle.set(style, (kept = new Map()));
|
|
116
|
+
const key = `${scope ? scopeOrdinal(scope) : 0}\0${offset}\0${text}`;
|
|
117
|
+
let found = kept.get(key);
|
|
118
|
+
if (found) {
|
|
119
|
+
kept.delete(key);
|
|
120
|
+
kept.set(key, found);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
breakdownsCompiled++;
|
|
124
|
+
const read = readsOf(() => compileBreakdownFresh(schema, def, style, offset, scope));
|
|
125
|
+
if (read.clock !== 0 || read.reader)
|
|
126
|
+
return read.value;
|
|
127
|
+
found = read.value;
|
|
128
|
+
kept.set(key, found);
|
|
129
|
+
if (kept.size > BREAKDOWNS_KEPT)
|
|
130
|
+
kept.delete(kept.keys().next().value);
|
|
131
|
+
}
|
|
132
|
+
// Each caller's own arrays: a driver or a caller may add to what it is handed.
|
|
133
|
+
return { ...found, params: [...found.params], columns: found.columns.map((column) => ({ ...column })) };
|
|
134
|
+
}
|
|
135
|
+
/* A catalog nothing can change: a captured scope catalog (scopeCatalog.ts), or whatever the server says it froze
|
|
136
|
+
all the way down (evidence.ts captureTenantData, which registers itself here: a shared module cannot import it). */
|
|
137
|
+
let alsoFixed = () => false;
|
|
138
|
+
/** Who says a catalog cannot change, beside the scope catalog's own captures. */
|
|
139
|
+
export function keepBreakdownsAgainst(fixed) {
|
|
140
|
+
alsoFixed = fixed;
|
|
141
|
+
}
|
|
142
|
+
const fixedCatalog = (schema) => isFixedScopeCatalog(schema) || alsoFixed(schema);
|
|
143
|
+
/* A definition's text, where the text says all of it: plain records, arrays, strings, booleans and finite numbers.
|
|
144
|
+
A property left undefined (a key with no `bucket`, a list with no `orderBy` — most of them, as a reader's snapshot
|
|
145
|
+
carries them) is said as one, never dropped, so the text of a definition that has the key and one that does not
|
|
146
|
+
still differ; anything else JSON would say alike — an undefined in a list, a non-finite number, an object that is
|
|
147
|
+
not a plain record, a function, a symbol or a BigInt — is no text, and is compiled afresh. */
|
|
148
|
+
const UNDEFINED = Object.freeze({ '\u0000': 'undefined' });
|
|
149
|
+
function breakdownText(def) {
|
|
150
|
+
let plain = true;
|
|
151
|
+
const text = JSON.stringify(def, function (key, item) {
|
|
152
|
+
// The value as held, not as its toJSON says it: a Date is said as text, and is not the text it says.
|
|
153
|
+
const held = this[key];
|
|
154
|
+
if (held === undefined) {
|
|
155
|
+
if (Array.isArray(this))
|
|
156
|
+
plain = false;
|
|
157
|
+
return UNDEFINED;
|
|
158
|
+
}
|
|
159
|
+
// JSON drops a function or symbol (two definitions apart only by one would share a text) and throws on a BigInt.
|
|
160
|
+
if (typeof held === 'function' || typeof held === 'symbol' || typeof held === 'bigint') {
|
|
161
|
+
plain = false;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
if ((typeof held === 'number' && !Number.isFinite(held)) ||
|
|
165
|
+
(held !== null && typeof held === 'object' && !Array.isArray(held) && ![null, Object.prototype].includes(Object.getPrototypeOf(held))))
|
|
166
|
+
plain = false;
|
|
167
|
+
return item;
|
|
168
|
+
});
|
|
169
|
+
return plain ? text : undefined;
|
|
170
|
+
}
|
|
171
|
+
const breakdownsKept = new WeakMap();
|
|
172
|
+
const BREAKDOWNS_KEPT = 256;
|
|
173
|
+
let breakdownsCompiled = 0;
|
|
174
|
+
/** For tests: how many breakdowns were compiled rather than found. */
|
|
175
|
+
export function breakdownsCompiledSoFar() {
|
|
176
|
+
return breakdownsCompiled;
|
|
177
|
+
}
|
|
178
|
+
const scopeOrdinals = new WeakMap();
|
|
179
|
+
let scopesSeen = 0;
|
|
180
|
+
const scopeOrdinal = (scope) => {
|
|
181
|
+
let ordinal = scopeOrdinals.get(scope);
|
|
182
|
+
if (ordinal === undefined)
|
|
183
|
+
scopeOrdinals.set(scope, (ordinal = ++scopesSeen));
|
|
184
|
+
return ordinal;
|
|
185
|
+
};
|
|
186
|
+
function compileBreakdownFresh(schema, def, style, offset, scope) {
|
|
187
|
+
// The App's soft keys, where this catalog is the database's own (declaredReferences.ts).
|
|
188
|
+
schema = withViewReferences(schema, def.references);
|
|
189
|
+
const base = findTable(schema, def.base);
|
|
190
|
+
if (!base) {
|
|
191
|
+
throw new PathError(`No table called "${def.base}" in this database, so there is nothing to break down.`, def.base, def.base, 0);
|
|
192
|
+
}
|
|
193
|
+
/* No `by` is one group: the whole answer as a single row —
|
|
194
|
+
"invoice sum total" is a number, not a mistake. The app home's metric
|
|
195
|
+
cards ride on exactly this, and it reads the same in the workbench. */
|
|
196
|
+
if (def.by.length > MAX_KEYS) {
|
|
197
|
+
/* A cap rather than a warning. Three keys is already a table nobody reads
|
|
198
|
+
across; the fourth is how a breakdown becomes the base table again, one
|
|
199
|
+
row per record, with the count column always saying 1. */
|
|
200
|
+
throw new PathError(`A breakdown groups by at most ${MAX_KEYS} things; this one names ${def.by.length}.`, def.by[MAX_KEYS].path, def.by[MAX_KEYS].path, 0);
|
|
201
|
+
}
|
|
202
|
+
if (!def.measures?.length) {
|
|
203
|
+
throw new PathError('A breakdown needs something to measure — `count`, or `sum total`.', '', '', 0);
|
|
204
|
+
}
|
|
205
|
+
const relations = new ScopedRelations(schema, style, scope);
|
|
206
|
+
const baseRelation = relations.table(base.id);
|
|
207
|
+
const plan = new JoinPlan(schema, base, style, relations.table);
|
|
208
|
+
/* The view compiler's paths, so a rollup — the base's or through a
|
|
209
|
+
reference — is the same correlated subquery here as in a list, its
|
|
210
|
+
child rows read through this statement's relations (a row scope's
|
|
211
|
+
CTE). A key, a measure and a condition may each name one. */
|
|
212
|
+
const paths = viewPaths(schema, {
|
|
213
|
+
id: '',
|
|
214
|
+
name: '',
|
|
215
|
+
base: def.base,
|
|
216
|
+
...(def.computed ? { computed: def.computed } : {}),
|
|
217
|
+
...(def.expressions ? { expressions: def.expressions } : {}),
|
|
218
|
+
...(def.tagged ? { tagged: def.tagged } : {}),
|
|
219
|
+
...(def.labels ? { labels: def.labels } : {}),
|
|
220
|
+
}, plan, style, relations);
|
|
221
|
+
const qualify = (path) => paths.qualify(path);
|
|
222
|
+
const membership = paths.membership;
|
|
223
|
+
const exists = paths.exists;
|
|
224
|
+
const seen = new Map();
|
|
225
|
+
/** Output names are unique or the result set loses a column silently. */
|
|
226
|
+
const nameFor = (wanted) => {
|
|
227
|
+
const count = seen.get(wanted) ?? 0;
|
|
228
|
+
seen.set(wanted, count + 1);
|
|
229
|
+
return count ? `${wanted}_${count + 1}` : wanted;
|
|
230
|
+
};
|
|
231
|
+
const columns = [];
|
|
232
|
+
const select = [];
|
|
233
|
+
const grouping = [];
|
|
234
|
+
for (const key of def.by ?? []) {
|
|
235
|
+
const resolved = paths.resolve(key.path);
|
|
236
|
+
let sql = qualify(key.path);
|
|
237
|
+
if (key.bucket) {
|
|
238
|
+
// A request's word, looked up in the dialect's own table: one of the four, never anything else.
|
|
239
|
+
if (!DATE_UNITS.includes(key.bucket)) {
|
|
240
|
+
throw new PathError(`"${String(key.bucket).slice(0, 40)}" is not a period. Use one of: ${DATE_UNITS.join(', ')}.`, key.path, key.path, 0);
|
|
241
|
+
}
|
|
242
|
+
if (!isDateish(resolved.column.type) && !def.dateColumns?.includes(`${resolved.table.id}.${resolved.column.name}`)) {
|
|
243
|
+
throw new PathError(`"${key.path}" is ${resolved.column.type || 'untyped'}, so it cannot be grouped by ${key.bucket}. ` +
|
|
244
|
+
'Group by it directly, or pick a date column.', key.path, key.path, resolved.hops.length);
|
|
245
|
+
}
|
|
246
|
+
sql = style.dateBucket(sql, key.bucket, def.weekStart);
|
|
247
|
+
}
|
|
248
|
+
const name = nameFor(key.alias?.trim() || (key.bucket ? `${key.bucket}(${resolved.path})` : resolved.path));
|
|
249
|
+
select.push(`${sql} AS ${style.quote(name)}`);
|
|
250
|
+
/* Grouped by the *expression*, not by the output name. Postgres
|
|
251
|
+
would take the alias; SQLite would too — but a bucketed key aliased to
|
|
252
|
+
`month(invoice_date)` and grouped by that name is one identifier away
|
|
253
|
+
from grouping by a column that happens to share it. The expression is
|
|
254
|
+
never ambiguous.
|
|
255
|
+
|
|
256
|
+
A rollup is the exception: its expression is a correlated subquery,
|
|
257
|
+
and an engine can fail to see two copies of one as
|
|
258
|
+
the same thing, so it refuses the SELECT list's copy as ungrouped.
|
|
259
|
+
Grouped by its position in the SELECT list instead — the one place it
|
|
260
|
+
is written, which every engine groups by and nothing can shadow. */
|
|
261
|
+
grouping.push(def.computed && Object.hasOwn(def.computed, key.path) ? String(select.length) : sql);
|
|
262
|
+
columns.push({
|
|
263
|
+
name,
|
|
264
|
+
kind: 'key',
|
|
265
|
+
path: resolved.path,
|
|
266
|
+
/* A bucket makes text out of whatever it was, and the interface should
|
|
267
|
+
render `2026-08` as the label it is rather than as a date it is not. */
|
|
268
|
+
type: key.bucket ? 'text' : resolved.column.type,
|
|
269
|
+
bucket: key.bucket,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
/* One aggregate's SQL, and what its values are. */
|
|
273
|
+
const termSql = (term) => {
|
|
274
|
+
/* The function is written into the statement as a word, so it is one of
|
|
275
|
+
the words: a structured def arrives from a request (`/api/breakdown`
|
|
276
|
+
`def`), and anything else there would be SQL of the caller's own,
|
|
277
|
+
outside every scope and grant this statement reads through. */
|
|
278
|
+
if (typeof term !== 'object' || term === null || !MEASURE_FUNCTIONS.includes(term.fn)) {
|
|
279
|
+
throw new PathError(`"${String(term?.fn).slice(0, 40)}" is not a measure. Use one of: ${MEASURE_FUNCTIONS.join(', ')}.`, '', '', 0);
|
|
280
|
+
}
|
|
281
|
+
if (term.fn === 'count') {
|
|
282
|
+
if (term.distinct && !term.path) {
|
|
283
|
+
throw new PathError('`count distinct` needs a column to count distinct values of.', '', '', 0);
|
|
284
|
+
}
|
|
285
|
+
/* COUNT(*) rather than COUNT(column) when no column is named: a column
|
|
286
|
+
count skips nulls, which turns "how many invoices" into "how many
|
|
287
|
+
invoices have a total" without saying so. Naming a column is how you
|
|
288
|
+
ask the other question, and then the skipping is the point. */
|
|
289
|
+
return {
|
|
290
|
+
sql: term.path ? `COUNT(${term.distinct ? 'DISTINCT ' : ''}${qualify(term.path)})` : 'COUNT(*)',
|
|
291
|
+
path: term.path,
|
|
292
|
+
type: 'integer',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
if (!term.path) {
|
|
296
|
+
throw new PathError(`\`${term.fn}\` needs a column: \`${term.fn} total\`.`, '', '', 0);
|
|
297
|
+
}
|
|
298
|
+
if (term.distinct) {
|
|
299
|
+
throw new PathError(`\`distinct\` only means something with \`count\`, not with \`${term.fn}\`.`, term.path, term.path, 0);
|
|
300
|
+
}
|
|
301
|
+
const refused = measureRefusal(term.fn, style.dialect);
|
|
302
|
+
if (refused)
|
|
303
|
+
throw new PathError(refused, term.path, term.path, 0);
|
|
304
|
+
const resolved = paths.resolve(term.path);
|
|
305
|
+
const read = aggregateInput(qualify(term.path), resolved.column.type, style.dialect);
|
|
306
|
+
return {
|
|
307
|
+
sql: term.fn === 'median' ? `percentile_cont(0.5) WITHIN GROUP (ORDER BY ${read})` : `${term.fn.toUpperCase()}(${read})`,
|
|
308
|
+
path: resolved.path,
|
|
309
|
+
/* `avg` and `median` are fractions of whatever they read — an average
|
|
310
|
+
of integers is not an integer, and saying otherwise makes the
|
|
311
|
+
interface round a number the database did not. */
|
|
312
|
+
type: term.fn === 'avg' || term.fn === 'median'
|
|
313
|
+
? 'numeric'
|
|
314
|
+
: term.fn === 'sum' && columnKind(resolved.column.type) === 'boolean'
|
|
315
|
+
? 'integer'
|
|
316
|
+
: resolved.column.type,
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
/* A measure that counts only some rows (`count when [...]`) is written once the statement's own parameters are
|
|
320
|
+
counted, below: its values are bound after the scope's and before the filter's, in the order the text reads. */
|
|
321
|
+
const whens = [];
|
|
322
|
+
const asFilter = (filter) => filter?.groups.length
|
|
323
|
+
? {
|
|
324
|
+
groups: filter.groups.map((group) => group.map((c) => c.any
|
|
325
|
+
? { column: c.path, op: c.op, any: c.any }
|
|
326
|
+
: c.other === undefined
|
|
327
|
+
? { column: c.path, op: c.op, value: c.value }
|
|
328
|
+
: { column: c.path, op: c.op, other: c.other })),
|
|
329
|
+
}
|
|
330
|
+
: undefined;
|
|
331
|
+
/* Every path a condition names is planned (its joins, its tags) before the scope prefix is frozen. */
|
|
332
|
+
const planPaths = (conditions) => {
|
|
333
|
+
for (const group of conditions?.groups ?? []) {
|
|
334
|
+
for (const condition of group) {
|
|
335
|
+
/* A tags condition (`has`) plans its link and tag relations here too, before the scope
|
|
336
|
+
prefix is frozen, as a view's does: its EXISTS reads them through it. */
|
|
337
|
+
if (condition.any) {
|
|
338
|
+
exists(condition.any);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
qualify(condition.column);
|
|
342
|
+
if (condition.other !== undefined)
|
|
343
|
+
qualify(condition.other);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
for (const measure of def.measures) {
|
|
348
|
+
if (measure.where !== undefined || measure.when !== undefined) {
|
|
349
|
+
if (measure.fn !== 'count' || measure.path || measure.distinct || measure.steps?.length || !measure.where) {
|
|
350
|
+
throw new PathError('`when` only goes with a bare `count`: `count when [status = open]`.', '', '', 0);
|
|
351
|
+
}
|
|
352
|
+
const conditions = asFilter(measure.where);
|
|
353
|
+
if (conditions) {
|
|
354
|
+
planPaths(conditions);
|
|
355
|
+
const name = nameFor(measureName(measure, undefined));
|
|
356
|
+
whens.push({ at: select.length, name, where: measure.where });
|
|
357
|
+
select.push('');
|
|
358
|
+
columns.push({ name, kind: 'measure', fn: 'count', type: 'integer' });
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const head = termSql(measure);
|
|
363
|
+
if (!measure.steps?.length) {
|
|
364
|
+
const name = nameFor(measureName(measure, head.path));
|
|
365
|
+
select.push(`${head.sql} AS ${style.quote(name)}`);
|
|
366
|
+
columns.push({ name, kind: 'measure', fn: measure.fn, path: head.path, type: head.type });
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
/* A computed measure is written out in its own order: SQL's precedence
|
|
370
|
+
is the grammar's. A divisor is made a decimal, so integer counts do
|
|
371
|
+
not divide to zero on PostgreSQL and SQLite, and NULLIF makes a zero
|
|
372
|
+
one an empty answer instead of an error. */
|
|
373
|
+
const pieces = [head.sql];
|
|
374
|
+
for (const { op, term } of measure.steps) {
|
|
375
|
+
if (!MEASURE_OPS.includes(op))
|
|
376
|
+
throw new PathError(`"${op}" is not arithmetic a measure joins with: + - * or /.`, '', '', 0);
|
|
377
|
+
if (typeof term === 'number' && !Number.isFinite(term))
|
|
378
|
+
throw new PathError("A computed measure's number must be finite.", '', '', 0);
|
|
379
|
+
const sql = typeof term === 'number' ? String(term) : termSql(term).sql;
|
|
380
|
+
pieces.push(op, op === '/' ? `NULLIF(1.0 * ${sql}, 0)` : sql);
|
|
381
|
+
}
|
|
382
|
+
const name = nameFor(measureName(measure, head.path));
|
|
383
|
+
select.push(`(${pieces.join(' ')}) AS ${style.quote(name)}`);
|
|
384
|
+
columns.push({ name, kind: 'measure', type: 'numeric' });
|
|
385
|
+
}
|
|
386
|
+
/* A key that is a reference is said by its record's name, as a grid's cell
|
|
387
|
+
is: the name through the same join the key's path walks, read through
|
|
388
|
+
this statement's relations, so a record this reader's scope does not
|
|
389
|
+
admit has no name and the group stays its key. Last, after the
|
|
390
|
+
measures, so a reader that takes the key and the measure by position
|
|
391
|
+
reads what it always did; grouped by position, since the key decides it. */
|
|
392
|
+
for (const [index, key] of (def.by ?? []).entries()) {
|
|
393
|
+
if (key.bucket || !def.names || !Object.hasOwn(def.names, key.path))
|
|
394
|
+
continue;
|
|
395
|
+
const path = def.names[key.path];
|
|
396
|
+
const name = nameFor(`${columns[index].name}.${LABEL_SEGMENT}`);
|
|
397
|
+
select.push(`${qualify(path)} AS ${style.quote(name)}`);
|
|
398
|
+
grouping.push(String(select.length));
|
|
399
|
+
columns.push({ name, kind: 'label', of: columns[index].name, path, type: 'text' });
|
|
400
|
+
}
|
|
401
|
+
const conditions = asFilter(def.filter);
|
|
402
|
+
planPaths(conditions);
|
|
403
|
+
const joins = plan.clauses();
|
|
404
|
+
const prefix = relations.finish();
|
|
405
|
+
const whenParams = [];
|
|
406
|
+
for (const { at, name, where: held } of whens) {
|
|
407
|
+
const built = buildWhere(asFilter(held), style, prefix.params.length + whenParams.length + 1, qualify, qualify, membership, exists);
|
|
408
|
+
whenParams.push(...built.params);
|
|
409
|
+
/* COUNT, not SUM: a count of no rows is 0, where a sum of none is null. */
|
|
410
|
+
select[at] = `COUNT(CASE WHEN ${built.text.replace(/^ WHERE /, '')} THEN 1 END) AS ${style.quote(name)}`;
|
|
411
|
+
}
|
|
412
|
+
const where = buildWhere(conditions, style, prefix.params.length + whenParams.length + 1, qualify, qualify, membership, exists);
|
|
413
|
+
/* Sorted by output name, and only by output name.
|
|
414
|
+
|
|
415
|
+
Every column of a breakdown is either a key or a measure, and both are in
|
|
416
|
+
the SELECT list under a name — so there is nothing else it could sort by,
|
|
417
|
+
and ORDER BY runs late enough to see the aliases on all three engines.
|
|
418
|
+
Sorting by an expression instead would let `sort total desc` mean the
|
|
419
|
+
ungrouped column on a statement where that column no longer exists. */
|
|
420
|
+
const names = new Set(columns.map((c) => c.name));
|
|
421
|
+
const order = (def.orderBy ?? []).map((o) => {
|
|
422
|
+
if (!names.has(o.name)) {
|
|
423
|
+
throw new PathError(`Nothing called "${o.name}" is in this breakdown. It shows: ${[...names].join(', ')}.`, o.name, o.name, 0);
|
|
424
|
+
}
|
|
425
|
+
if (o.emptyLast)
|
|
426
|
+
return `(${style.quote(o.name)} IS NULL) ASC`;
|
|
427
|
+
return `${style.quote(o.name)} ${o.direction === 'desc' ? 'DESC' : 'ASC'}`;
|
|
428
|
+
});
|
|
429
|
+
const limit = clampLimit(def.limit);
|
|
430
|
+
const skip = clampOffset(offset);
|
|
431
|
+
const n = prefix.params.length + whenParams.length + where.params.length;
|
|
432
|
+
const lines = [`SELECT ${select.join(',\n ')}`, ` FROM ${baseRelation} ${style.quote(plan.baseAlias)}`, ...joins];
|
|
433
|
+
if (where.text)
|
|
434
|
+
lines.push(where.text);
|
|
435
|
+
if (grouping.length)
|
|
436
|
+
lines.push(` GROUP BY ${grouping.join(', ')}`);
|
|
437
|
+
if (order.length)
|
|
438
|
+
lines.push(` ORDER BY ${order.join(', ')}`);
|
|
439
|
+
lines.push(` LIMIT ${style.placeholder(n + 1)} OFFSET ${style.placeholder(n + 2)}`);
|
|
440
|
+
return { text: prefix.text + lines.join('\n'), params: [...prefix.params, ...whenParams, ...where.params, limit, skip], columns, limit };
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* The breakdown with the rollups its paths name, read from a catalog that
|
|
444
|
+
* serves them (an App's) — and with any it arrived with dropped, as
|
|
445
|
+
* withViewComputed does for a view: which rows a number sums is the
|
|
446
|
+
* catalog's to say, never a request's.
|
|
447
|
+
*/
|
|
448
|
+
export function withBreakdownComputed(schema, def) {
|
|
449
|
+
// A request's declared dates and names are dropped with its rollups: all are the catalog's to carry (appResources.ts).
|
|
450
|
+
const { computed: _given, expressions: _written, tagged: _tagged, dateColumns: _dated, names: _names, labels: _labels, ...rest } = def;
|
|
451
|
+
/* A key that is a reference to a record with a name: its groups are said
|
|
452
|
+
by that name (compileBreakdown), the one a grid's cell reads (labelPath),
|
|
453
|
+
through the same reference. */
|
|
454
|
+
const names = {};
|
|
455
|
+
const labels = {};
|
|
456
|
+
for (const key of def.by ?? []) {
|
|
457
|
+
if (typeof key?.path !== 'string' || key.bucket || Object.hasOwn(names, key.path))
|
|
458
|
+
continue;
|
|
459
|
+
const target = tableAtPrefix(schema, def.base, key.path);
|
|
460
|
+
const resolved = target && tryResolvePath(schema, def.base, key.path).resolved;
|
|
461
|
+
if (!target || !resolved || resolved.rollup)
|
|
462
|
+
continue;
|
|
463
|
+
const naming = labelPath(schema, target);
|
|
464
|
+
if (!naming)
|
|
465
|
+
continue;
|
|
466
|
+
names[key.path] = `${key.path}.${naming}`;
|
|
467
|
+
const prefix = labelPrefix(names[key.path]);
|
|
468
|
+
if (prefix !== undefined && target.labelTemplate)
|
|
469
|
+
labels[prefix] = target.labelTemplate;
|
|
470
|
+
}
|
|
471
|
+
const named = Object.keys(names).length ? { ...rest, names, ...(Object.keys(labels).length ? { labels } : {}) } : rest;
|
|
472
|
+
const paths = [
|
|
473
|
+
...(def.by ?? []).map((key) => key.path),
|
|
474
|
+
...(def.measures ?? []).flatMap(measurePaths),
|
|
475
|
+
...filterPaths(def.filter),
|
|
476
|
+
...measureConditions(def.measures).flatMap(filterPaths),
|
|
477
|
+
];
|
|
478
|
+
/* The rollups and computed columns its paths name, as a view's are (withViewComputed). */
|
|
479
|
+
const pinned = withViewComputed(schema, {
|
|
480
|
+
id: '',
|
|
481
|
+
name: '',
|
|
482
|
+
base: def.base,
|
|
483
|
+
columns: paths.filter((path) => typeof path === 'string').map((path) => ({ path })),
|
|
484
|
+
});
|
|
485
|
+
return {
|
|
486
|
+
...named,
|
|
487
|
+
...(pinned.computed ? { computed: pinned.computed } : {}),
|
|
488
|
+
...(pinned.expressions ? { expressions: pinned.expressions } : {}),
|
|
489
|
+
...(pinned.tagged ? { tagged: pinned.tagged } : {}),
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* How many groups there are, ignoring the page.
|
|
494
|
+
*
|
|
495
|
+
* `SELECT COUNT(*) FROM (the grouped statement)` — the only honest way to
|
|
496
|
+
* count groups, because the number is a property of the grouping rather than
|
|
497
|
+
* of the base table. Without the limit and offset, which is the whole point of
|
|
498
|
+
* asking.
|
|
499
|
+
*/
|
|
500
|
+
export function compileBreakdownCount(schema, def, style, scope, limit) {
|
|
501
|
+
const inner = compileBreakdown(schema, def, style, 0, scope);
|
|
502
|
+
/* The LIMIT/OFFSET line is the last one and carries the last two
|
|
503
|
+
parameters. Dropped by rebuilding rather than by string surgery would be
|
|
504
|
+
nicer; it is the last line by construction, and the test says so. */
|
|
505
|
+
const lines = inner.text.split('\n');
|
|
506
|
+
const body = lines.slice(0, -1).join('\n');
|
|
507
|
+
return {
|
|
508
|
+
/* With a bound, at most `limit` groups are counted (countStatement). The
|
|
509
|
+
grouping itself still reads what it groups; the bound keeps the count's
|
|
510
|
+
answer, not the scan, from growing with the data. */
|
|
511
|
+
text: limit === undefined ? `SELECT COUNT(*) AS n FROM (\n${body}\n) AS grouped` : countStatement(`(\n${body}\n) AS grouped`, limit),
|
|
512
|
+
params: inner.params.slice(0, -2),
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Render a breakdown as the SQL it will become, values inline.
|
|
517
|
+
*
|
|
518
|
+
* The exact counterpart of `explainView`, and here for the same reason: the
|
|
519
|
+
* SQL pane is how somebody learns what a line of the language did on their
|
|
520
|
+
* behalf, and a statement full of `?` teaches nothing about the filter that
|
|
521
|
+
* produced it. It also has to be a statement that runs, because the pane
|
|
522
|
+
* offers to open it in the SQL editor.
|
|
523
|
+
*/
|
|
524
|
+
export function explainBreakdown(schema, def, style, offset = 0) {
|
|
525
|
+
const compiled = compileBreakdown(schema, def, style, offset);
|
|
526
|
+
let i = 0;
|
|
527
|
+
return compiled.text.replace(/\$\d+|\?/g, () => sqlLiteral(compiled.params[i++]));
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* The answer as its readers take it — the keys first and the measures after,
|
|
531
|
+
* by position — with each reference key's names (compileBreakdown's `label`
|
|
532
|
+
* columns) lifted out beside it: a chart reads its key and measure by index,
|
|
533
|
+
* a board its count last, and a name among them would be read as either.
|
|
534
|
+
*/
|
|
535
|
+
export function withGroupNames(result) {
|
|
536
|
+
const labels = result.resolved.filter((column) => column.kind === 'label' && column.of);
|
|
537
|
+
if (!labels.length)
|
|
538
|
+
return result;
|
|
539
|
+
const names = {};
|
|
540
|
+
for (const label of labels) {
|
|
541
|
+
const said = {};
|
|
542
|
+
for (const row of result.rows) {
|
|
543
|
+
const key = row[label.of], name = row[label.name];
|
|
544
|
+
if (key !== null && key !== undefined && typeof name === 'string' && name !== '')
|
|
545
|
+
said[String(key)] = name;
|
|
546
|
+
}
|
|
547
|
+
names[label.of] = said;
|
|
548
|
+
}
|
|
549
|
+
const hidden = new Set(labels.map((label) => label.name));
|
|
550
|
+
return {
|
|
551
|
+
...result,
|
|
552
|
+
columns: result.columns.filter((name) => !hidden.has(name)),
|
|
553
|
+
resolved: result.resolved.filter((column) => !hidden.has(column.name)),
|
|
554
|
+
rows: result.rows.map((row) => Object.fromEntries(Object.entries(row).filter(([name]) => !hidden.has(name)))),
|
|
555
|
+
names,
|
|
556
|
+
};
|
|
557
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type DatabaseSchema } from './schema.js';
|
|
2
|
+
import type { QueryError, QueryPerson } from './query.js';
|
|
3
|
+
import { type BreakdownDef } from './breakdown.js';
|
|
4
|
+
interface ParsedBreakdownText {
|
|
5
|
+
breakdown?: BreakdownDef;
|
|
6
|
+
/** The conditions half of the line, exactly as typed — everything
|
|
7
|
+
before the measures. The section emitter's whole reason to exist
|
|
8
|
+
here: only the parser knows where that boundary fell. */
|
|
9
|
+
conditions?: string;
|
|
10
|
+
errors: QueryError[];
|
|
11
|
+
}
|
|
12
|
+
/** Whether a line is asking for a breakdown at all. */
|
|
13
|
+
export declare function looksLikeBreakdown(text: string): boolean;
|
|
14
|
+
export declare function textToBreakdown(schema: DatabaseSchema, text: string, options?: {
|
|
15
|
+
now?: Date;
|
|
16
|
+
me?: QueryPerson | null;
|
|
17
|
+
}): ParsedBreakdownText;
|
|
18
|
+
/**
|
|
19
|
+
* The line as an authored dashboard section, or null when it is not one:
|
|
20
|
+
* the workbench-to-config bridge. Compose in the query bar, copy the
|
|
21
|
+
* section, paste it into a dashboard — the kind is read off the shape
|
|
22
|
+
* (two keys cross a matrix, a bucketed key draws a chart, one plain key
|
|
23
|
+
* ranks a breakdown, none is a metric), and the conditions half survives
|
|
24
|
+
* exactly as typed, because only this parser knows where it ended.
|
|
25
|
+
*/
|
|
26
|
+
export declare function sectionFromText(schema: DatabaseSchema, text: string): {
|
|
27
|
+
kind: "breakdown" | "chart" | "matrix" | "metric";
|
|
28
|
+
table: string;
|
|
29
|
+
query?: string | undefined;
|
|
30
|
+
measure?: string | undefined;
|
|
31
|
+
by?: string | undefined;
|
|
32
|
+
} | null;
|
|
33
|
+
/**
|
|
34
|
+
* Render a breakdown as text — the inverse, and the round trip is tested.
|
|
35
|
+
*
|
|
36
|
+
* One line, unlike a view. A breakdown is a phrase: two measures and a key is
|
|
37
|
+
* the normal size of one, and the whole point of the form is that it fits in
|
|
38
|
+
* a query bar.
|
|
39
|
+
*/
|
|
40
|
+
export declare function breakdownToText(schema: DatabaseSchema, def: BreakdownDef): string;
|
|
41
|
+
export {};
|