@hot-updater/server 1.0.0-rc.18 → 1.0.0-rc.21
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 +12 -14
- package/dist/adapters/drizzle.cjs +6 -8
- package/dist/adapters/drizzle.d.cts +2 -2
- package/dist/adapters/drizzle.d.mts +2 -2
- package/dist/adapters/drizzle.mjs +1 -3
- package/dist/adapters/drizzleExecutor.d.cts +1 -1
- package/dist/adapters/drizzleExecutor.d.mts +1 -1
- package/dist/adapters/kysely.cjs +4 -6
- package/dist/adapters/kysely.d.cts +3 -3
- package/dist/adapters/kysely.d.mts +3 -3
- package/dist/adapters/kysely.mjs +1 -3
- package/dist/adapters/kyselyExecutor.d.cts +1 -2
- package/dist/adapters/kyselyExecutor.d.mts +1 -2
- package/dist/adapters/mongodb.cjs +10 -21
- package/dist/adapters/mongodb.d.cts +1 -1
- package/dist/adapters/mongodb.d.mts +1 -1
- package/dist/adapters/mongodb.mjs +9 -20
- package/dist/adapters/mongodbAdapter.cjs +11 -11
- package/dist/adapters/mongodbAdapter.d.cts +1 -1
- package/dist/adapters/mongodbAdapter.d.mts +1 -1
- package/dist/adapters/mongodbAdapter.mjs +1 -1
- package/dist/adapters/prisma.cjs +7 -9
- package/dist/adapters/prisma.d.cts +1 -1
- package/dist/adapters/prisma.d.mts +1 -1
- package/dist/adapters/prisma.mjs +1 -3
- package/dist/adapters/prismaExecutor.cjs +2 -3
- package/dist/adapters/prismaExecutor.mjs +1 -2
- package/dist/adapters/sqlProviders.cjs +7 -2
- package/dist/adapters/sqlProviders.mjs +6 -1
- package/dist/assembly/assemblePlugins.cjs +26 -48
- package/dist/assembly/assemblePlugins.d.cts +2 -5
- package/dist/assembly/assemblePlugins.d.mts +2 -5
- package/dist/assembly/assemblePlugins.mjs +19 -45
- package/dist/assembly/clientPlugins.cjs +6 -4
- package/dist/assembly/clientPlugins.mjs +2 -0
- package/dist/assembly/configError.d.cts +1 -6
- package/dist/assembly/configError.d.mts +1 -6
- package/dist/assembly/configError.mjs +1 -6
- package/dist/core/api.cjs +0 -40
- package/dist/core/api.d.cts +5 -16
- package/dist/core/api.d.mts +5 -16
- package/dist/core/api.mjs +1 -37
- package/dist/core/baseCandidates.cjs +0 -43
- package/dist/core/baseCandidates.mjs +2 -43
- package/dist/core/operations.cjs +13 -12
- package/dist/core/operations.mjs +6 -5
- package/dist/core/reads.cjs +4 -4
- package/dist/core/reads.d.cts +2 -41
- package/dist/core/reads.d.mts +2 -41
- package/dist/core/reads.mjs +3 -3
- package/dist/core/releases.cjs +6 -6
- package/dist/core/releases.mjs +1 -1
- package/dist/core/writes.cjs +2 -2
- package/dist/core/writes.mjs +1 -1
- package/dist/createHotUpdaterCore.cjs +39 -39
- package/dist/createHotUpdaterCore.d.cts +61 -8
- package/dist/createHotUpdaterCore.d.mts +61 -8
- package/dist/createHotUpdaterCore.mjs +35 -32
- package/dist/db/engineDrizzleSchema.cjs +3 -4
- package/dist/db/engineDrizzleSchema.mjs +1 -2
- package/dist/db/enginePrismaSchema.cjs +4 -5
- package/dist/db/enginePrismaSchema.d.cts +1 -1
- package/dist/db/enginePrismaSchema.d.mts +1 -1
- package/dist/db/enginePrismaSchema.mjs +1 -2
- package/dist/db/releaseCatalog.cjs +6 -6
- package/dist/db/releaseCatalog.mjs +1 -1
- package/dist/db/updateArtifacts.cjs +6 -6
- package/dist/db/updateArtifacts.mjs +1 -1
- package/dist/handler.cjs +3 -4
- package/dist/handler.mjs +1 -1
- package/dist/handlerAdminRoutes.cjs +1 -2
- package/dist/handlerAdminRoutes.mjs +1 -2
- package/dist/handlerVersionRoutes.cjs +2 -2
- package/dist/handlerVersionRoutes.mjs +2 -2
- package/dist/index.cjs +9 -2
- package/dist/index.d.cts +4 -3
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +2 -1
- package/dist/node.cjs +3 -6
- package/dist/node.d.cts +3 -3
- package/dist/node.d.mts +3 -3
- package/dist/node.mjs +4 -4
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/plugins/api-keys/index.cjs +8 -52
- package/dist/plugins/api-keys/index.d.cts +1 -84
- package/dist/plugins/api-keys/index.d.mts +1 -84
- package/dist/plugins/api-keys/index.mjs +2 -49
- package/dist/plugins/definePlugin.d.cts +2 -200
- package/dist/plugins/definePlugin.d.mts +2 -200
- package/dist/plugins/insights/index.cjs +7 -126
- package/dist/plugins/insights/index.d.cts +1 -398
- package/dist/plugins/insights/index.d.mts +1 -398
- package/dist/plugins/insights/index.mjs +2 -121
- package/dist/plugins/official.cjs +30 -0
- package/dist/plugins/official.mjs +29 -0
- package/dist/storageAccess.cjs +13 -10
- package/dist/storageAccess.mjs +13 -10
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.mts +1 -1
- package/package.json +15 -37
- package/dist/_virtual/_rolldown/runtime.cjs +0 -23
- package/dist/assembly/clientPlugins.d.cts +0 -16
- package/dist/assembly/clientPlugins.d.mts +0 -16
- package/dist/assembly/configError.cjs +0 -7
- package/dist/assembly/databasePlugins.cjs +0 -27
- package/dist/assembly/databasePlugins.d.cts +0 -43
- package/dist/assembly/databasePlugins.d.mts +0 -43
- package/dist/assembly/databasePlugins.mjs +0 -26
- package/dist/assembly/pluginCli.cjs +0 -125
- package/dist/assembly/pluginCli.d.cts +0 -59
- package/dist/assembly/pluginCli.d.mts +0 -59
- package/dist/assembly/pluginCli.mjs +0 -121
- package/dist/core/baseCandidates.d.cts +0 -17
- package/dist/core/baseCandidates.d.mts +0 -17
- package/dist/core/schema.cjs +0 -279
- package/dist/core/schema.d.cts +0 -307
- package/dist/core/schema.d.mts +0 -307
- package/dist/core/schema.mjs +0 -277
- package/dist/database/aggregateBatching.cjs +0 -626
- package/dist/database/aggregateBatching.d.cts +0 -10
- package/dist/database/aggregateBatching.d.mts +0 -10
- package/dist/database/aggregateBatching.mjs +0 -625
- package/dist/database/busy.cjs +0 -36
- package/dist/database/busy.d.cts +0 -12
- package/dist/database/busy.d.mts +0 -12
- package/dist/database/busy.mjs +0 -36
- package/dist/database/cursor.cjs +0 -44
- package/dist/database/cursor.d.cts +0 -7
- package/dist/database/cursor.d.mts +0 -7
- package/dist/database/cursor.mjs +0 -40
- package/dist/database/database.cjs +0 -63
- package/dist/database/database.d.cts +0 -136
- package/dist/database/database.d.mts +0 -136
- package/dist/database/database.mjs +0 -63
- package/dist/database/definitions.d.cts +0 -78
- package/dist/database/definitions.d.mts +0 -78
- package/dist/database/engine.cjs +0 -37
- package/dist/database/engine.d.cts +0 -41
- package/dist/database/engine.d.mts +0 -41
- package/dist/database/engine.mjs +0 -37
- package/dist/database/engineAggregates.cjs +0 -118
- package/dist/database/engineAggregates.mjs +0 -115
- package/dist/database/engineReads.cjs +0 -182
- package/dist/database/engineReads.d.cts +0 -35
- package/dist/database/engineReads.d.mts +0 -35
- package/dist/database/engineReads.mjs +0 -181
- package/dist/database/engineTransaction.cjs +0 -399
- package/dist/database/engineTransaction.d.cts +0 -26
- package/dist/database/engineTransaction.d.mts +0 -26
- package/dist/database/engineTransaction.mjs +0 -398
- package/dist/database/errors.cjs +0 -27
- package/dist/database/errors.d.cts +0 -21
- package/dist/database/errors.d.mts +0 -21
- package/dist/database/errors.mjs +0 -24
- package/dist/database/fence.cjs +0 -125
- package/dist/database/fence.d.cts +0 -42
- package/dist/database/fence.d.mts +0 -42
- package/dist/database/fence.mjs +0 -117
- package/dist/database/index.cjs +0 -136
- package/dist/database/index.d.cts +0 -18
- package/dist/database/index.d.mts +0 -18
- package/dist/database/index.mjs +0 -16
- package/dist/database/kv/kvAdapter.cjs +0 -285
- package/dist/database/kv/kvAdapter.d.cts +0 -109
- package/dist/database/kv/kvAdapter.d.mts +0 -109
- package/dist/database/kv/kvAdapter.mjs +0 -284
- package/dist/database/resolveSchema.cjs +0 -199
- package/dist/database/resolveSchema.d.cts +0 -42
- package/dist/database/resolveSchema.d.mts +0 -42
- package/dist/database/resolveSchema.mjs +0 -196
- package/dist/database/retention.cjs +0 -92
- package/dist/database/retention.mjs +0 -90
- package/dist/database/schema.cjs +0 -24
- package/dist/database/schema.d.cts +0 -90
- package/dist/database/schema.d.mts +0 -90
- package/dist/database/schema.mjs +0 -23
- package/dist/database/sql/sqlAdapter.cjs +0 -290
- package/dist/database/sql/sqlAdapter.d.cts +0 -46
- package/dist/database/sql/sqlAdapter.d.mts +0 -46
- package/dist/database/sql/sqlAdapter.mjs +0 -287
- package/dist/database/sql/sqlSchema.cjs +0 -171
- package/dist/database/sql/sqlSchema.d.cts +0 -8
- package/dist/database/sql/sqlSchema.d.mts +0 -8
- package/dist/database/sql/sqlSchema.mjs +0 -165
- package/dist/db/coreDatabase.cjs +0 -85
- package/dist/db/coreDatabase.d.cts +0 -57
- package/dist/db/coreDatabase.d.mts +0 -57
- package/dist/db/coreDatabase.mjs +0 -78
- package/dist/db/createBundleDiff.cjs +0 -144
- package/dist/db/createBundleDiff.d.cts +0 -17
- package/dist/db/createBundleDiff.d.mts +0 -17
- package/dist/db/createBundleDiff.mjs +0 -141
- package/dist/db/engineMigrator.cjs +0 -53
- package/dist/db/engineMigrator.mjs +0 -53
- package/dist/db/engineSql.cjs +0 -26
- package/dist/db/engineSql.d.cts +0 -17
- package/dist/db/engineSql.d.mts +0 -17
- package/dist/db/engineSql.mjs +0 -25
- package/dist/db/engineSqlMigrator.cjs +0 -66
- package/dist/db/engineSqlMigrator.mjs +0 -66
- package/dist/db/fixedMigratorShared.cjs +0 -12
- package/dist/db/fixedMigratorShared.mjs +0 -11
- package/dist/db/index.cjs +0 -50
- package/dist/db/index.d.cts +0 -23
- package/dist/db/index.d.mts +0 -23
- package/dist/db/index.mjs +0 -33
- package/dist/db/releaseCatalog.d.cts +0 -16
- package/dist/db/releaseCatalog.d.mts +0 -16
- package/dist/db/schemaSettings.cjs +0 -63
- package/dist/db/schemaSettings.d.cts +0 -12
- package/dist/db/schemaSettings.d.mts +0 -12
- package/dist/db/schemaSettings.mjs +0 -62
- package/dist/db/settingsMigrator.cjs +0 -92
- package/dist/db/settingsMigrator.mjs +0 -89
- package/dist/db/types.cjs +0 -11
- package/dist/db/types.d.cts +0 -78
- package/dist/db/types.d.mts +0 -78
- package/dist/db/types.mjs +0 -10
- package/dist/diff.cjs +0 -3
- package/dist/diff.d.cts +0 -2
- package/dist/diff.d.mts +0 -2
- package/dist/diff.mjs +0 -2
- package/dist/plugins/api-keys/apiKeys.cjs +0 -139
- package/dist/plugins/api-keys/apiKeys.d.cts +0 -27
- package/dist/plugins/api-keys/apiKeys.d.mts +0 -27
- package/dist/plugins/api-keys/apiKeys.mjs +0 -129
- package/dist/plugins/api-keys/cli.cjs +0 -115
- package/dist/plugins/api-keys/cli.mjs +0 -114
- package/dist/plugins/api-keys/model.cjs +0 -58
- package/dist/plugins/api-keys/model.d.cts +0 -26
- package/dist/plugins/api-keys/model.d.mts +0 -26
- package/dist/plugins/api-keys/model.mjs +0 -58
- package/dist/plugins/api-keys/schema.cjs +0 -38
- package/dist/plugins/api-keys/schema.d.cts +0 -44
- package/dist/plugins/api-keys/schema.d.mts +0 -44
- package/dist/plugins/api-keys/schema.mjs +0 -39
- package/dist/plugins/definePlugin.cjs +0 -5
- package/dist/plugins/definePlugin.mjs +0 -5
- package/dist/plugins/index.cjs +0 -38
- package/dist/plugins/index.d.cts +0 -12
- package/dist/plugins/index.d.mts +0 -12
- package/dist/plugins/index.mjs +0 -9
- package/dist/plugins/insights/contract.cjs +0 -193
- package/dist/plugins/insights/contract.mjs +0 -189
- package/dist/plugins/insights/domain.d.cts +0 -149
- package/dist/plugins/insights/domain.d.mts +0 -149
- package/dist/plugins/insights/errors.cjs +0 -15
- package/dist/plugins/insights/errors.mjs +0 -14
- package/dist/plugins/insights/eventInput.cjs +0 -284
- package/dist/plugins/insights/eventInput.mjs +0 -282
- package/dist/plugins/insights/eventRow.cjs +0 -49
- package/dist/plugins/insights/eventRow.d.cts +0 -64
- package/dist/plugins/insights/eventRow.d.mts +0 -64
- package/dist/plugins/insights/eventRow.mjs +0 -47
- package/dist/plugins/insights/model.cjs +0 -17
- package/dist/plugins/insights/model.d.cts +0 -10
- package/dist/plugins/insights/model.d.mts +0 -10
- package/dist/plugins/insights/model.mjs +0 -17
- package/dist/plugins/insights/modelTypes.d.cts +0 -176
- package/dist/plugins/insights/modelTypes.d.mts +0 -176
- package/dist/plugins/insights/overview.cjs +0 -109
- package/dist/plugins/insights/overview.d.cts +0 -15
- package/dist/plugins/insights/overview.d.mts +0 -15
- package/dist/plugins/insights/overview.mjs +0 -107
- package/dist/plugins/insights/provider.cjs +0 -375
- package/dist/plugins/insights/provider.d.cts +0 -6
- package/dist/plugins/insights/provider.d.mts +0 -6
- package/dist/plugins/insights/provider.mjs +0 -374
- package/dist/plugins/insights/queryInput.cjs +0 -93
- package/dist/plugins/insights/queryInput.mjs +0 -91
- package/dist/plugins/insights/reads.cjs +0 -681
- package/dist/plugins/insights/reads.d.cts +0 -61
- package/dist/plugins/insights/reads.d.mts +0 -61
- package/dist/plugins/insights/reads.mjs +0 -674
- package/dist/plugins/insights/recordEvent.cjs +0 -319
- package/dist/plugins/insights/recordEvent.d.cts +0 -18
- package/dist/plugins/insights/recordEvent.d.mts +0 -18
- package/dist/plugins/insights/recordEvent.mjs +0 -316
- package/dist/plugins/insights/routes.cjs +0 -96
- package/dist/plugins/insights/routes.mjs +0 -95
- package/dist/plugins/insights/schema.cjs +0 -489
- package/dist/plugins/insights/schema.d.cts +0 -698
- package/dist/plugins/insights/schema.d.mts +0 -698
- package/dist/plugins/insights/schema.mjs +0 -483
- package/dist/plugins/insights/testing/eventRow.d.mts +0 -64
- package/dist/plugins/insights/testing/expectInsightsIndex.mjs +0 -18
- package/dist/plugins/insights/testing/fixtures.d.mts +0 -8
- package/dist/plugins/insights/testing/fixtures.mjs +0 -25
- package/dist/plugins/insights/testing/index.d.mts +0 -24
- package/dist/plugins/insights/testing/index.mjs +0 -25
- package/dist/plugins/insights/testing/insightsModelTests.mjs +0 -714
- package/dist/plugins/insights/testing/modelTypes.d.mts +0 -176
- package/dist/plugins/insights/testing/setupInsightsHttpTestSuite.d.mts +0 -11
- package/dist/plugins/insights/testing/setupInsightsHttpTestSuite.mjs +0 -107
- package/dist/plugins/insights/testing/setupInsightsModelTestSuite.d.mts +0 -11
- package/dist/plugins/insights/testing/setupInsightsModelTestSuite.mjs +0 -31
- package/dist/plugins/insights/testing/types.d.mts +0 -26
- package/dist/plugins/insights/types.d.cts +0 -33
- package/dist/plugins/insights/types.d.mts +0 -33
- package/src/adapters/drizzle.conformance.integration.spec.ts +0 -34
- package/src/adapters/drizzle.readBudgets.integration.spec.ts +0 -43
- package/src/adapters/drizzle.spec.ts +0 -314
- package/src/adapters/drizzle.ts +0 -64
- package/src/adapters/drizzleExecutor.ts +0 -166
- package/src/adapters/kysely.conformance.integration.spec.ts +0 -36
- package/src/adapters/kysely.readBudgets.integration.spec.ts +0 -44
- package/src/adapters/kysely.spec.ts +0 -244
- package/src/adapters/kysely.ts +0 -46
- package/src/adapters/kyselyExecutor.ts +0 -53
- package/src/adapters/mongodb.integration.spec.ts +0 -271
- package/src/adapters/mongodb.readBudgets.integration.spec.ts +0 -61
- package/src/adapters/mongodb.ts +0 -45
- package/src/adapters/mongodbAdapter.ts +0 -307
- package/src/adapters/prisma.conformance.integration.spec.ts +0 -36
- package/src/adapters/prisma.integration.spec.ts +0 -146
- package/src/adapters/prisma.readBudgets.integration.spec.ts +0 -43
- package/src/adapters/prisma.spec.ts +0 -257
- package/src/adapters/prisma.ts +0 -74
- package/src/adapters/prismaExecutor.ts +0 -107
- package/src/adapters/prismaTestClients.ts +0 -182
- package/src/adapters/sqlProviders.ts +0 -14
- package/src/assembly/assemblePlugins.ts +0 -302
- package/src/assembly/assembly.spec.ts +0 -377
- package/src/assembly/assembly.types.spec.ts +0 -203
- package/src/assembly/clientPlugins.ts +0 -113
- package/src/assembly/configError.ts +0 -4
- package/src/assembly/databasePlugins.spec.ts +0 -71
- package/src/assembly/databasePlugins.ts +0 -102
- package/src/assembly/pluginCli.spec.ts +0 -254
- package/src/assembly/pluginCli.ts +0 -248
- package/src/assembly/pluginRoutes.spec.ts +0 -186
- package/src/core/api.ts +0 -139
- package/src/core/baseCandidates.spec.ts +0 -378
- package/src/core/baseCandidates.ts +0 -136
- package/src/core/index.ts +0 -44
- package/src/core/operations.spec.ts +0 -415
- package/src/core/operations.ts +0 -551
- package/src/core/reads.spec.ts +0 -300
- package/src/core/reads.ts +0 -417
- package/src/core/releases.spec.ts +0 -308
- package/src/core/releases.ts +0 -345
- package/src/core/schema.ts +0 -173
- package/src/core/writes.ts +0 -160
- package/src/createHotUpdaterCore.ts +0 -260
- package/src/database/aggregateBatching.spec.ts +0 -577
- package/src/database/aggregateBatching.ts +0 -803
- package/src/database/busy.spec.ts +0 -45
- package/src/database/busy.ts +0 -50
- package/src/database/cursor.ts +0 -65
- package/src/database/database.ts +0 -366
- package/src/database/database.types.spec.ts +0 -135
- package/src/database/definitions.ts +0 -76
- package/src/database/engine.ts +0 -62
- package/src/database/engineAggregates.spec.ts +0 -536
- package/src/database/engineAggregates.ts +0 -189
- package/src/database/engineReads.spec.ts +0 -219
- package/src/database/engineReads.ts +0 -304
- package/src/database/engineTransaction.spec.ts +0 -710
- package/src/database/engineTransaction.ts +0 -582
- package/src/database/errors.ts +0 -30
- package/src/database/fence.spec.ts +0 -223
- package/src/database/fence.ts +0 -175
- package/src/database/index.ts +0 -160
- package/src/database/kv/kvAdapter.batching.integration.spec.ts +0 -15
- package/src/database/kv/kvAdapter.conformance.integration.spec.ts +0 -19
- package/src/database/kv/kvAdapter.readBudgets.integration.spec.ts +0 -17
- package/src/database/kv/kvAdapter.spec.ts +0 -595
- package/src/database/kv/kvAdapter.ts +0 -479
- package/src/database/kv/kvTestStore.ts +0 -118
- package/src/database/memory.readBudgets.integration.spec.ts +0 -13
- package/src/database/resolveSchema.spec.ts +0 -230
- package/src/database/resolveSchema.ts +0 -375
- package/src/database/retention.spec.ts +0 -314
- package/src/database/retention.ts +0 -129
- package/src/database/schema.ts +0 -213
- package/src/database/schema.types.spec.ts +0 -94
- package/src/database/sql/docker-compose.yml +0 -36
- package/src/database/sql/sqlAdapter.integration.spec.ts +0 -145
- package/src/database/sql/sqlAdapter.readBudgets.integration.spec.ts +0 -123
- package/src/database/sql/sqlAdapter.spec.ts +0 -343
- package/src/database/sql/sqlAdapter.ts +0 -552
- package/src/database/sql/sqlSchema.ts +0 -286
- package/src/database/sql/sqlTestExecutors.ts +0 -183
- package/src/databaseTestSuite.spec.ts +0 -44
- package/src/db/bundleRows.spec.ts +0 -164
- package/src/db/bundleRows.ts +0 -10
- package/src/db/coreDatabase.ts +0 -153
- package/src/db/createBundleDiff.spec.ts +0 -464
- package/src/db/createBundleDiff.ts +0 -370
- package/src/db/engineDrizzleSchema.ts +0 -72
- package/src/db/engineMigrator.ts +0 -105
- package/src/db/enginePrismaSchema.ts +0 -147
- package/src/db/engineSql.spec.ts +0 -130
- package/src/db/engineSql.ts +0 -48
- package/src/db/engineSqlMigrator.ts +0 -116
- package/src/db/fixedMigratorShared.ts +0 -17
- package/src/db/index.spec.ts +0 -554
- package/src/db/index.ts +0 -93
- package/src/db/node-sqlite.d.ts +0 -25
- package/src/db/pluginTables.spec.ts +0 -248
- package/src/db/releaseCatalog.ts +0 -106
- package/src/db/schemaSettings.ts +0 -104
- package/src/db/settingsMigrator.spec.ts +0 -65
- package/src/db/settingsMigrator.ts +0 -152
- package/src/db/types.ts +0 -82
- package/src/db/updateArtifacts.spec.ts +0 -235
- package/src/db/updateArtifacts.ts +0 -473
- package/src/db/updateCheck.bench.ts +0 -47
- package/src/diff.ts +0 -6
- package/src/handler-standalone.integration.spec.ts +0 -321
- package/src/handler.spec.ts +0 -273
- package/src/handler.testFixtures.ts +0 -25
- package/src/handler.ts +0 -302
- package/src/handlerAdminRoutes.spec.ts +0 -183
- package/src/handlerAdminRoutes.ts +0 -536
- package/src/handlerAuth.integration.spec.ts +0 -33
- package/src/handlerErrors.ts +0 -7
- package/src/handlerManagement.spec.ts +0 -202
- package/src/handlerParameters.ts +0 -105
- package/src/handlerReleaseCatalogRoutes.ts +0 -176
- package/src/handlerTypes.ts +0 -19
- package/src/handlerVersionRoutes.ts +0 -25
- package/src/index.ts +0 -13
- package/src/internalRouter.ts +0 -104
- package/src/lintRules.spec.ts +0 -76
- package/src/node.spec.ts +0 -119
- package/src/node.ts +0 -111
- package/src/plugins/api-keys/apiKeys.integration.spec.ts +0 -92
- package/src/plugins/api-keys/apiKeys.spec.ts +0 -166
- package/src/plugins/api-keys/apiKeys.ts +0 -214
- package/src/plugins/api-keys/apiKeysPlugin.spec.ts +0 -168
- package/src/plugins/api-keys/cli.spec.ts +0 -187
- package/src/plugins/api-keys/cli.ts +0 -140
- package/src/plugins/api-keys/index.ts +0 -73
- package/src/plugins/api-keys/model.ts +0 -88
- package/src/plugins/api-keys/schema.ts +0 -22
- package/src/plugins/definePlugin.ts +0 -242
- package/src/plugins/index.ts +0 -73
- package/src/plugins/insights/contract.spec.ts +0 -285
- package/src/plugins/insights/contract.ts +0 -372
- package/src/plugins/insights/domain.ts +0 -176
- package/src/plugins/insights/errors.ts +0 -11
- package/src/plugins/insights/eventInput.ts +0 -381
- package/src/plugins/insights/eventRow.ts +0 -209
- package/src/plugins/insights/index.ts +0 -222
- package/src/plugins/insights/insights.integration.spec.ts +0 -921
- package/src/plugins/insights/insightsRollout.integration.spec.ts +0 -186
- package/src/plugins/insights/model.spec.ts +0 -64
- package/src/plugins/insights/model.ts +0 -18
- package/src/plugins/insights/modelTypes.ts +0 -197
- package/src/plugins/insights/overview.spec.ts +0 -64
- package/src/plugins/insights/overview.ts +0 -214
- package/src/plugins/insights/provider.spec.ts +0 -585
- package/src/plugins/insights/provider.ts +0 -704
- package/src/plugins/insights/queryInput.ts +0 -142
- package/src/plugins/insights/reads.spec.ts +0 -1013
- package/src/plugins/insights/reads.ts +0 -1037
- package/src/plugins/insights/recordEvent.spec.ts +0 -897
- package/src/plugins/insights/recordEvent.ts +0 -476
- package/src/plugins/insights/retention.spec.ts +0 -115
- package/src/plugins/insights/routes.ts +0 -149
- package/src/plugins/insights/schema.ts +0 -409
- package/src/plugins/insights/testing/expectInsightsIndex.spec.ts +0 -42
- package/src/plugins/insights/testing/expectInsightsIndex.ts +0 -20
- package/src/plugins/insights/testing/fixtures.ts +0 -30
- package/src/plugins/insights/testing/index.ts +0 -45
- package/src/plugins/insights/testing/insightsModelTests.ts +0 -860
- package/src/plugins/insights/testing/setupInsightsHttpTestSuite.ts +0 -152
- package/src/plugins/insights/testing/setupInsightsModelTestSuite.ts +0 -36
- package/src/plugins/insights/testing/types.ts +0 -30
- package/src/plugins/insights/types.ts +0 -55
- package/src/plugins/pluginTestHarness.spec.ts +0 -56
- package/src/readBudgets.testFixtures.ts +0 -15
- package/src/releaseCatalogRoutes.integration.spec.ts +0 -200
- package/src/retention.spec.ts +0 -73
- package/src/runtime.spec.ts +0 -207
- package/src/runtime.testFixtures.ts +0 -60
- package/src/runtimeBundles.integration.spec.ts +0 -112
- package/src/storageAccess.spec.ts +0 -152
- package/src/storageAccess.ts +0 -141
- package/src/testUtilsMutation.integration.spec.ts +0 -216
- package/src/testUtilsPackage.integration.spec.ts +0 -180
- package/src/types/index.ts +0 -2
- package/src/version.ts +0 -3
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
import type { AggregateBatching } from "@hot-updater/plugin-core";
|
|
2
|
-
import type { DatabaseAdapter } from "@hot-updater/plugin-core/internal";
|
|
3
|
-
|
|
4
|
-
import { createCoreApi, type CoreApi } from "../core/api";
|
|
5
|
-
import { createCoreReads, type CoreStorage } from "../core/reads";
|
|
6
|
-
import { coreModule } from "../core/schema";
|
|
7
|
-
import { aggregateBatchingModule } from "../database/aggregateBatching";
|
|
8
|
-
import { createDatabaseEngine } from "../database/database";
|
|
9
|
-
import type { ReadMeasurement } from "../database/engine";
|
|
10
|
-
import { fencedName, SETTINGS_TABLE, withSchemaFence } from "../database/fence";
|
|
11
|
-
import { resolveSchema, validateSchema } from "../database/resolveSchema";
|
|
12
|
-
import { pruneDuringWrites } from "../database/retention";
|
|
13
|
-
import type { ModuleSchema } from "../database/schema";
|
|
14
|
-
import { pluginModule, pluginSettings } from "../db/coreDatabase";
|
|
15
|
-
import type {
|
|
16
|
-
ClientAuth,
|
|
17
|
-
PluginEndpoint,
|
|
18
|
-
PluginInstance,
|
|
19
|
-
} from "../plugins/definePlugin";
|
|
20
|
-
import { clientPluginsOf } from "./clientPlugins";
|
|
21
|
-
import { HotUpdaterConfigError } from "./configError";
|
|
22
|
-
|
|
23
|
-
export { HotUpdaterConfigError };
|
|
24
|
-
|
|
25
|
-
export interface MountedEndpoint extends PluginEndpoint {
|
|
26
|
-
readonly plugin: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AssembledPlugins {
|
|
30
|
-
/** Core's reads and typed writes, on the same engine as the plugins, which get only the reads. */
|
|
31
|
-
readonly core: CoreApi;
|
|
32
|
-
readonly api: Readonly<Record<string, unknown>>;
|
|
33
|
-
readonly endpoints: readonly MountedEndpoint[];
|
|
34
|
-
readonly clientAuth?: ClientAuth & { readonly plugin: string };
|
|
35
|
-
/** With `verify`: runs `read` and reports what it read at both boundaries. */
|
|
36
|
-
readonly measureReads?: <T>(
|
|
37
|
-
read: () => Promise<T>,
|
|
38
|
-
) => Promise<ReadMeasurement<T>>;
|
|
39
|
-
/** Applies batched aggregate changes still pending. */
|
|
40
|
-
readonly flush: () => Promise<void>;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface PluginShape {
|
|
44
|
-
readonly id: string;
|
|
45
|
-
readonly provides?: { readonly clientAuth?: true };
|
|
46
|
-
readonly schemaVersion: string;
|
|
47
|
-
readonly schema: ModuleSchema;
|
|
48
|
-
readonly namespace?: false;
|
|
49
|
-
init(context: unknown): PluginInstance;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const PLUGIN_KEYS = new Set([
|
|
53
|
-
"id",
|
|
54
|
-
"provides",
|
|
55
|
-
"schemaVersion",
|
|
56
|
-
"schema",
|
|
57
|
-
"namespace",
|
|
58
|
-
"init",
|
|
59
|
-
"cli",
|
|
60
|
-
]);
|
|
61
|
-
const CLI_KEYS = new Set(["commands", "clientCredential", "clientPlugin"]);
|
|
62
|
-
const INSTANCE_KEYS = new Set(["api", "endpoints", "clientAuth"]);
|
|
63
|
-
const METHODS = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
64
|
-
/** An id that prefixes table names is a SQL name; one that does not may use camelCase. */
|
|
65
|
-
const PREFIX_ID = /^[a-z][a-z0-9_]*$/u;
|
|
66
|
-
const UNPREFIXED_ID = /^[a-z][A-Za-z0-9_]*$/u;
|
|
67
|
-
const PATH = /^(\/(:?[A-Za-z0-9_.~-]+))+$/u;
|
|
68
|
-
|
|
69
|
-
const fail = (message: string): never => {
|
|
70
|
-
throw new HotUpdaterConfigError(message);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
74
|
-
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
75
|
-
|
|
76
|
-
const checkPlugin = (value: unknown, at: string): PluginShape => {
|
|
77
|
-
if (!isRecord(value)) return fail(`${at} is not a plugin.`);
|
|
78
|
-
if ("kind" in value) {
|
|
79
|
-
fail(`${at} has a kind; core is built in and is never passed as a plugin.`);
|
|
80
|
-
}
|
|
81
|
-
const unknown = Object.keys(value).find((key) => !PLUGIN_KEYS.has(key));
|
|
82
|
-
if (unknown !== undefined) fail(`${at} has an unknown key "${unknown}".`);
|
|
83
|
-
const plugin = value as unknown as PluginShape;
|
|
84
|
-
if (plugin.namespace !== undefined && plugin.namespace !== false) {
|
|
85
|
-
fail(`${at} may only set namespace: false.`);
|
|
86
|
-
}
|
|
87
|
-
const pattern = plugin.namespace === false ? UNPREFIXED_ID : PREFIX_ID;
|
|
88
|
-
if (typeof plugin.id !== "string" || !pattern.test(plugin.id)) {
|
|
89
|
-
fail(`${at} needs an id matching ${pattern.source}.`);
|
|
90
|
-
}
|
|
91
|
-
if (plugin.id === coreModule.id) {
|
|
92
|
-
fail(`${at} uses the id "core", which is core's own.`);
|
|
93
|
-
}
|
|
94
|
-
if (
|
|
95
|
-
typeof plugin.schemaVersion !== "string" ||
|
|
96
|
-
!isRecord(plugin.schema) ||
|
|
97
|
-
typeof plugin.init !== "function"
|
|
98
|
-
) {
|
|
99
|
-
fail(`Plugin "${plugin.id}" needs a schemaVersion, a schema, and init.`);
|
|
100
|
-
}
|
|
101
|
-
const provides = plugin.provides as unknown;
|
|
102
|
-
if (
|
|
103
|
-
provides !== undefined &&
|
|
104
|
-
(!isRecord(provides) ||
|
|
105
|
-
Object.entries(provides).some(
|
|
106
|
-
([key, flag]) => key !== "clientAuth" || flag !== true,
|
|
107
|
-
))
|
|
108
|
-
) {
|
|
109
|
-
fail(
|
|
110
|
-
`Plugin "${plugin.id}" may only declare provides: { clientAuth: true }.`,
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
// The CLI checks each command when it registers them.
|
|
114
|
-
const cli = value.cli;
|
|
115
|
-
if (cli !== undefined) {
|
|
116
|
-
if (
|
|
117
|
-
!isRecord(cli) ||
|
|
118
|
-
Object.keys(cli).some((key) => !CLI_KEYS.has(key)) ||
|
|
119
|
-
(cli.commands !== undefined && !Array.isArray(cli.commands))
|
|
120
|
-
) {
|
|
121
|
-
fail(
|
|
122
|
-
`Plugin "${plugin.id}" cli may only hold commands (an array), clientCredential, and clientPlugin.`,
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
if (
|
|
126
|
-
(cli as Record<string, unknown>).clientCredential !== undefined &&
|
|
127
|
-
plugin.provides?.clientAuth !== true
|
|
128
|
-
) {
|
|
129
|
-
fail(
|
|
130
|
-
`Plugin "${plugin.id}" adds cli.clientCredential without declaring provides: { clientAuth: true }.`,
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return plugin;
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
const checkEndpoint = (id: string, value: unknown): MountedEndpoint => {
|
|
138
|
-
const endpoint = (isRecord(value) ? value : {}) as Partial<PluginEndpoint>;
|
|
139
|
-
if (
|
|
140
|
-
!METHODS.has(endpoint.method as string) ||
|
|
141
|
-
typeof endpoint.path !== "string" ||
|
|
142
|
-
!PATH.test(endpoint.path) ||
|
|
143
|
-
(endpoint.access !== "client" && endpoint.access !== "admin") ||
|
|
144
|
-
typeof endpoint.handler !== "function"
|
|
145
|
-
) {
|
|
146
|
-
fail(
|
|
147
|
-
`Plugin "${id}" has an endpoint that needs a method, a path like /name/:param, access "client" or "admin", and a handler.`,
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
return { ...(endpoint as PluginEndpoint), plugin: id };
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
const checkInstance = (plugin: PluginShape, instance: unknown) => {
|
|
154
|
-
const { id } = plugin;
|
|
155
|
-
if (!isRecord(instance))
|
|
156
|
-
return fail(`Plugin "${id}" init returned no instance.`);
|
|
157
|
-
if (typeof instance.then === "function") {
|
|
158
|
-
fail(`Plugin "${id}" init returned a promise; init must be synchronous.`);
|
|
159
|
-
}
|
|
160
|
-
const unknown = Object.keys(instance).find((key) => !INSTANCE_KEYS.has(key));
|
|
161
|
-
if (unknown !== undefined || !("api" in instance)) {
|
|
162
|
-
fail(
|
|
163
|
-
`Plugin "${id}" init must return { api, endpoints?, clientAuth? }${unknown === undefined ? "" : `, not "${unknown}"`}.`,
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
const declared = plugin.provides?.clientAuth === true;
|
|
167
|
-
const clientAuth = instance.clientAuth as ClientAuth | undefined;
|
|
168
|
-
if (declared !== (clientAuth !== undefined)) {
|
|
169
|
-
fail(
|
|
170
|
-
declared
|
|
171
|
-
? `Plugin "${id}" declares provides: { clientAuth: true } but its instance has no clientAuth.`
|
|
172
|
-
: `Plugin "${id}" returns clientAuth without declaring provides: { clientAuth: true }.`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
if (
|
|
176
|
-
clientAuth !== undefined &&
|
|
177
|
-
(typeof clientAuth.authenticate !== "function" ||
|
|
178
|
-
!Array.isArray(clientAuth.varyHeaders) ||
|
|
179
|
-
!clientAuth.varyHeaders.every((header) => typeof header === "string"))
|
|
180
|
-
) {
|
|
181
|
-
fail(`Plugin "${id}" clientAuth needs varyHeaders and authenticate.`);
|
|
182
|
-
}
|
|
183
|
-
const endpoints = instance.endpoints ?? [];
|
|
184
|
-
if (!Array.isArray(endpoints))
|
|
185
|
-
fail(`Plugin "${id}" endpoints must be an array.`);
|
|
186
|
-
return {
|
|
187
|
-
api: instance.api,
|
|
188
|
-
clientAuth,
|
|
189
|
-
endpoints: (endpoints as unknown[]).map((endpoint) =>
|
|
190
|
-
checkEndpoint(id, endpoint),
|
|
191
|
-
),
|
|
192
|
-
};
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Checks every plugin, runs each `init` once against one engine over the
|
|
197
|
-
* database's storage adapter, and collects APIs, endpoints, and clientAuth.
|
|
198
|
-
* Core's reads run on the same engine, so plugins read core through `ctx.core`.
|
|
199
|
-
*/
|
|
200
|
-
export const assemblePlugins = (
|
|
201
|
-
value: unknown,
|
|
202
|
-
adapter: DatabaseAdapter,
|
|
203
|
-
{
|
|
204
|
-
now = Date.now,
|
|
205
|
-
storage = { resolveFileUrl: async () => null },
|
|
206
|
-
verify = false,
|
|
207
|
-
onCachedRoutesChange,
|
|
208
|
-
batching,
|
|
209
|
-
}: {
|
|
210
|
-
readonly now?: () => number;
|
|
211
|
-
readonly storage?: CoreStorage;
|
|
212
|
-
/** Runs the engine in verify mode, which meters reads for `measureReads`. */
|
|
213
|
-
readonly verify?: boolean;
|
|
214
|
-
/** The database's CDN purge, which core calls after a catalog write. */
|
|
215
|
-
readonly onCachedRoutesChange?: () => Promise<void>;
|
|
216
|
-
/** The database's aggregate batching, which adds its log tables. */
|
|
217
|
-
readonly batching?: AggregateBatching;
|
|
218
|
-
} = {},
|
|
219
|
-
): AssembledPlugins => {
|
|
220
|
-
if (!Array.isArray(value))
|
|
221
|
-
return fail("plugins must be an array of plugins.");
|
|
222
|
-
const plugins = value.map((plugin, position) =>
|
|
223
|
-
checkPlugin(plugin, `plugins[${position}]`),
|
|
224
|
-
);
|
|
225
|
-
const ids = new Set<string>();
|
|
226
|
-
for (const { id } of plugins) {
|
|
227
|
-
if (ids.has(id)) fail(`Plugin "${id}" is registered twice.`);
|
|
228
|
-
ids.add(id);
|
|
229
|
-
}
|
|
230
|
-
// Tooling prints them; a server checks them at startup like the rest.
|
|
231
|
-
clientPluginsOf(plugins);
|
|
232
|
-
const modules = plugins.map(pluginModule);
|
|
233
|
-
// Tooling creates the tables of the plugins a server runs, so their names
|
|
234
|
-
// need to be free of core's and of each other's, not of anyone else's.
|
|
235
|
-
if (modules.length > 0) validateSchema([coreModule, ...modules]);
|
|
236
|
-
// A fenced database also waits for each plugin's settings row.
|
|
237
|
-
const name = fencedName(adapter);
|
|
238
|
-
const fenced =
|
|
239
|
-
name === undefined || plugins.length === 0
|
|
240
|
-
? adapter
|
|
241
|
-
: withSchemaFence(adapter, name, pluginSettings(plugins));
|
|
242
|
-
const schema = resolveSchema([
|
|
243
|
-
coreModule,
|
|
244
|
-
...modules,
|
|
245
|
-
...(batching === undefined ? [] : [aggregateBatchingModule]),
|
|
246
|
-
]);
|
|
247
|
-
// Writes prune expired rows, except where verify measures what each API
|
|
248
|
-
// reads and writes.
|
|
249
|
-
const engine = createDatabaseEngine({
|
|
250
|
-
adapter: verify
|
|
251
|
-
? fenced
|
|
252
|
-
: pruneDuringWrites(fenced, schema.tables, {
|
|
253
|
-
leaseTable: SETTINGS_TABLE,
|
|
254
|
-
now,
|
|
255
|
-
}),
|
|
256
|
-
schema,
|
|
257
|
-
verify,
|
|
258
|
-
...(batching === undefined ? {} : { batching, now }),
|
|
259
|
-
});
|
|
260
|
-
const coreDatabase = engine.database(coreModule);
|
|
261
|
-
const core = createCoreApi(coreDatabase, storage, {
|
|
262
|
-
now,
|
|
263
|
-
...(onCachedRoutesChange === undefined ? {} : { onCachedRoutesChange }),
|
|
264
|
-
});
|
|
265
|
-
const reads = createCoreReads(coreDatabase, storage);
|
|
266
|
-
const api: Record<string, unknown> = {};
|
|
267
|
-
const endpoints: MountedEndpoint[] = [];
|
|
268
|
-
let clientAuth: AssembledPlugins["clientAuth"];
|
|
269
|
-
plugins.forEach((plugin, position) => {
|
|
270
|
-
const instance = checkInstance(
|
|
271
|
-
plugin,
|
|
272
|
-
plugin.init({
|
|
273
|
-
db: engine.database(modules[position]!),
|
|
274
|
-
core: reads,
|
|
275
|
-
now,
|
|
276
|
-
}),
|
|
277
|
-
);
|
|
278
|
-
if (instance.clientAuth !== undefined) {
|
|
279
|
-
if (clientAuth !== undefined) {
|
|
280
|
-
fail(
|
|
281
|
-
`Plugins "${clientAuth.plugin}" and "${plugin.id}" both provide clientAuth; keep one.`,
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
const provided = instance.clientAuth;
|
|
285
|
-
clientAuth = {
|
|
286
|
-
plugin: plugin.id,
|
|
287
|
-
varyHeaders: provided.varyHeaders,
|
|
288
|
-
authenticate: (headers) => provided.authenticate(headers),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
endpoints.push(...instance.endpoints);
|
|
292
|
-
api[plugin.id] = instance.api;
|
|
293
|
-
});
|
|
294
|
-
return {
|
|
295
|
-
core,
|
|
296
|
-
api,
|
|
297
|
-
endpoints,
|
|
298
|
-
...(clientAuth === undefined ? {} : { clientAuth }),
|
|
299
|
-
...(verify ? { measureReads: engine.measureReads } : {}),
|
|
300
|
-
flush: engine.flush,
|
|
301
|
-
};
|
|
302
|
-
};
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import { createMemoryAdapter } from "@hot-updater/plugin-core/internal";
|
|
2
|
-
import { describe, expect, it, vi } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { createBundleFixture } from "../../../test-utils/src/databaseTestFixtures";
|
|
5
|
-
import { createHotUpdater } from "../createHotUpdaterCore";
|
|
6
|
-
import { defineTable } from "../database/schema";
|
|
7
|
-
import { serverPluginsOf } from "../db";
|
|
8
|
-
import { listHotUpdaterRoutes } from "../handler";
|
|
9
|
-
import { definePlugin } from "../plugins/definePlugin";
|
|
10
|
-
import { HotUpdaterConfigError } from "./assemblePlugins";
|
|
11
|
-
|
|
12
|
-
const database = () => ({ name: "memory", adapter: createMemoryAdapter() });
|
|
13
|
-
|
|
14
|
-
const notes = definePlugin({
|
|
15
|
-
id: "notes",
|
|
16
|
-
schemaVersion: "1",
|
|
17
|
-
schema: {
|
|
18
|
-
notes: defineTable(
|
|
19
|
-
{ id: { type: "string" }, text: { type: "string" } },
|
|
20
|
-
{ key: ["id"] },
|
|
21
|
-
),
|
|
22
|
-
},
|
|
23
|
-
init: ({ db, now }) => ({
|
|
24
|
-
api: {
|
|
25
|
-
add: (id: string, text: string) =>
|
|
26
|
-
db.transaction(async (tx) => {
|
|
27
|
-
tx.create("notes", { id, text });
|
|
28
|
-
}),
|
|
29
|
-
read: (id: string) => db.findOne("notes", { id }),
|
|
30
|
-
now,
|
|
31
|
-
},
|
|
32
|
-
endpoints: [
|
|
33
|
-
{
|
|
34
|
-
method: "GET",
|
|
35
|
-
path: "/notes/:id",
|
|
36
|
-
access: "client",
|
|
37
|
-
handler: async (_request, params) =>
|
|
38
|
-
Response.json(await db.findOne("notes", { id: params.id! }), {
|
|
39
|
-
headers: { "cache-control": "public, max-age=60" },
|
|
40
|
-
}),
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
method: "DELETE",
|
|
44
|
-
path: "/notes/:id",
|
|
45
|
-
access: "admin",
|
|
46
|
-
handler: async () => new Response(null, { status: 204 }),
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
}),
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const keys = (
|
|
53
|
-
authenticate = async (headers: Headers) => headers.get("x-key") === "valid",
|
|
54
|
-
) =>
|
|
55
|
-
definePlugin({
|
|
56
|
-
id: "keys",
|
|
57
|
-
provides: { clientAuth: true },
|
|
58
|
-
schemaVersion: "1",
|
|
59
|
-
schema: {},
|
|
60
|
-
init: () => ({
|
|
61
|
-
api: {},
|
|
62
|
-
clientAuth: { varyHeaders: ["X-Key"], authenticate },
|
|
63
|
-
}),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const startup = (options: Record<string, unknown>) => () =>
|
|
67
|
-
createHotUpdater({ database: database(), ...options } as never);
|
|
68
|
-
|
|
69
|
-
describe("createHotUpdater with plugins", () => {
|
|
70
|
-
it("keeps the configured plugins, with their CLI additions, for the CLI", () => {
|
|
71
|
-
const withCommands = {
|
|
72
|
-
...notes,
|
|
73
|
-
cli: {
|
|
74
|
-
commands: [
|
|
75
|
-
{ name: "notes", description: "Manage notes", run: async () => {} },
|
|
76
|
-
],
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
const hotUpdater = createHotUpdater({
|
|
80
|
-
database: database(),
|
|
81
|
-
plugins: [withCommands],
|
|
82
|
-
clientAccess: "public",
|
|
83
|
-
});
|
|
84
|
-
expect(serverPluginsOf(hotUpdater)).toEqual([withCommands]);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("gives each plugin its own tables on the engine and exposes its API by id", async () => {
|
|
88
|
-
const hotUpdater = createHotUpdater({
|
|
89
|
-
database: database(),
|
|
90
|
-
plugins: [notes],
|
|
91
|
-
clientAccess: "public",
|
|
92
|
-
});
|
|
93
|
-
await hotUpdater.api.notes.add("n1", "hello");
|
|
94
|
-
await expect(hotUpdater.api.notes.read("n1")).resolves.toEqual({
|
|
95
|
-
id: "n1",
|
|
96
|
-
text: "hello",
|
|
97
|
-
});
|
|
98
|
-
expect(hotUpdater.api.notes.now()).toBeTypeOf("number");
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it("mounts endpoints by access and lists every route with its access, core's and the plugins' only", () => {
|
|
102
|
-
const { handlers } = createHotUpdater({
|
|
103
|
-
database: database(),
|
|
104
|
-
plugins: [notes, keys()],
|
|
105
|
-
});
|
|
106
|
-
expect(listHotUpdaterRoutes(handlers)).toEqual([
|
|
107
|
-
{ method: "GET", path: "/version", access: "public" },
|
|
108
|
-
{
|
|
109
|
-
method: "GET",
|
|
110
|
-
path: "/release-catalogs/app-version/:platform/:channelKey/:appVersion",
|
|
111
|
-
access: "client",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
method: "GET",
|
|
115
|
-
path: "/release-catalogs/fingerprint/:platform/:channelKey/:fingerprintHash",
|
|
116
|
-
access: "client",
|
|
117
|
-
},
|
|
118
|
-
{
|
|
119
|
-
method: "GET",
|
|
120
|
-
path: "/artifacts/v1/:targetBundleId/from/:currentBundleId",
|
|
121
|
-
access: "client",
|
|
122
|
-
},
|
|
123
|
-
{ method: "GET", path: "/version", access: "admin" },
|
|
124
|
-
{ method: "POST", path: "/releases", access: "admin" },
|
|
125
|
-
{ method: "POST", path: "/releases/:id/promote", access: "admin" },
|
|
126
|
-
{ method: "GET", path: "/releases/:id", access: "admin" },
|
|
127
|
-
{ method: "GET", path: "/releases", access: "admin" },
|
|
128
|
-
{ method: "PATCH", path: "/releases/:id", access: "admin" },
|
|
129
|
-
{ method: "POST", path: "/releases/:id/preflight", access: "admin" },
|
|
130
|
-
{ method: "DELETE", path: "/releases/:id", access: "admin" },
|
|
131
|
-
{ method: "GET", path: "/release-catalogs/:scopeKey", access: "admin" },
|
|
132
|
-
{ method: "GET", path: "/release-catalogs", access: "admin" },
|
|
133
|
-
{
|
|
134
|
-
method: "POST",
|
|
135
|
-
path: "/release-catalogs/:scopeKey/rebuild",
|
|
136
|
-
access: "admin",
|
|
137
|
-
},
|
|
138
|
-
{
|
|
139
|
-
method: "POST",
|
|
140
|
-
path: "/release-catalogs/:scopeKey/preflight",
|
|
141
|
-
access: "admin",
|
|
142
|
-
},
|
|
143
|
-
{ method: "GET", path: "/channels", access: "admin" },
|
|
144
|
-
{ method: "POST", path: "/channels", access: "admin" },
|
|
145
|
-
{ method: "DELETE", path: "/channels/:id", access: "admin" },
|
|
146
|
-
{ method: "GET", path: "/bundles/count", access: "admin" },
|
|
147
|
-
{ method: "GET", path: "/bundles/child-counts", access: "admin" },
|
|
148
|
-
{ method: "GET", path: "/bundles/:id", access: "admin" },
|
|
149
|
-
{ method: "GET", path: "/bundles", access: "admin" },
|
|
150
|
-
{ method: "PATCH", path: "/bundles/:id", access: "admin" },
|
|
151
|
-
{ method: "POST", path: "/bundles/delete", access: "admin" },
|
|
152
|
-
{ method: "GET", path: "/bundles/:id/children", access: "admin" },
|
|
153
|
-
{
|
|
154
|
-
method: "GET",
|
|
155
|
-
path: "/base-candidates/:candidateKey",
|
|
156
|
-
access: "admin",
|
|
157
|
-
},
|
|
158
|
-
{ method: "GET", path: "/notes/:id", access: "client" },
|
|
159
|
-
{ method: "DELETE", path: "/notes/:id", access: "admin" },
|
|
160
|
-
]);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it("puts client routes and client endpoints behind clientAuth, never /version, and varies cacheable answers", async () => {
|
|
164
|
-
const authenticate = vi.fn(async (headers: Headers) => {
|
|
165
|
-
if (headers.get("x-key") === "down") throw new Error("unavailable");
|
|
166
|
-
return headers.get("x-key") === "valid";
|
|
167
|
-
});
|
|
168
|
-
const hotUpdater = createHotUpdater({
|
|
169
|
-
database: database(),
|
|
170
|
-
plugins: [notes, keys(authenticate)],
|
|
171
|
-
});
|
|
172
|
-
await hotUpdater.api.notes.add("n1", "hello");
|
|
173
|
-
const client = (path: string, key?: string) =>
|
|
174
|
-
hotUpdater.handlers.client(
|
|
175
|
-
new Request(`https://updates.example.com${path}`, {
|
|
176
|
-
headers: key === undefined ? {} : { "x-key": key },
|
|
177
|
-
}),
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
expect((await client("/version")).status).toBe(200);
|
|
181
|
-
expect(authenticate).not.toHaveBeenCalled();
|
|
182
|
-
expect((await client("/notes/n1")).status).toBe(401);
|
|
183
|
-
expect((await client("/notes/n1", "down")).status).toBe(503);
|
|
184
|
-
const allowed = await client("/notes/n1", "valid");
|
|
185
|
-
expect(allowed.status).toBe(200);
|
|
186
|
-
await expect(allowed.json()).resolves.toMatchObject({ text: "hello" });
|
|
187
|
-
expect(allowed.headers.get("vary")).toBe("x-key");
|
|
188
|
-
expect(
|
|
189
|
-
(
|
|
190
|
-
await hotUpdater.handlers.admin(
|
|
191
|
-
new Request("https://updates.example.com/notes/n1", {
|
|
192
|
-
method: "DELETE",
|
|
193
|
-
}),
|
|
194
|
-
)
|
|
195
|
-
).status,
|
|
196
|
-
).toBe(204);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it("leaves client routes open with clientAccess: public", async () => {
|
|
200
|
-
const hotUpdater = createHotUpdater({
|
|
201
|
-
database: database(),
|
|
202
|
-
plugins: [notes],
|
|
203
|
-
clientAccess: "public",
|
|
204
|
-
});
|
|
205
|
-
await hotUpdater.api.notes.add("n1", "hello");
|
|
206
|
-
const response = await hotUpdater.handlers.client(
|
|
207
|
-
new Request("https://updates.example.com/notes/n1"),
|
|
208
|
-
);
|
|
209
|
-
expect(response.status).toBe(200);
|
|
210
|
-
expect(response.headers.get("vary")).toBeNull();
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
it("purges the database's cached client routes after core writes a Release Catalog, not after a plugin write", async () => {
|
|
214
|
-
const onCachedRoutesChange = vi.fn(async () => undefined);
|
|
215
|
-
const hotUpdater = createHotUpdater({
|
|
216
|
-
database: { ...database(), onCachedRoutesChange },
|
|
217
|
-
plugins: [notes],
|
|
218
|
-
clientAccess: "public",
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
await hotUpdater.api.notes.add("n1", "hello");
|
|
222
|
-
expect(onCachedRoutesChange).not.toHaveBeenCalled();
|
|
223
|
-
await hotUpdater.core.deploy([
|
|
224
|
-
{
|
|
225
|
-
bundle: createBundleFixture("801"),
|
|
226
|
-
release: {
|
|
227
|
-
channel: "production",
|
|
228
|
-
enabled: true,
|
|
229
|
-
fingerprintHash: null,
|
|
230
|
-
message: null,
|
|
231
|
-
shouldForceUpdate: false,
|
|
232
|
-
targetAppVersion: "1.0.0",
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
]);
|
|
236
|
-
|
|
237
|
-
expect(onCachedRoutesChange).toHaveBeenCalledTimes(1);
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
it("rejects every misconfiguration at startup", () => {
|
|
241
|
-
const valid = {
|
|
242
|
-
id: "valid",
|
|
243
|
-
schemaVersion: "1",
|
|
244
|
-
schema: {},
|
|
245
|
-
init: () => ({ api: {} }),
|
|
246
|
-
};
|
|
247
|
-
const cases: [Record<string, unknown>, string][] = [
|
|
248
|
-
[{ plugins: [notes] }, 'Set clientAccess to "public"'],
|
|
249
|
-
[
|
|
250
|
-
{ plugins: [keys()], clientAccess: "public" },
|
|
251
|
-
'Plugin "keys" provides clientAuth, so remove clientAccess.',
|
|
252
|
-
],
|
|
253
|
-
[
|
|
254
|
-
{
|
|
255
|
-
plugins: [keys(), { ...keys(), id: "sso" }],
|
|
256
|
-
},
|
|
257
|
-
'Plugins "keys" and "sso" both provide clientAuth; keep one.',
|
|
258
|
-
],
|
|
259
|
-
[
|
|
260
|
-
{ plugins: [valid, valid], clientAccess: "public" },
|
|
261
|
-
'Plugin "valid" is registered twice.',
|
|
262
|
-
],
|
|
263
|
-
[
|
|
264
|
-
{ plugins: [{ ...valid, kind: "core" }], clientAccess: "public" },
|
|
265
|
-
"plugins[0] has a kind; core is built in and is never passed as a plugin.",
|
|
266
|
-
],
|
|
267
|
-
[
|
|
268
|
-
{ plugins: [{ ...valid, hooks: {} }], clientAccess: "public" },
|
|
269
|
-
'plugins[0] has an unknown key "hooks".',
|
|
270
|
-
],
|
|
271
|
-
[
|
|
272
|
-
{
|
|
273
|
-
plugins: [{ ...valid, init: async () => ({ api: {} }) }],
|
|
274
|
-
clientAccess: "public",
|
|
275
|
-
},
|
|
276
|
-
'Plugin "valid" init returned a promise; init must be synchronous.',
|
|
277
|
-
],
|
|
278
|
-
[
|
|
279
|
-
{
|
|
280
|
-
plugins: [{ ...valid, provides: { clientAuth: true } }],
|
|
281
|
-
clientAccess: "public",
|
|
282
|
-
},
|
|
283
|
-
'Plugin "valid" declares provides: { clientAuth: true } but its instance has no clientAuth.',
|
|
284
|
-
],
|
|
285
|
-
[
|
|
286
|
-
{
|
|
287
|
-
plugins: [
|
|
288
|
-
{
|
|
289
|
-
...valid,
|
|
290
|
-
init: () => ({
|
|
291
|
-
api: {},
|
|
292
|
-
clientAuth: { varyHeaders: [], authenticate: async () => true },
|
|
293
|
-
}),
|
|
294
|
-
},
|
|
295
|
-
],
|
|
296
|
-
clientAccess: "public",
|
|
297
|
-
},
|
|
298
|
-
'Plugin "valid" returns clientAuth without declaring provides: { clientAuth: true }.',
|
|
299
|
-
],
|
|
300
|
-
[
|
|
301
|
-
{
|
|
302
|
-
plugins: [
|
|
303
|
-
{
|
|
304
|
-
...valid,
|
|
305
|
-
init: () => ({
|
|
306
|
-
api: {},
|
|
307
|
-
endpoints: [
|
|
308
|
-
{
|
|
309
|
-
method: "GET",
|
|
310
|
-
path: "/version",
|
|
311
|
-
access: "client",
|
|
312
|
-
handler: async () => new Response(),
|
|
313
|
-
},
|
|
314
|
-
],
|
|
315
|
-
}),
|
|
316
|
-
},
|
|
317
|
-
],
|
|
318
|
-
clientAccess: "public",
|
|
319
|
-
},
|
|
320
|
-
"plugin valid: GET /version (GET /version) collides with version on handlers.client.",
|
|
321
|
-
],
|
|
322
|
-
[
|
|
323
|
-
{ plugins: [{ ...valid, id: "Valid" }], clientAccess: "public" },
|
|
324
|
-
"plugins[0] needs an id matching ^[a-z][a-z0-9_]*$.",
|
|
325
|
-
],
|
|
326
|
-
[
|
|
327
|
-
{
|
|
328
|
-
plugins: [{ ...valid, namespace: "valid" }],
|
|
329
|
-
clientAccess: "public",
|
|
330
|
-
},
|
|
331
|
-
"plugins[0] may only set namespace: false.",
|
|
332
|
-
],
|
|
333
|
-
[
|
|
334
|
-
{
|
|
335
|
-
plugins: [{ ...valid, namespace: false, id: "Valid" }],
|
|
336
|
-
clientAccess: "public",
|
|
337
|
-
},
|
|
338
|
-
"plugins[0] needs an id matching ^[a-z][A-Za-z0-9_]*$.",
|
|
339
|
-
],
|
|
340
|
-
[
|
|
341
|
-
{
|
|
342
|
-
plugins: [{ ...valid, cli: { hooks: [] } }],
|
|
343
|
-
clientAccess: "public",
|
|
344
|
-
},
|
|
345
|
-
'Plugin "valid" cli may only hold commands (an array), clientCredential, and clientPlugin.',
|
|
346
|
-
],
|
|
347
|
-
[
|
|
348
|
-
{
|
|
349
|
-
plugins: [{ ...valid, cli: { commands: {} } }],
|
|
350
|
-
clientAccess: "public",
|
|
351
|
-
},
|
|
352
|
-
'Plugin "valid" cli may only hold commands (an array), clientCredential, and clientPlugin.',
|
|
353
|
-
],
|
|
354
|
-
[
|
|
355
|
-
{
|
|
356
|
-
plugins: [{ ...valid, cli: { clientCredential: {} } }],
|
|
357
|
-
clientAccess: "public",
|
|
358
|
-
},
|
|
359
|
-
'Plugin "valid" adds cli.clientCredential without declaring provides: { clientAuth: true }.',
|
|
360
|
-
],
|
|
361
|
-
[
|
|
362
|
-
{
|
|
363
|
-
plugins: [
|
|
364
|
-
{ ...valid, cli: { clientPlugin: { module: "", name: "x" } } },
|
|
365
|
-
],
|
|
366
|
-
clientAccess: "public",
|
|
367
|
-
},
|
|
368
|
-
'Plugin "valid" cli.clientPlugin needs a module and an export name the app can import',
|
|
369
|
-
],
|
|
370
|
-
];
|
|
371
|
-
for (const [options, message] of cases) {
|
|
372
|
-
const run = startup(options);
|
|
373
|
-
expect(run).toThrow(HotUpdaterConfigError);
|
|
374
|
-
expect(run).toThrow(message);
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
});
|