@mistriwork/engine 0.0.0 → 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 +16 -1
- 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 +262 -3
- package/schema/model.d.ts +298 -0
- package/schema/theme.d.ts +587 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a dashboard's sections ask, in the words they ask it: a metric's
|
|
3
|
+
* figure and its compare window's (its trend's are dashboardTrend.ts), a breakdown's groups, a chart's
|
|
4
|
+
* and a matrix's crosses, a board's lanes, a list's first rows, a pick's
|
|
5
|
+
* choices. The client's sections build their requests from these
|
|
6
|
+
* (client/components/Dashboard.tsx, dashboardModel.ts), and the server
|
|
7
|
+
* builds a first page's from them too (server/http/firstPaintRooms.ts), so
|
|
8
|
+
* the answers it carries are for the requests the sections then make.
|
|
9
|
+
*/
|
|
10
|
+
import { andQuery } from './query.js';
|
|
11
|
+
import { labelColumn, primaryKey } from './schema.js';
|
|
12
|
+
import { listPresentationColumns } from './appListColumns.js';
|
|
13
|
+
/** A breakdown's goals, a row per group: the target table's measure by its own `by`, within its query. */
|
|
14
|
+
export function targetText(section) {
|
|
15
|
+
const goal = section.target;
|
|
16
|
+
if (typeof goal !== 'object' || !goal.by)
|
|
17
|
+
return null;
|
|
18
|
+
return [goal.table, goal.query, `${goal.measure} by ${goal.by}`, `limit ${groupsAskedFor(section)}`].filter(Boolean).join(' ');
|
|
19
|
+
}
|
|
20
|
+
/** The breakdown a section asks for: its measure by its key, within its query. */
|
|
21
|
+
export function breakdownText(section, limit) {
|
|
22
|
+
/* A scatter asks its two measures in one line: the answer is its key, then `x`, then the measure. */
|
|
23
|
+
const measured = `${section.x ? `${section.x}, ` : ''}${section.measure ?? 'count'}`;
|
|
24
|
+
return [section.table, section.query, `${measured} by ${section.by}`, limit ? `limit ${limit}` : ''].filter(Boolean).join(' ');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A section with the reader's window in place of the author's. The server
|
|
28
|
+
* hands sections whose query already windows the `over` column a
|
|
29
|
+
* `windowBase` without that phrase, so the reader's phrase stands where
|
|
30
|
+
* the author's did rather than intersecting it. A compare derived from
|
|
31
|
+
* "previous period" is re-derived against the reader's window — `last 30
|
|
32
|
+
* days` picked compares against `previous 30 days`; a hand-written compare,
|
|
33
|
+
* or a window phrase with no "previous" (this month), hides the delta rather
|
|
34
|
+
* than compare the reader's window to the author's.
|
|
35
|
+
*/
|
|
36
|
+
export function windowed(section, windowNow) {
|
|
37
|
+
if (!section.over || !windowNow)
|
|
38
|
+
return section;
|
|
39
|
+
const base = section.windowBase ?? section.query;
|
|
40
|
+
const out = { ...section, query: andQuery(base, `${section.over} = ${windowNow}`), windowPhrase: windowNow };
|
|
41
|
+
/* A goal per group follows the reader's window by its own clock: this quarter's quotas beside this quarter's wins. */
|
|
42
|
+
const goal = section.target;
|
|
43
|
+
if (typeof goal === 'object' && goal.over)
|
|
44
|
+
out.target = {
|
|
45
|
+
...goal,
|
|
46
|
+
query: andQuery(section.targetWhole ? (goal.windowBase ?? goal.query) : goal.query, `${goal.over} = ${windowNow}`),
|
|
47
|
+
};
|
|
48
|
+
/* A metric with no window of its own is served `comparePeriod` and no
|
|
49
|
+
compare at all: it has a change only once a window is picked. */
|
|
50
|
+
if (section.compare || section.comparePeriod) {
|
|
51
|
+
const m = /^last\s+(\d+)\s+(minute|hour|day|week|month|year)s?$/i.exec(windowNow.trim());
|
|
52
|
+
out.compare =
|
|
53
|
+
section.comparePeriod && m ? andQuery(base, `${section.over} = previous ${m[1]} ${m[2]}${m[1] === '1' ? '' : 's'}`) : undefined;
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
/** The period a section with windows of its own is over: the reader's pick, else its first. */
|
|
58
|
+
export const sectionPeriod = (section, periods) => (section.windows?.length ? (section.windowKey && periods[section.windowKey]) || section.windows[0] : null);
|
|
59
|
+
/** A metric's figure over `query` — its own, or its compare window's. */
|
|
60
|
+
export const measureText = (section, query) => [section.table, query, section.measure ?? 'count'].filter(Boolean).join(' ');
|
|
61
|
+
/**
|
|
62
|
+
* How many groups a breakdown asks for before it orders them itself. The
|
|
63
|
+
* grammar answers groups in the engine's order — the alphabet, on SQLite —
|
|
64
|
+
* so a section's `limit` asked of the server kept the first groups by name,
|
|
65
|
+
* not the biggest. The groups are fetched, ordered on the client and then
|
|
66
|
+
* cut to the section's limit.
|
|
67
|
+
*/
|
|
68
|
+
const GROUPS_ASKED = 200;
|
|
69
|
+
export const groupsAskedFor = (section) => Math.max(section.limit ?? 0, GROUPS_ASKED);
|
|
70
|
+
/** The groups a chart asks for: stacked bars cross two keys, and bars and a donut order their groups themselves. */
|
|
71
|
+
export function chartLimit(section) {
|
|
72
|
+
/* A heat map's grid is every cross of its keys: a week of hours is 168. */
|
|
73
|
+
if (section.shape === 'stacked' || section.shape === 'heatmap')
|
|
74
|
+
return 400;
|
|
75
|
+
/* A waterfall's and a treemap's groups are all drawn, as a donut's slices: `limit` keeps the largest. */
|
|
76
|
+
if (section.shape === 'waterfall' || section.shape === 'treemap' || section.shape === 'scatter')
|
|
77
|
+
return groupsAskedFor(section);
|
|
78
|
+
/* A line (or the default chart) over two keys draws one line per second key: every period crossed with every series is a
|
|
79
|
+
row, and the server's twenty-row default dropped the later periods, so each line fell to zero where the rows ran out. */
|
|
80
|
+
if (section.shape === 'line' && (section.by ?? '').includes(','))
|
|
81
|
+
return 400;
|
|
82
|
+
return section.shape === 'donut' || section.shape === 'bar' ? groupsAskedFor(section) : section.limit;
|
|
83
|
+
}
|
|
84
|
+
/** A board's lane counts. */
|
|
85
|
+
export const boardCountText = (section) => [section.table, section.query, `count by ${section.by}`].filter(Boolean).join(' ');
|
|
86
|
+
/**
|
|
87
|
+
* A dashboard list's question: its rows with the columns it draws, the names
|
|
88
|
+
* its references are read by and the keys a row opens by, kept out of sight,
|
|
89
|
+
* up to its limit. `presentation` is the embedded view's, whose dress reads
|
|
90
|
+
* columns of its own.
|
|
91
|
+
*/
|
|
92
|
+
export function dashboardListQuery(lookups, section, presentation) {
|
|
93
|
+
const table = section.table ? lookups.table(section.table) : undefined;
|
|
94
|
+
const shown = [...new Set([...(section.show ?? []), ...(presentation ? listPresentationColumns(presentation) : [])])];
|
|
95
|
+
const keys = shown.length ? (table ? primaryKey(table) : []).filter((key) => !shown.includes(key)) : [];
|
|
96
|
+
const referenceLabels = shown.length ? lookups.referenceNames(section.table, shown) : {};
|
|
97
|
+
/* The record's own name rides along unseen as its key does: on a phone a
|
|
98
|
+
row is a card titled by it, and without it a lease was titled "21". */
|
|
99
|
+
const naming = shown.length && table ? lookups.naming(table) : undefined;
|
|
100
|
+
const titled = naming && !primaryKey(table).includes(naming) ? [naming] : [];
|
|
101
|
+
const named = [...new Set([...Object.values(referenceLabels), ...titled])].filter((name) => !shown.includes(name));
|
|
102
|
+
const query = [
|
|
103
|
+
section.table,
|
|
104
|
+
section.query,
|
|
105
|
+
section.sort ? `sort ${section.sort}` : '',
|
|
106
|
+
shown.length ? `show ${[...shown, ...named, ...keys].join(', ')}` : '',
|
|
107
|
+
`limit ${section.limit ?? 5}`,
|
|
108
|
+
]
|
|
109
|
+
.filter(Boolean)
|
|
110
|
+
.join(' ');
|
|
111
|
+
return { query, keys, named, referenceLabels };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* A pick's choices, where its column is a whole single-column reference: the
|
|
115
|
+
* records it points at, by name, fifty at most. Null where the column is a
|
|
116
|
+
* vocabulary (its values are the choices) or no section's table has it.
|
|
117
|
+
*/
|
|
118
|
+
export function pickQuery(lookups, sections, column) {
|
|
119
|
+
const home = sections.find((section) => section.table && lookups.table(section.table)?.columns.some((c) => c.name === column));
|
|
120
|
+
const table = home?.table ? lookups.table(home.table) : undefined;
|
|
121
|
+
const meta = table?.columns.find((c) => c.name === column);
|
|
122
|
+
const fk = table && !meta?.allowed?.length
|
|
123
|
+
? lookups.references(table.id).find((f) => f.from.columns.length === 1 && f.from.columns[0] === column)
|
|
124
|
+
: undefined;
|
|
125
|
+
const target = fk ? lookups.table(fk.to.table) : undefined;
|
|
126
|
+
const naming = target ? labelColumn(target) : undefined;
|
|
127
|
+
const pk = target ? primaryKey(target)[0] : undefined;
|
|
128
|
+
return target && naming ? `${target.id} show ${[...new Set([naming, pk])].join(', ')} sort ${naming}` : null;
|
|
129
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type AutoUnit } from './autoBucket.js';
|
|
2
|
+
import type { AskedSection } from './dashboardAsks.js';
|
|
3
|
+
/**
|
|
4
|
+
* The window a metric's trend is drawn over — the reader's, else the author's own on `over` — and the units it is
|
|
5
|
+
* read and drawn in: the span's unit (autoBucket.ts), read as days under a week so a week is added up on the client
|
|
6
|
+
* (`trendPoints`) and never handed whole to one month. Null where the metric has no window the date grammar bounds.
|
|
7
|
+
*/
|
|
8
|
+
export declare function sparkWindow(section: Pick<AskedSection, 'query' | 'over' | 'windowPhrase'>, now?: Date): {
|
|
9
|
+
phrase: string;
|
|
10
|
+
read: AutoUnit;
|
|
11
|
+
unit: AutoUnit;
|
|
12
|
+
} | null;
|
|
13
|
+
/**
|
|
14
|
+
* A metric's trend. Over a window, the metric over that window in the span's unit, so "last 30 days" draws thirty
|
|
15
|
+
* days and "last 12 months" twelve months: every windowed metric has one, beside its change, unless its author said
|
|
16
|
+
* `spark: false`. With no window, the author's `spark: true` is the measure's last twelve months, bucketed monthly by
|
|
17
|
+
* the section's own clock (`over`), composed with whatever the card is narrowed to by other than time. None without a
|
|
18
|
+
* clock.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sparkText(section: Pick<AskedSection, 'table' | 'query' | 'windowBase' | 'windowPhrase' | 'measure' | 'over' | 'spark'>): string | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A metric's trend, in the words it is asked: the window it is drawn over and the read that draws it. Apart from
|
|
3
|
+
* dashboardAsks.ts because it reads the window's span through autoBucket.ts, which only a drawn dashboard needs:
|
|
4
|
+
* held there, every App's first page carried the bucketing a chart and a trend use (scripts/bundle-budget.mjs).
|
|
5
|
+
* Asked by the client's metric (client/components/Dashboard.tsx) and by the server for a first page
|
|
6
|
+
* (server/http/firstPaintRooms.ts), in the same words.
|
|
7
|
+
*/
|
|
8
|
+
import { andQuery } from './query.js';
|
|
9
|
+
import { unitForSpan, windowDays, windowPhraseOf } from './autoBucket.js';
|
|
10
|
+
/**
|
|
11
|
+
* The window a metric's trend is drawn over — the reader's, else the author's own on `over` — and the units it is
|
|
12
|
+
* read and drawn in: the span's unit (autoBucket.ts), read as days under a week so a week is added up on the client
|
|
13
|
+
* (`trendPoints`) and never handed whole to one month. Null where the metric has no window the date grammar bounds.
|
|
14
|
+
*/
|
|
15
|
+
export function sparkWindow(section, now = new Date()) {
|
|
16
|
+
if (!section.over)
|
|
17
|
+
return null;
|
|
18
|
+
const phrase = section.windowPhrase ?? windowPhraseOf(section.query, section.over);
|
|
19
|
+
const days = phrase ? windowDays(phrase, now) : null;
|
|
20
|
+
if (!phrase || !days)
|
|
21
|
+
return null;
|
|
22
|
+
/* "today" is one day: a trend of fewer than four points is no line, and no slot is held for it. */
|
|
23
|
+
if (days < 4)
|
|
24
|
+
return null;
|
|
25
|
+
const unit = unitForSpan(days);
|
|
26
|
+
return { phrase, unit, read: unit === 'week' ? 'day' : unit };
|
|
27
|
+
}
|
|
28
|
+
/** The most periods a windowed trend reads: 182 days, or months and quarters past them, with room to spare. */
|
|
29
|
+
const TREND_PERIODS = 400;
|
|
30
|
+
/**
|
|
31
|
+
* A metric's trend. Over a window, the metric over that window in the span's unit, so "last 30 days" draws thirty
|
|
32
|
+
* days and "last 12 months" twelve months: every windowed metric has one, beside its change, unless its author said
|
|
33
|
+
* `spark: false`. With no window, the author's `spark: true` is the measure's last twelve months, bucketed monthly by
|
|
34
|
+
* the section's own clock (`over`), composed with whatever the card is narrowed to by other than time. None without a
|
|
35
|
+
* clock.
|
|
36
|
+
*/
|
|
37
|
+
export function sparkText(section) {
|
|
38
|
+
/* An authored false is heard over a window too: a card can be a figure alone, which it once could not say. */
|
|
39
|
+
if (!section.over || section.spark === false)
|
|
40
|
+
return null;
|
|
41
|
+
const window = sparkWindow(section);
|
|
42
|
+
if (window)
|
|
43
|
+
return [section.table, section.query, `${section.measure ?? 'count'} by ${window.read} ${section.over}`, `limit ${TREND_PERIODS}`]
|
|
44
|
+
.filter(Boolean)
|
|
45
|
+
.join(' ');
|
|
46
|
+
if (!section.spark)
|
|
47
|
+
return null;
|
|
48
|
+
const trendBase = section.windowBase ?? section.query;
|
|
49
|
+
return `${section.table} ${andQuery(trendBase, `${section.over} = last 12 months`)} ${section.measure ?? 'count'} by month ${section.over}`;
|
|
50
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declared references: an App's soft keys (`references: { product_sku: {
|
|
3
|
+
* to: 'product', column: 'sku' } }`), a column the database holds no foreign
|
|
4
|
+
* key for, joined as if it did.
|
|
5
|
+
*
|
|
6
|
+
* One edge, two places. In the App's logical catalog it is an ordinary
|
|
7
|
+
* ForeignKey marked `declared`, so every path, label, link, related list and
|
|
8
|
+
* rollup that follows a key follows it, and the reader's pruned catalog
|
|
9
|
+
* (accessibleSchema) drops it with the column or table it touches, as it does
|
|
10
|
+
* a real one. Below that catalog every layer compiles against the database's
|
|
11
|
+
* own, which has no such key; there the edge rides in the view
|
|
12
|
+
* (`ViewDef.references`), attached by the App's resource layer, and the
|
|
13
|
+
* compilers put it back for that one statement.
|
|
14
|
+
*
|
|
15
|
+
* Only a join that cannot multiply the base row is taken: the target column
|
|
16
|
+
* must be the table's single-column primary key or proven unique. That is
|
|
17
|
+
* judged when the App loads and again, against the catalog in hand, at every
|
|
18
|
+
* compile, so an edge that has stopped being true is dropped rather than run.
|
|
19
|
+
*/
|
|
20
|
+
import { type DatabaseSchema, type ForeignKey } from './schema.js';
|
|
21
|
+
/** One declaration as a resource block says it, in the source's local logical names. */
|
|
22
|
+
export interface DeclaredReference {
|
|
23
|
+
table: string;
|
|
24
|
+
column: string;
|
|
25
|
+
to: string;
|
|
26
|
+
/** Absent: the target's single-column primary key. */
|
|
27
|
+
toColumn?: string;
|
|
28
|
+
}
|
|
29
|
+
/** The edge a declaration names, or why the catalog refuses it. */
|
|
30
|
+
export declare function declaredEdge(schema: DatabaseSchema, reference: DeclaredReference, at: string): ForeignKey;
|
|
31
|
+
/**
|
|
32
|
+
* The single-column edges a composite key implies. A tenant's
|
|
33
|
+
* key `(tenant_id, group_id) → assignment_group (tenant_id, id)`, where `id`
|
|
34
|
+
* is unique on its own, names one group by `group_id` alone: every other leg
|
|
35
|
+
* is NOT NULL, so the composite is checked whenever `group_id` is set, and
|
|
36
|
+
* the row it lands on is the one `id` names. A reader who cannot read
|
|
37
|
+
* `tenant_id` — every tenant context — still follows the reference by the leg
|
|
38
|
+
* it reads. Never a smaller key: the target column is already unique alone,
|
|
39
|
+
* and the database's composite key still decides what may be written.
|
|
40
|
+
*/
|
|
41
|
+
export declare function impliedEdges(schema: DatabaseSchema): ForeignKey[];
|
|
42
|
+
/** The catalog with these edges in it: each column gains its `references`, as a real key's does. */
|
|
43
|
+
export declare function withDeclaredEdges(schema: DatabaseSchema, edges: readonly ForeignKey[]): DatabaseSchema;
|
|
44
|
+
/**
|
|
45
|
+
* A view's carried edges put back into the catalog it is compiled against,
|
|
46
|
+
* each only while it still holds there: both ends real columns, the target
|
|
47
|
+
* unique, the column not already a key. One that does not is dropped, and
|
|
48
|
+
* a path through it fails as a path through nothing.
|
|
49
|
+
*/
|
|
50
|
+
export declare function withViewReferences(schema: DatabaseSchema, references: readonly ForeignKey[] | undefined): DatabaseSchema;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Declared references: an App's soft keys (`references: { product_sku: {
|
|
3
|
+
* to: 'product', column: 'sku' } }`), a column the database holds no foreign
|
|
4
|
+
* key for, joined as if it did.
|
|
5
|
+
*
|
|
6
|
+
* One edge, two places. In the App's logical catalog it is an ordinary
|
|
7
|
+
* ForeignKey marked `declared`, so every path, label, link, related list and
|
|
8
|
+
* rollup that follows a key follows it, and the reader's pruned catalog
|
|
9
|
+
* (accessibleSchema) drops it with the column or table it touches, as it does
|
|
10
|
+
* a real one. Below that catalog every layer compiles against the database's
|
|
11
|
+
* own, which has no such key; there the edge rides in the view
|
|
12
|
+
* (`ViewDef.references`), attached by the App's resource layer, and the
|
|
13
|
+
* compilers put it back for that one statement.
|
|
14
|
+
*
|
|
15
|
+
* Only a join that cannot multiply the base row is taken: the target column
|
|
16
|
+
* must be the table's single-column primary key or proven unique. That is
|
|
17
|
+
* judged when the App loads and again, against the catalog in hand, at every
|
|
18
|
+
* compile, so an edge that has stopped being true is dropped rather than run.
|
|
19
|
+
*/
|
|
20
|
+
import { columnKind, findTable, primaryKey } from './schema.js';
|
|
21
|
+
/** The target column proves one row per value: the whole primary key, or unique on its own. */
|
|
22
|
+
function uniqueIn(table, column) {
|
|
23
|
+
const key = primaryKey(table);
|
|
24
|
+
return (key.length === 1 && key[0] === column) || Boolean(table.unique?.includes(column));
|
|
25
|
+
}
|
|
26
|
+
/** The edge a declaration names, or why the catalog refuses it. */
|
|
27
|
+
export function declaredEdge(schema, reference, at) {
|
|
28
|
+
const from = schema.tables.find((table) => table.id === reference.table);
|
|
29
|
+
if (!from)
|
|
30
|
+
throw new Error(`${at}: "${reference.table}" is not a table in this catalog.`);
|
|
31
|
+
const column = from.columns.find((item) => item.name === reference.column);
|
|
32
|
+
if (!column)
|
|
33
|
+
throw new Error(`${at}: "${reference.column}" is not a column of ${reference.table}.`);
|
|
34
|
+
if (column.primaryKey)
|
|
35
|
+
throw new Error(`${at}: "${reference.column}" is part of the primary key; a reference is a column that points elsewhere.`);
|
|
36
|
+
if (column.references || schema.foreignKeys.some((fk) => fk.from.table === from.id && fk.from.columns.includes(column.name))) {
|
|
37
|
+
throw new Error(`${at}: "${reference.column}" already has a foreign key in the database, which the App follows as it is.`);
|
|
38
|
+
}
|
|
39
|
+
const to = schema.tables.find((table) => table.id === reference.to);
|
|
40
|
+
if (!to)
|
|
41
|
+
throw new Error(`${at}: "to" names "${reference.to}", which is not a table in this catalog.`);
|
|
42
|
+
const key = primaryKey(to);
|
|
43
|
+
const toColumn = reference.toColumn ?? (key.length === 1 ? key[0] : undefined);
|
|
44
|
+
if (!toColumn)
|
|
45
|
+
throw new Error(`${at}: ${reference.to} has no single-column primary key, so name the "column" it is looked up by.`);
|
|
46
|
+
const target = to.columns.find((item) => item.name === toColumn);
|
|
47
|
+
if (!target)
|
|
48
|
+
throw new Error(`${at}: "${toColumn}" is not a column of ${reference.to}.`);
|
|
49
|
+
if (!uniqueIn(to, toColumn)) {
|
|
50
|
+
throw new Error(`${at}: ${reference.to}.${toColumn} is not proven unique (its primary key, or a unique constraint or index on it alone), ` +
|
|
51
|
+
`so one ${reference.table} could name several. Add a unique index on ${reference.to}(${toColumn}).`);
|
|
52
|
+
}
|
|
53
|
+
if (columnKind(column.type) !== columnKind(target.type)) {
|
|
54
|
+
throw new Error(`${at}: ${reference.table}.${column.name} is ${column.type} and ${reference.to}.${toColumn} is ${target.type}; ` +
|
|
55
|
+
'a reference compares like with like.');
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
name: `${from.id}_${column.name}_declared`,
|
|
59
|
+
from: { table: from.id, columns: [column.name] },
|
|
60
|
+
to: { table: to.id, columns: [toColumn] },
|
|
61
|
+
declared: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The single-column edges a composite key implies. A tenant's
|
|
66
|
+
* key `(tenant_id, group_id) → assignment_group (tenant_id, id)`, where `id`
|
|
67
|
+
* is unique on its own, names one group by `group_id` alone: every other leg
|
|
68
|
+
* is NOT NULL, so the composite is checked whenever `group_id` is set, and
|
|
69
|
+
* the row it lands on is the one `id` names. A reader who cannot read
|
|
70
|
+
* `tenant_id` — every tenant context — still follows the reference by the leg
|
|
71
|
+
* it reads. Never a smaller key: the target column is already unique alone,
|
|
72
|
+
* and the database's composite key still decides what may be written.
|
|
73
|
+
*/
|
|
74
|
+
export function impliedEdges(schema) {
|
|
75
|
+
const edges = [];
|
|
76
|
+
/* Each table's single-column keys, read once: a scan of the graph for every leg of every composite made this a
|
|
77
|
+
large part of pruning a tenant App's catalog, which startup does once per command footprint and role. */
|
|
78
|
+
let singles;
|
|
79
|
+
const singleKeys = () => {
|
|
80
|
+
const by = new Map();
|
|
81
|
+
for (const other of schema.foreignKeys)
|
|
82
|
+
if (other.from.columns.length === 1) {
|
|
83
|
+
const columns = by.get(other.from.table);
|
|
84
|
+
if (columns)
|
|
85
|
+
columns.add(other.from.columns[0]);
|
|
86
|
+
else
|
|
87
|
+
by.set(other.from.table, new Set(other.from.columns));
|
|
88
|
+
}
|
|
89
|
+
return by;
|
|
90
|
+
};
|
|
91
|
+
for (const fk of schema.foreignKeys) {
|
|
92
|
+
if (fk.declared || fk.from.columns.length < 2)
|
|
93
|
+
continue;
|
|
94
|
+
const from = findTable(schema, fk.from.table);
|
|
95
|
+
const to = findTable(schema, fk.to.table);
|
|
96
|
+
if (!from || !to)
|
|
97
|
+
continue;
|
|
98
|
+
fk.from.columns.forEach((name, index) => {
|
|
99
|
+
const column = from.columns.find((item) => item.name === name);
|
|
100
|
+
const target = to.columns.find((item) => item.name === fk.to.columns[index]);
|
|
101
|
+
if (!column || !target || column.primaryKey || !uniqueIn(to, target.name) || columnKind(column.type) !== columnKind(target.type))
|
|
102
|
+
return;
|
|
103
|
+
const legs = fk.from.columns.filter((other) => other !== name);
|
|
104
|
+
if (legs.some((other) => from.columns.find((item) => item.name === other)?.nullable !== false))
|
|
105
|
+
return;
|
|
106
|
+
// A column that is already a single-column key is that key's, and needs no second edge.
|
|
107
|
+
if ((singles ??= singleKeys()).get(from.id)?.has(name))
|
|
108
|
+
return;
|
|
109
|
+
edges.push({
|
|
110
|
+
// Named by what it joins, not after the constraint: a composite's name usually spells the column a tenant never reads.
|
|
111
|
+
name: `${from.id}.${name}->${to.id}`,
|
|
112
|
+
from: { table: from.id, columns: [name] },
|
|
113
|
+
to: { table: to.id, columns: [target.name] },
|
|
114
|
+
declared: true,
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return edges;
|
|
119
|
+
}
|
|
120
|
+
const sameEdge = (a, b) => a.from.table === b.from.table &&
|
|
121
|
+
a.to.table === b.to.table &&
|
|
122
|
+
a.from.columns[0] === b.from.columns[0] &&
|
|
123
|
+
a.to.columns[0] === b.to.columns[0];
|
|
124
|
+
/** The catalog with these edges in it: each column gains its `references`, as a real key's does. */
|
|
125
|
+
export function withDeclaredEdges(schema, edges) {
|
|
126
|
+
if (!edges.length)
|
|
127
|
+
return schema;
|
|
128
|
+
return {
|
|
129
|
+
...schema,
|
|
130
|
+
tables: schema.tables.map((table) => {
|
|
131
|
+
const mine = edges.filter((edge) => edge.from.table === table.id);
|
|
132
|
+
if (!mine.length)
|
|
133
|
+
return table;
|
|
134
|
+
return {
|
|
135
|
+
...table,
|
|
136
|
+
columns: table.columns.map((column) => {
|
|
137
|
+
const edge = mine.find((item) => item.from.columns[0] === column.name);
|
|
138
|
+
return edge && !column.references ? { ...column, references: { table: edge.to.table, column: edge.to.columns[0] } } : column;
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
}),
|
|
142
|
+
foreignKeys: [...schema.foreignKeys, ...edges],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A view's carried edges put back into the catalog it is compiled against,
|
|
147
|
+
* each only while it still holds there: both ends real columns, the target
|
|
148
|
+
* unique, the column not already a key. One that does not is dropped, and
|
|
149
|
+
* a path through it fails as a path through nothing.
|
|
150
|
+
*/
|
|
151
|
+
export function withViewReferences(schema, references) {
|
|
152
|
+
if (!references?.length)
|
|
153
|
+
return schema;
|
|
154
|
+
/* One catalog per (view's references, catalog): a fresh object each time would also start
|
|
155
|
+
every cache keyed by the catalog (resolvePath's) empty on every compile. */
|
|
156
|
+
let byCatalog = viewCatalogs.get(references);
|
|
157
|
+
if (!byCatalog)
|
|
158
|
+
viewCatalogs.set(references, (byCatalog = new WeakMap()));
|
|
159
|
+
let kept = byCatalog.get(schema);
|
|
160
|
+
if (kept)
|
|
161
|
+
return kept;
|
|
162
|
+
/* And by their text: a scoped reader copies every view it is handed before compiling it (scopedReads.ts
|
|
163
|
+
snapshotScopedData), so the same references arrive as a new array on every read and the identity above never
|
|
164
|
+
holds there — a field report's large tenant App built this catalog afresh for every list, count and breakdown
|
|
165
|
+
of an enter. The widened catalog depends on nothing but the edges' words and the catalog, so equal text is the
|
|
166
|
+
same answer. Bounded per catalog: one App carries one set; past the bound a set is built as before. */
|
|
167
|
+
const text = JSON.stringify(references);
|
|
168
|
+
let byText = viewCatalogsByText.get(schema);
|
|
169
|
+
if (!byText)
|
|
170
|
+
viewCatalogsByText.set(schema, (byText = new Map()));
|
|
171
|
+
kept = byText.get(text);
|
|
172
|
+
if (!kept) {
|
|
173
|
+
kept = withViewReferencesFresh(schema, references);
|
|
174
|
+
if (byText.size < VIEW_CATALOGS_BY_TEXT)
|
|
175
|
+
byText.set(text, kept);
|
|
176
|
+
}
|
|
177
|
+
byCatalog.set(schema, kept);
|
|
178
|
+
return kept;
|
|
179
|
+
}
|
|
180
|
+
const viewCatalogs = new WeakMap();
|
|
181
|
+
const viewCatalogsByText = new WeakMap();
|
|
182
|
+
const VIEW_CATALOGS_BY_TEXT = 64;
|
|
183
|
+
function withViewReferencesFresh(schema, references) {
|
|
184
|
+
let implied;
|
|
185
|
+
const holding = references.filter((edge) => {
|
|
186
|
+
if (edge.from.columns.length !== 1 || edge.to.columns.length !== 1)
|
|
187
|
+
return false;
|
|
188
|
+
if (schema.foreignKeys.some((fk) => fk.name === edge.name && fk.from.table === edge.from.table))
|
|
189
|
+
return false;
|
|
190
|
+
// A leg of a composite key in this catalog: true here as long as the key is.
|
|
191
|
+
if ((implied ??= impliedEdges(schema)).some((other) => sameEdge(other, edge)))
|
|
192
|
+
return true;
|
|
193
|
+
try {
|
|
194
|
+
declaredEdge(schema, { table: edge.from.table, column: edge.from.columns[0], to: edge.to.table, toColumn: edge.to.columns[0] }, '');
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return withDeclaredEdges(schema, holding.map((edge) => ({ ...edge, declared: true })));
|
|
202
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fields whose definitions are rows: a record's JSON column holds one value
|
|
3
|
+
* per key, and the keys — their captions, types, choices and whether they
|
|
4
|
+
* are required — are rows of another resource that an administrator edits
|
|
5
|
+
* while the App runs, per tenant where the App is a tenant App:
|
|
6
|
+
*
|
|
7
|
+
* product: { defined: { specs: { from: 'attribute', per: 'category_id' } } }
|
|
8
|
+
* attribute: { access: 'write' }
|
|
9
|
+
*
|
|
10
|
+
* A product is shaped by the attribute rows whose `category_id` is its own
|
|
11
|
+
* and by those whose `category_id` is empty (fields every product has).
|
|
12
|
+
*
|
|
13
|
+
* The definition rows have fixed column names, so there is one way to say
|
|
14
|
+
* one: `key`, `label` and `type` always; `options` (one choice per line),
|
|
15
|
+
* `required`, `help` and `position` where the table has them. A row this
|
|
16
|
+
* module cannot read (a type outside the set, a key that is not an
|
|
17
|
+
* identifier) is left out rather than allowed to break a page: definitions
|
|
18
|
+
* are data, typed by people, at runtime.
|
|
19
|
+
*
|
|
20
|
+
* This module is what the declaration says and what a first page needs to
|
|
21
|
+
* know of it (a path the query language reads, the column served). Reading
|
|
22
|
+
* definition rows and judging values by them is definedValues.ts's, which
|
|
23
|
+
* only the server and the parts loaded on demand import.
|
|
24
|
+
*/
|
|
25
|
+
import { type ColumnMeta, type TableMeta } from './schema.js';
|
|
26
|
+
/** The kinds a definition's `type` may name: the command `field.*` words, with `choices` for many of a list. */
|
|
27
|
+
export declare const DEFINED_TYPES: readonly ['text', 'textarea', 'number', 'money', 'date', 'datetime', 'bool', 'choice', 'choices'];
|
|
28
|
+
export type DefinedType = (typeof DEFINED_TYPES)[number];
|
|
29
|
+
/** `resources.<r>.defined.<column>`: which rows define the column's fields, and the column that picks them. */
|
|
30
|
+
export interface DefinedBinding {
|
|
31
|
+
/** The resource whose rows are the definitions. */
|
|
32
|
+
from: string;
|
|
33
|
+
/** A column both tables have: a record is shaped by the definitions whose value matches its own, and those with none. */
|
|
34
|
+
per?: string;
|
|
35
|
+
}
|
|
36
|
+
/** One definition row, read. */
|
|
37
|
+
export interface DefinedField {
|
|
38
|
+
key: string;
|
|
39
|
+
label: string;
|
|
40
|
+
type: DefinedType;
|
|
41
|
+
/** The choices of a `choice` or `choices` field, in the order written. */
|
|
42
|
+
options: string[];
|
|
43
|
+
required: boolean;
|
|
44
|
+
help?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The columns every definitions table has, and those it may. */
|
|
47
|
+
export declare const NEEDED: readonly ['key', 'label', 'type'];
|
|
48
|
+
export declare const OPTIONAL: readonly ['options', 'required', 'help', 'position'];
|
|
49
|
+
/** A definition's key, and the key a path reads: written into SQL as a literal, so held to an identifier. */
|
|
50
|
+
export declare const DEFINED_KEY: RegExp;
|
|
51
|
+
/** The most definitions read for one record: a form, not a catalog. */
|
|
52
|
+
export declare const DEFINITIONS_MOST = 200;
|
|
53
|
+
/** `resources.<r>.defined`, read for its shape. Throws the sentence the author reads. */
|
|
54
|
+
export declare function parseDefined(raw: unknown, at: string): Record<string, DefinedBinding>;
|
|
55
|
+
/** The column each binding is on, by resolved table id: what the App serves the browser (`ResolvedResource.defined`). */
|
|
56
|
+
export declare function servedDefined(bindings: Readonly<Record<string, DefinedBinding>> | undefined, table: TableMeta, fromTable: (name: string) => TableMeta | undefined): {
|
|
57
|
+
served: Record<string, DefinedBinding>;
|
|
58
|
+
problems: string[];
|
|
59
|
+
};
|
|
60
|
+
/** A JSON key a path reads (`specs.capacity`) is no column of its own: its type says so, and the query language reads its values as they are written. */
|
|
61
|
+
export declare const DEFINED_PATH_TYPE = "defined";
|
|
62
|
+
/** The column a JSON key path reads as: of no declared kind, so `2` compares as a number and `"2"` as text (query.ts `coerce`). */
|
|
63
|
+
export declare function definedColumnMeta(key: string): ColumnMeta;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fields whose definitions are rows: a record's JSON column holds one value
|
|
3
|
+
* per key, and the keys — their captions, types, choices and whether they
|
|
4
|
+
* are required — are rows of another resource that an administrator edits
|
|
5
|
+
* while the App runs, per tenant where the App is a tenant App:
|
|
6
|
+
*
|
|
7
|
+
* product: { defined: { specs: { from: 'attribute', per: 'category_id' } } }
|
|
8
|
+
* attribute: { access: 'write' }
|
|
9
|
+
*
|
|
10
|
+
* A product is shaped by the attribute rows whose `category_id` is its own
|
|
11
|
+
* and by those whose `category_id` is empty (fields every product has).
|
|
12
|
+
*
|
|
13
|
+
* The definition rows have fixed column names, so there is one way to say
|
|
14
|
+
* one: `key`, `label` and `type` always; `options` (one choice per line),
|
|
15
|
+
* `required`, `help` and `position` where the table has them. A row this
|
|
16
|
+
* module cannot read (a type outside the set, a key that is not an
|
|
17
|
+
* identifier) is left out rather than allowed to break a page: definitions
|
|
18
|
+
* are data, typed by people, at runtime.
|
|
19
|
+
*
|
|
20
|
+
* This module is what the declaration says and what a first page needs to
|
|
21
|
+
* know of it (a path the query language reads, the column served). Reading
|
|
22
|
+
* definition rows and judging values by them is definedValues.ts's, which
|
|
23
|
+
* only the server and the parts loaded on demand import.
|
|
24
|
+
*/
|
|
25
|
+
import { isJsonType } from './schema.js';
|
|
26
|
+
/** The kinds a definition's `type` may name: the command `field.*` words, with `choices` for many of a list. */
|
|
27
|
+
export const DEFINED_TYPES = ['text', 'textarea', 'number', 'money', 'date', 'datetime', 'bool', 'choice', 'choices'];
|
|
28
|
+
/** The columns every definitions table has, and those it may. */
|
|
29
|
+
export const NEEDED = ['key', 'label', 'type'];
|
|
30
|
+
export const OPTIONAL = ['options', 'required', 'help', 'position'];
|
|
31
|
+
/** A definition's key, and the key a path reads: written into SQL as a literal, so held to an identifier. */
|
|
32
|
+
export const DEFINED_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
33
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
34
|
+
/** The most definitions read for one record: a form, not a catalog. */
|
|
35
|
+
export const DEFINITIONS_MOST = 200;
|
|
36
|
+
/** `resources.<r>.defined`, read for its shape. Throws the sentence the author reads. */
|
|
37
|
+
export function parseDefined(raw, at) {
|
|
38
|
+
const shape = `${at} has a "defined" that is not an object of the resource's own JSON column → { from, per? }, like defined: { specs: { from: 'attribute', per: 'category_id' } }.`;
|
|
39
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw) || !Object.keys(raw).length)
|
|
40
|
+
throw new Error(shape);
|
|
41
|
+
const bindings = {};
|
|
42
|
+
for (const [column, value] of Object.entries(raw)) {
|
|
43
|
+
const here = `${at}, defined "${column}"`;
|
|
44
|
+
if (!IDENTIFIER.test(column))
|
|
45
|
+
throw new Error(`${here}: defined fields are keyed by the resource's own JSON column, never a path.`);
|
|
46
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
47
|
+
throw new Error(shape);
|
|
48
|
+
const given = value;
|
|
49
|
+
const unknown = Object.keys(given).find((key) => key !== 'from' && key !== 'per');
|
|
50
|
+
if (unknown)
|
|
51
|
+
throw new Error(`${here} has "${unknown}"; it takes from and per.`);
|
|
52
|
+
if (typeof given.from !== 'string' || !given.from.trim())
|
|
53
|
+
throw new Error(`${here}: "from" names the resource whose rows define the fields.`);
|
|
54
|
+
const binding = { from: given.from.trim() };
|
|
55
|
+
if (given.per !== undefined) {
|
|
56
|
+
if (typeof given.per !== 'string' || !IDENTIFIER.test(given.per))
|
|
57
|
+
throw new Error(`${here}: "per" names one column both resources have, like per: 'category_id'.`);
|
|
58
|
+
binding.per = given.per;
|
|
59
|
+
}
|
|
60
|
+
bindings[column] = binding;
|
|
61
|
+
}
|
|
62
|
+
return bindings;
|
|
63
|
+
}
|
|
64
|
+
/** Why a binding cannot run on this catalog, or undefined: a JSON column, a definitions table with its columns, and `per` on both. */
|
|
65
|
+
function definedProblem(column, binding, table, from) {
|
|
66
|
+
const own = table.columns.find((candidate) => candidate.name === column);
|
|
67
|
+
if (!own)
|
|
68
|
+
return `defined "${column}": "${table.name}" has no column "${column}".`;
|
|
69
|
+
if (!isJsonType(own.type))
|
|
70
|
+
return `defined "${column}" is ${own.type || 'untyped'}; defined fields live in a JSON column (jsonb on PostgreSQL, a column declared JSON on SQLite).`;
|
|
71
|
+
if (!from)
|
|
72
|
+
return `defined "${column}": "${binding.from}" is not a declared resource on this connection.`;
|
|
73
|
+
const missing = NEEDED.filter((name) => !from.columns.some((candidate) => candidate.name === name));
|
|
74
|
+
if (missing.length)
|
|
75
|
+
return `defined "${column}": "${from.name}" has no ${missing.map((name) => `"${name}"`).join(', ')}; a definition row has key, label and type (and may have options, required, help and position).`;
|
|
76
|
+
if (binding.per) {
|
|
77
|
+
if (!table.columns.some((candidate) => candidate.name === binding.per))
|
|
78
|
+
return `defined "${column}": "${table.name}" has no column "${binding.per}" for per.`;
|
|
79
|
+
if (!from.columns.some((candidate) => candidate.name === binding.per))
|
|
80
|
+
return `defined "${column}": "${from.name}" has no column "${binding.per}" for per.`;
|
|
81
|
+
}
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
/** The column each binding is on, by resolved table id: what the App serves the browser (`ResolvedResource.defined`). */
|
|
85
|
+
export function servedDefined(bindings, table, fromTable) {
|
|
86
|
+
const served = {};
|
|
87
|
+
const problems = [];
|
|
88
|
+
for (const [column, binding] of Object.entries(bindings ?? {})) {
|
|
89
|
+
const from = fromTable(binding.from);
|
|
90
|
+
const problem = definedProblem(column, binding, table, from);
|
|
91
|
+
if (problem)
|
|
92
|
+
problems.push(problem);
|
|
93
|
+
else
|
|
94
|
+
served[column] = { from: from.id, ...(binding.per ? { per: binding.per } : {}) };
|
|
95
|
+
}
|
|
96
|
+
return { served, problems };
|
|
97
|
+
}
|
|
98
|
+
/** A JSON key a path reads (`specs.capacity`) is no column of its own: its type says so, and the query language reads its values as they are written. */
|
|
99
|
+
export const DEFINED_PATH_TYPE = 'defined';
|
|
100
|
+
/** The column a JSON key path reads as: of no declared kind, so `2` compares as a number and `"2"` as text (query.ts `coerce`). */
|
|
101
|
+
export function definedColumnMeta(key) {
|
|
102
|
+
return { name: key, type: DEFINED_PATH_TYPE, nullable: true, primaryKey: false };
|
|
103
|
+
}
|