@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
package/src/runtime.spec.ts
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createStoragePlugin,
|
|
3
|
-
type ConfigInput,
|
|
4
|
-
} from "@hot-updater/plugin-core";
|
|
5
|
-
import { describe, expect, expectTypeOf, it, vi } from "vitest";
|
|
6
|
-
|
|
7
|
-
import packageJson from "../package.json" with { type: "json" };
|
|
8
|
-
import { coreSettings } from "./db/coreDatabase";
|
|
9
|
-
import { createHotUpdater, HotUpdaterConfigError } from "./index";
|
|
10
|
-
import type {
|
|
11
|
-
ClientAccessPolicy,
|
|
12
|
-
CreateHotUpdaterOptions,
|
|
13
|
-
RuntimeHotUpdaterAPI,
|
|
14
|
-
} from "./index";
|
|
15
|
-
import {
|
|
16
|
-
createFencedDatabase,
|
|
17
|
-
createRuntimeDatabase,
|
|
18
|
-
} from "./runtime.testFixtures";
|
|
19
|
-
import { HOT_UPDATER_SERVER_VERSION } from "./version";
|
|
20
|
-
|
|
21
|
-
describe("runtime createHotUpdater", () => {
|
|
22
|
-
it.each(["authorityId", "catalogId"])("rejects a user-supplied %s", (key) => {
|
|
23
|
-
expect(() =>
|
|
24
|
-
createHotUpdater({
|
|
25
|
-
clientAccess: "public",
|
|
26
|
-
database: createRuntimeDatabase(),
|
|
27
|
-
[key]: "user-controlled",
|
|
28
|
-
}),
|
|
29
|
-
).toThrow(`Remove ${key}`);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it("publishes only the supported runtime and database subpaths", () => {
|
|
33
|
-
const packageExports = packageJson.exports;
|
|
34
|
-
|
|
35
|
-
const databaseEntry = packageExports["./db"];
|
|
36
|
-
const hasRuntimeEntry = Object.hasOwn(packageExports, "./runtime");
|
|
37
|
-
|
|
38
|
-
expect(databaseEntry).toBeDefined();
|
|
39
|
-
expect(hasRuntimeEntry).toBe(false);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("types the options and the instance without the legacy API", () => {
|
|
43
|
-
expectTypeOf<ConfigInput>().not.toHaveProperty("authorityId");
|
|
44
|
-
expectTypeOf<ConfigInput>().not.toHaveProperty("catalogId");
|
|
45
|
-
expectTypeOf<keyof CreateHotUpdaterOptions>().toEqualTypeOf<
|
|
46
|
-
"clientAccess" | "database" | "plugins" | "storage"
|
|
47
|
-
>();
|
|
48
|
-
expectTypeOf<
|
|
49
|
-
CreateHotUpdaterOptions["clientAccess"]
|
|
50
|
-
>().toEqualTypeOf<ClientAccessPolicy>();
|
|
51
|
-
expectTypeOf<keyof RuntimeHotUpdaterAPI>().toEqualTypeOf<
|
|
52
|
-
"adapterName" | "api" | "core" | "flush" | "handlers"
|
|
53
|
-
>();
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("runs on an engine database without exposing its tooling", () => {
|
|
57
|
-
const storage = createStoragePlugin({
|
|
58
|
-
name: "contextlessTestStorage",
|
|
59
|
-
protocol: "s3",
|
|
60
|
-
get: async () => ({ response: null }),
|
|
61
|
-
getDownloadUrl: async () => ({
|
|
62
|
-
url: "https://assets.example.com/bundle.zip",
|
|
63
|
-
}),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const hotUpdater = createHotUpdater({
|
|
67
|
-
clientAccess: "public",
|
|
68
|
-
database: createRuntimeDatabase("contextlessTestDatabase"),
|
|
69
|
-
storage: [storage],
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(hotUpdater.adapterName).toBe("contextlessTestDatabase");
|
|
73
|
-
expect(Object.keys(hotUpdater).sort()).toEqual([
|
|
74
|
-
"adapterName",
|
|
75
|
-
"api",
|
|
76
|
-
"core",
|
|
77
|
-
"flush",
|
|
78
|
-
"handlers",
|
|
79
|
-
]);
|
|
80
|
-
expect(hotUpdater).not.toHaveProperty("authorityId");
|
|
81
|
-
expectTypeOf(hotUpdater).not.toHaveProperty("createMigrator");
|
|
82
|
-
expectTypeOf(hotUpdater.handlers.client)
|
|
83
|
-
.parameter(0)
|
|
84
|
-
.toEqualTypeOf<Request>();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it("refuses a database that is not on the storage engine", () => {
|
|
88
|
-
const legacy = { name: "legacy", models: {}, commit: async () => ({}) };
|
|
89
|
-
const remote = {
|
|
90
|
-
name: "standalone",
|
|
91
|
-
core: {},
|
|
92
|
-
fetchAdmin: async () => new Response(null),
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
for (const database of [legacy, remote, undefined]) {
|
|
96
|
-
expect(() =>
|
|
97
|
-
createHotUpdater({
|
|
98
|
-
clientAccess: "public",
|
|
99
|
-
database: database as unknown as CreateHotUpdaterOptions["database"],
|
|
100
|
-
}),
|
|
101
|
-
).toThrow(HotUpdaterConfigError);
|
|
102
|
-
}
|
|
103
|
-
expect(() =>
|
|
104
|
-
createHotUpdater({
|
|
105
|
-
clientAccess: "public",
|
|
106
|
-
database: remote as unknown as CreateHotUpdaterOptions["database"],
|
|
107
|
-
}),
|
|
108
|
-
).toThrow("standaloneRepository reaches a server's admin API");
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("answers 503 until the schema settings are written", async () => {
|
|
112
|
-
const error = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
113
|
-
try {
|
|
114
|
-
const hotUpdater = createHotUpdater({
|
|
115
|
-
clientAccess: "public",
|
|
116
|
-
database: await createFencedDatabase("kysely"),
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
const response = await hotUpdater.handlers.admin(
|
|
120
|
-
new Request("https://updates.example.com/channels"),
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
expect(response.status).toBe(503);
|
|
124
|
-
await expect(hotUpdater.core.listChannels()).rejects.toThrow(
|
|
125
|
-
"Hot Updater schema setting",
|
|
126
|
-
);
|
|
127
|
-
} finally {
|
|
128
|
-
error.mockRestore();
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("serves a fenced database once its settings are written", async () => {
|
|
133
|
-
const hotUpdater = createHotUpdater({
|
|
134
|
-
clientAccess: "public",
|
|
135
|
-
database: await createFencedDatabase("kysely", coreSettings),
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
await expect(hotUpdater.core.listChannels()).resolves.toEqual([]);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it.each(["s3", "https"])(
|
|
142
|
-
"rejects registered %s storage without getDownloadUrl",
|
|
143
|
-
(protocol) => {
|
|
144
|
-
const storage = createStoragePlugin({
|
|
145
|
-
name: "deployOnlyStorage",
|
|
146
|
-
protocol,
|
|
147
|
-
get: async () => ({ response: null }),
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
expect(() =>
|
|
151
|
-
createHotUpdater({
|
|
152
|
-
clientAccess: "public",
|
|
153
|
-
database: createRuntimeDatabase(),
|
|
154
|
-
storage: [storage],
|
|
155
|
-
}),
|
|
156
|
-
).toThrow(
|
|
157
|
-
'Storage plugin "deployOnlyStorage" does not implement getDownloadUrl.',
|
|
158
|
-
);
|
|
159
|
-
},
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
it("keeps the version route mounted on the client handler", async () => {
|
|
163
|
-
const hotUpdater = createHotUpdater({
|
|
164
|
-
clientAccess: "public",
|
|
165
|
-
database: createRuntimeDatabase(),
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
const response = await hotUpdater.handlers.client(
|
|
169
|
-
new Request("https://updates.example.com/version"),
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
expect(response.status).toBe(200);
|
|
173
|
-
await expect(response.json()).resolves.toEqual({
|
|
174
|
-
adminProtocol: 2,
|
|
175
|
-
infrastructureGeneration: 1,
|
|
176
|
-
version: HOT_UPDATER_SERVER_VERSION,
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it("rejects a missing client access policy", () => {
|
|
181
|
-
expect(() =>
|
|
182
|
-
createHotUpdater({
|
|
183
|
-
database: createRuntimeDatabase(),
|
|
184
|
-
} as unknown as CreateHotUpdaterOptions),
|
|
185
|
-
).toThrow(
|
|
186
|
-
'Set clientAccess to "public", or add a plugin that provides clientAuth.',
|
|
187
|
-
);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("rejects a clientAccess object, naming what replaced it", () => {
|
|
191
|
-
const withObject = (clientAccess: unknown) => () =>
|
|
192
|
-
createHotUpdater({
|
|
193
|
-
clientAccess: clientAccess as ClientAccessPolicy,
|
|
194
|
-
database: createRuntimeDatabase(),
|
|
195
|
-
});
|
|
196
|
-
const removed =
|
|
197
|
-
'clientAccess objects were removed in 1.0. Set clientAccess: "public", or add a plugin that provides clientAuth to plugins.';
|
|
198
|
-
|
|
199
|
-
expect(withObject({ type: "api-key", headerName: "x-client-key" })).toThrow(
|
|
200
|
-
removed,
|
|
201
|
-
);
|
|
202
|
-
expect(withObject({ type: "public" })).toThrow(removed);
|
|
203
|
-
expect(withObject("private")).toThrow(
|
|
204
|
-
'clientAccess must be "public"; to protect client routes, add a plugin that provides clientAuth to plugins.',
|
|
205
|
-
);
|
|
206
|
-
});
|
|
207
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import type { Bundle } from "@hot-updater/core";
|
|
2
|
-
import type {
|
|
3
|
-
EngineDatabase,
|
|
4
|
-
StoragePlugin,
|
|
5
|
-
StoragePluginWith,
|
|
6
|
-
} from "@hot-updater/plugin-core";
|
|
7
|
-
import { createStoragePlugin } from "@hot-updater/plugin-core";
|
|
8
|
-
import { createMemoryAdapter } from "@hot-updater/plugin-core/internal";
|
|
9
|
-
|
|
10
|
-
import type { SchemaSettings } from "./database/fence";
|
|
11
|
-
import { createEngineDatabase, toolingTargetOf } from "./db/coreDatabase";
|
|
12
|
-
import { migrateSchema } from "./db/schemaSettings";
|
|
13
|
-
import { apiKeys } from "./plugins/api-keys";
|
|
14
|
-
import { insights } from "./plugins/insights";
|
|
15
|
-
|
|
16
|
-
export const runtimeBundle: Bundle = {
|
|
17
|
-
id: "00000000-0000-0000-0000-000000000001",
|
|
18
|
-
platform: "ios",
|
|
19
|
-
gitCommitHash: null,
|
|
20
|
-
manifestStorageUri: "s3://test-bucket/bundles/bundle/manifest.json",
|
|
21
|
-
manifestFileHash: "manifest-hash",
|
|
22
|
-
assetBaseStorageUri: "s3://test-bucket/assets",
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const createRuntimeStorage = (
|
|
26
|
-
get: NonNullable<StoragePlugin["get"]> = async () => ({ response: null }),
|
|
27
|
-
getDownloadUrl?: StoragePlugin["getDownloadUrl"],
|
|
28
|
-
): StoragePluginWith<"get"> =>
|
|
29
|
-
createStoragePlugin({
|
|
30
|
-
name: "testStorage",
|
|
31
|
-
protocol: "s3",
|
|
32
|
-
get,
|
|
33
|
-
...(getDownloadUrl ? { getDownloadUrl } : {}),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/** An in-memory database on the storage engine, without the schema fence. */
|
|
37
|
-
export const createRuntimeDatabase = (
|
|
38
|
-
name = "testDatabase",
|
|
39
|
-
): EngineDatabase => ({
|
|
40
|
-
name,
|
|
41
|
-
adapter: createMemoryAdapter(),
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
/** Every table the runtime specs use: core's, Insights', and API keys'. */
|
|
45
|
-
const runtimeTables = toolingTargetOf([insights(), apiKeys()]).schema.tables;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* An in-memory database behind the schema fence, as a provider builds it:
|
|
49
|
-
* its tables and `settings` rows written first, or none at all.
|
|
50
|
-
*/
|
|
51
|
-
export const createFencedDatabase = async (
|
|
52
|
-
name: string,
|
|
53
|
-
settings?: SchemaSettings,
|
|
54
|
-
): Promise<EngineDatabase> => {
|
|
55
|
-
const adapter = createMemoryAdapter();
|
|
56
|
-
if (settings !== undefined) {
|
|
57
|
-
await migrateSchema(adapter, name, runtimeTables, settings);
|
|
58
|
-
}
|
|
59
|
-
return createEngineDatabase({ name, adapter });
|
|
60
|
-
};
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { type ChildProcess, spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import net from "node:net";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
import { afterEach, describe, expect, it } from "vitest";
|
|
7
|
-
|
|
8
|
-
import { HOT_UPDATER_SERVER_VERSION } from "./version";
|
|
9
|
-
|
|
10
|
-
/** Runs the built bundle from `tests/runtime`, with `plugins: []` and no Node APIs. */
|
|
11
|
-
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
-
|
|
13
|
-
for (const [command, ...args] of [
|
|
14
|
-
["deno", "--version"],
|
|
15
|
-
["pnpm", "exec", "wrangler", "--version"],
|
|
16
|
-
]) {
|
|
17
|
-
if (spawnSync(command!, args, { cwd: root, stdio: "ignore" }).status !== 0) {
|
|
18
|
-
throw new Error(
|
|
19
|
-
`${command === "pnpm" ? "wrangler" : command} is required for the server runtime bundle tests.`,
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const openPort = () =>
|
|
25
|
-
new Promise<number>((resolve, reject) => {
|
|
26
|
-
const server = net.createServer();
|
|
27
|
-
server.on("error", reject);
|
|
28
|
-
server.listen(0, "127.0.0.1", () => {
|
|
29
|
-
const { port } = server.address() as net.AddressInfo;
|
|
30
|
-
server.close(() => resolve(port));
|
|
31
|
-
});
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
let running: ChildProcess | undefined;
|
|
35
|
-
|
|
36
|
-
const serve = async (command: string, args: readonly string[]) => {
|
|
37
|
-
const port = await openPort();
|
|
38
|
-
const child = spawn(command, [...args, "--port", String(port)], {
|
|
39
|
-
cwd: root,
|
|
40
|
-
detached: true,
|
|
41
|
-
env: { ...process.env, NO_COLOR: "1" },
|
|
42
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
43
|
-
});
|
|
44
|
-
running = child;
|
|
45
|
-
const logs: string[] = [];
|
|
46
|
-
child.stdout.on("data", (chunk: Buffer) => logs.push(chunk.toString()));
|
|
47
|
-
child.stderr.on("data", (chunk: Buffer) => logs.push(chunk.toString()));
|
|
48
|
-
const url = `http://127.0.0.1:${port}`;
|
|
49
|
-
for (
|
|
50
|
-
let attempt = 0;
|
|
51
|
-
attempt < 120 && child.exitCode === null;
|
|
52
|
-
attempt += 1
|
|
53
|
-
) {
|
|
54
|
-
const ready = await fetch(`${url}/version`).then(
|
|
55
|
-
(response) => response.ok,
|
|
56
|
-
() => false,
|
|
57
|
-
);
|
|
58
|
-
if (ready) return url;
|
|
59
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
60
|
-
}
|
|
61
|
-
throw new Error(`${command} never served /version:\n${logs.join("")}`);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
describe.sequential("server bundle with plugins: []", () => {
|
|
65
|
-
afterEach(() => {
|
|
66
|
-
if (running?.pid !== undefined && running.exitCode === null) {
|
|
67
|
-
process.kill(-running.pid, "SIGTERM");
|
|
68
|
-
}
|
|
69
|
-
running = undefined;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it.each([
|
|
73
|
-
[
|
|
74
|
-
"Deno",
|
|
75
|
-
"deno",
|
|
76
|
-
[
|
|
77
|
-
"run",
|
|
78
|
-
"--allow-read",
|
|
79
|
-
"--allow-net",
|
|
80
|
-
"--allow-env",
|
|
81
|
-
"tests/runtime/deno-server.mjs",
|
|
82
|
-
],
|
|
83
|
-
],
|
|
84
|
-
[
|
|
85
|
-
"workerd",
|
|
86
|
-
"pnpm",
|
|
87
|
-
[
|
|
88
|
-
"exec",
|
|
89
|
-
"wrangler",
|
|
90
|
-
"dev",
|
|
91
|
-
"--config",
|
|
92
|
-
"tests/runtime/wrangler.runtime.jsonc",
|
|
93
|
-
"--local",
|
|
94
|
-
"--log-level",
|
|
95
|
-
"error",
|
|
96
|
-
],
|
|
97
|
-
],
|
|
98
|
-
] as const)(
|
|
99
|
-
"serves client routes on %s",
|
|
100
|
-
async (_runtime, command, args) => {
|
|
101
|
-
const url = await serve(command, args);
|
|
102
|
-
const version = await fetch(`${url}/version`);
|
|
103
|
-
await expect(version.json()).resolves.toEqual({
|
|
104
|
-
adminProtocol: 2,
|
|
105
|
-
infrastructureGeneration: 1,
|
|
106
|
-
version: HOT_UPDATER_SERVER_VERSION,
|
|
107
|
-
});
|
|
108
|
-
expect((await fetch(`${url}/not-a-route`)).status).toBe(404);
|
|
109
|
-
},
|
|
110
|
-
120_000,
|
|
111
|
-
);
|
|
112
|
-
});
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createStorageDownloadPath,
|
|
3
|
-
createStoragePlugin,
|
|
4
|
-
} from "@hot-updater/plugin-core";
|
|
5
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
6
|
-
|
|
7
|
-
import { createStorageAccess } from "./storageAccess";
|
|
8
|
-
|
|
9
|
-
describe("createStorageAccess", () => {
|
|
10
|
-
afterEach(() => {
|
|
11
|
-
vi.unstubAllGlobals();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("reads text through a matching storage implementation", async () => {
|
|
15
|
-
const get = vi.fn(
|
|
16
|
-
async (_storageUri: string) => new Response("manifest text"),
|
|
17
|
-
);
|
|
18
|
-
const storage = createStoragePlugin({
|
|
19
|
-
name: "r2Storage",
|
|
20
|
-
protocol: "r2",
|
|
21
|
-
get: async (input) => ({ response: await get(input.storageUri) }),
|
|
22
|
-
});
|
|
23
|
-
const { readStorageText } = createStorageAccess([storage]);
|
|
24
|
-
|
|
25
|
-
await expect(readStorageText("r2://assets/manifest.json")).resolves.toBe(
|
|
26
|
-
"manifest text",
|
|
27
|
-
);
|
|
28
|
-
expect(get).toHaveBeenCalledWith("r2://assets/manifest.json");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("reads direct HTTP storage when no plugin owns the protocol", async () => {
|
|
32
|
-
const fetchMock = vi.fn<typeof fetch>(
|
|
33
|
-
async () => new Response("manifest text"),
|
|
34
|
-
);
|
|
35
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
36
|
-
const { readStorageText } = createStorageAccess([]);
|
|
37
|
-
|
|
38
|
-
await expect(
|
|
39
|
-
readStorageText("https://assets.example.com/manifest.json"),
|
|
40
|
-
).resolves.toBe("manifest text");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("uses an unowned HTTPS URI directly as the download URL", async () => {
|
|
44
|
-
const { resolveFileUrl } = createStorageAccess([]);
|
|
45
|
-
const storageUri = "https://assets.example.com/bundle.zip";
|
|
46
|
-
|
|
47
|
-
await expect(resolveFileUrl(storageUri)).resolves.toBe(storageUri);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("lets a matching HTTPS storage own reads before direct fetch", async () => {
|
|
51
|
-
const fetchMock = vi.fn<typeof fetch>();
|
|
52
|
-
vi.stubGlobal("fetch", fetchMock);
|
|
53
|
-
const get = vi.fn(
|
|
54
|
-
async () => ({ response: new Response("owned manifest") }) as const,
|
|
55
|
-
);
|
|
56
|
-
const storage = createStoragePlugin({
|
|
57
|
-
name: "standaloneStorage",
|
|
58
|
-
protocol: "https",
|
|
59
|
-
get,
|
|
60
|
-
});
|
|
61
|
-
const { readStorageText } = createStorageAccess([storage]);
|
|
62
|
-
const storageUri = "https://storage.example.com/manifest.json";
|
|
63
|
-
|
|
64
|
-
await expect(readStorageText(storageUri)).resolves.toBe("owned manifest");
|
|
65
|
-
expect(get).toHaveBeenCalledWith({ storageUri });
|
|
66
|
-
expect(fetchMock).not.toHaveBeenCalled();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("lets a matching HTTPS storage resolve the download URL", async () => {
|
|
70
|
-
const getDownloadUrl = vi.fn(async () => ({
|
|
71
|
-
url: "https://cdn.example.com/bundle.zip",
|
|
72
|
-
}));
|
|
73
|
-
const storage = createStoragePlugin({
|
|
74
|
-
name: "standaloneStorage",
|
|
75
|
-
protocol: "https",
|
|
76
|
-
get: async () => ({ response: null }),
|
|
77
|
-
getDownloadUrl,
|
|
78
|
-
});
|
|
79
|
-
const { resolveFileUrl } = createStorageAccess([storage]);
|
|
80
|
-
const storageUri = "https://storage.example.com/bundle.zip";
|
|
81
|
-
|
|
82
|
-
await expect(resolveFileUrl(storageUri)).resolves.toBe(
|
|
83
|
-
"https://cdn.example.com/bundle.zip",
|
|
84
|
-
);
|
|
85
|
-
expect(getDownloadUrl).toHaveBeenCalledWith({ storageUri });
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("creates and serves a runtime-neutral delivery URL", async () => {
|
|
89
|
-
const storage = createStoragePlugin({
|
|
90
|
-
name: "r2Storage",
|
|
91
|
-
protocol: "r2",
|
|
92
|
-
get: vi.fn(
|
|
93
|
-
async () =>
|
|
94
|
-
({
|
|
95
|
-
response: new Response("bundle", {
|
|
96
|
-
headers: { "content-type": "application/zip" },
|
|
97
|
-
}),
|
|
98
|
-
}) as const,
|
|
99
|
-
),
|
|
100
|
-
getDownloadUrl: async ({ storageUri }) => ({
|
|
101
|
-
url: createStorageDownloadPath(storageUri, "test-signature"),
|
|
102
|
-
}),
|
|
103
|
-
});
|
|
104
|
-
const access = createStorageAccess([storage]);
|
|
105
|
-
|
|
106
|
-
const fileUrl = await access.resolveFileUrl("r2://bucket/bundle.zip");
|
|
107
|
-
expect(fileUrl).toMatch(/^\/storage\//);
|
|
108
|
-
const segments = fileUrl!.split("/");
|
|
109
|
-
const token = segments.at(-2)!;
|
|
110
|
-
const signature = segments.at(-1)!;
|
|
111
|
-
const response = await access.downloadStorageObject!(token, signature);
|
|
112
|
-
await expect(response?.text()).resolves.toBe("bundle");
|
|
113
|
-
await expect(
|
|
114
|
-
access.downloadStorageObject!(token, `${signature}tampered`),
|
|
115
|
-
).resolves.toBeNull();
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it("lets a CDN resolver bypass built-in server delivery", async () => {
|
|
119
|
-
const resolveUrl = vi.fn(async () => ({
|
|
120
|
-
url: "https://cdn.example.com/bundle.zip",
|
|
121
|
-
}));
|
|
122
|
-
const storage = createStoragePlugin({
|
|
123
|
-
name: "s3Storage",
|
|
124
|
-
protocol: "s3",
|
|
125
|
-
get: async () => ({ response: null }),
|
|
126
|
-
getDownloadUrl: resolveUrl,
|
|
127
|
-
});
|
|
128
|
-
const access = createStorageAccess([storage]);
|
|
129
|
-
|
|
130
|
-
await expect(access.resolveFileUrl("s3://bucket/bundle.zip")).resolves.toBe(
|
|
131
|
-
"https://cdn.example.com/bundle.zip",
|
|
132
|
-
);
|
|
133
|
-
expect(access.downloadStorageObject).toEqual(expect.any(Function));
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it("rejects ambiguous storage protocol ownership", () => {
|
|
137
|
-
const first = createStoragePlugin({
|
|
138
|
-
name: "firstR2Storage",
|
|
139
|
-
protocol: "r2",
|
|
140
|
-
get: async () => ({ response: null }),
|
|
141
|
-
});
|
|
142
|
-
const second = createStoragePlugin({
|
|
143
|
-
name: "secondR2Storage",
|
|
144
|
-
protocol: "r2",
|
|
145
|
-
get: async () => ({ response: null }),
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
expect(() => createStorageAccess([first, second])).toThrow(
|
|
149
|
-
"Multiple storage plugins handle protocol: r2",
|
|
150
|
-
);
|
|
151
|
-
});
|
|
152
|
-
});
|
package/src/storageAccess.ts
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
parseStorageDownloadPath,
|
|
3
|
-
type StoragePluginWith,
|
|
4
|
-
} from "@hot-updater/plugin-core";
|
|
5
|
-
|
|
6
|
-
const assertRemoteUrl = (value: string) => {
|
|
7
|
-
const protocol = new URL(value).protocol;
|
|
8
|
-
if (protocol !== "http:" && protocol !== "https:") {
|
|
9
|
-
throw new Error("Storage getDownloadUrl must resolve to an HTTP(S) URL.");
|
|
10
|
-
}
|
|
11
|
-
return value;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const getStorageProtocol = (storageUri: string) =>
|
|
15
|
-
new URL(storageUri).protocol.replace(":", "");
|
|
16
|
-
|
|
17
|
-
const isRemoteUrlProtocol = (protocol: string) =>
|
|
18
|
-
protocol === "http" || protocol === "https";
|
|
19
|
-
|
|
20
|
-
const resolveDownloadPath = (value: string, storageUri: string) => {
|
|
21
|
-
const parsed = parseStorageDownloadPath(value);
|
|
22
|
-
if (!parsed || parsed.storageUri !== storageUri) {
|
|
23
|
-
throw new Error(
|
|
24
|
-
"Storage getDownloadUrl must return an HTTP(S) URL or a valid storage download path.",
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const tokensEqual = (left: string, right: string) => {
|
|
31
|
-
const leftBytes = new TextEncoder().encode(left);
|
|
32
|
-
const rightBytes = new TextEncoder().encode(right);
|
|
33
|
-
let difference = leftBytes.length ^ rightBytes.length;
|
|
34
|
-
const length = Math.max(leftBytes.length, rightBytes.length);
|
|
35
|
-
for (let index = 0; index < length; index += 1) {
|
|
36
|
-
difference |= (leftBytes[index] ?? 0) ^ (rightBytes[index] ?? 0);
|
|
37
|
-
}
|
|
38
|
-
return difference === 0;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export const createStorageAccess = (
|
|
42
|
-
storagePlugins: StoragePluginWith<"get">[],
|
|
43
|
-
) => {
|
|
44
|
-
const protocols = new Set<string>();
|
|
45
|
-
for (const storage of storagePlugins) {
|
|
46
|
-
if (protocols.has(storage.protocol)) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`Multiple storage plugins handle protocol: ${storage.protocol}`,
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
protocols.add(storage.protocol);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const findStorage = (protocol: string) =>
|
|
55
|
-
storagePlugins.find((item) => item.protocol === protocol);
|
|
56
|
-
|
|
57
|
-
const readStorageResponse = async (
|
|
58
|
-
storageUri: string,
|
|
59
|
-
): Promise<Response | null> => {
|
|
60
|
-
const protocol = getStorageProtocol(storageUri);
|
|
61
|
-
const storage = findStorage(protocol);
|
|
62
|
-
if (storage) return (await storage.get({ storageUri })).response;
|
|
63
|
-
|
|
64
|
-
if (isRemoteUrlProtocol(protocol)) {
|
|
65
|
-
const response = await fetch(storageUri);
|
|
66
|
-
return response.ok ? response : null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
throw new Error(`No storage plugin for protocol: ${protocol}`);
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const resolveFileUrl = async (
|
|
73
|
-
storageUri: string | null,
|
|
74
|
-
): Promise<string | null> => {
|
|
75
|
-
if (!storageUri) return null;
|
|
76
|
-
|
|
77
|
-
const protocol = getStorageProtocol(storageUri);
|
|
78
|
-
const storage = findStorage(protocol);
|
|
79
|
-
if (!storage) {
|
|
80
|
-
if (isRemoteUrlProtocol(protocol)) return storageUri;
|
|
81
|
-
throw new Error(`No storage plugin for protocol: ${protocol}`);
|
|
82
|
-
}
|
|
83
|
-
if (!storage.getDownloadUrl) {
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Storage plugin "${storage.name}" does not implement getDownloadUrl.`,
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
const { url: downloadUrl } = await storage.getDownloadUrl({ storageUri });
|
|
89
|
-
try {
|
|
90
|
-
return assertRemoteUrl(downloadUrl);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
if (/^[a-z][a-z\d+.-]*:/i.test(downloadUrl)) throw error;
|
|
93
|
-
}
|
|
94
|
-
return resolveDownloadPath(downloadUrl, storageUri);
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
const readStorageText = async (
|
|
98
|
-
storageUri: string,
|
|
99
|
-
): Promise<string | null> => {
|
|
100
|
-
const response = await readStorageResponse(storageUri);
|
|
101
|
-
return response?.text() ?? null;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const downloadStorageObject = storagePlugins.some(
|
|
105
|
-
(storage) => storage.getDownloadUrl !== undefined,
|
|
106
|
-
)
|
|
107
|
-
? async (
|
|
108
|
-
storageUriToken: string,
|
|
109
|
-
encodedSignature: string,
|
|
110
|
-
): Promise<Response | null> => {
|
|
111
|
-
const requestedPath = `/storage/${storageUriToken}/${encodedSignature}`;
|
|
112
|
-
const requested = parseStorageDownloadPath(requestedPath);
|
|
113
|
-
if (!requested) return null;
|
|
114
|
-
let storage: StoragePluginWith<"get"> | undefined;
|
|
115
|
-
try {
|
|
116
|
-
storage = findStorage(getStorageProtocol(requested.storageUri));
|
|
117
|
-
} catch {
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
if (!storage?.getDownloadUrl) return null;
|
|
121
|
-
const { url: downloadUrl } = await storage.getDownloadUrl({
|
|
122
|
-
storageUri: requested.storageUri,
|
|
123
|
-
});
|
|
124
|
-
try {
|
|
125
|
-
new URL(downloadUrl);
|
|
126
|
-
return null;
|
|
127
|
-
} catch {
|
|
128
|
-
if (/^[a-z][a-z\d+.-]*:/i.test(downloadUrl)) return null;
|
|
129
|
-
}
|
|
130
|
-
if (!tokensEqual(downloadUrl, requestedPath)) return null;
|
|
131
|
-
return (await storage.get({ storageUri: requested.storageUri }))
|
|
132
|
-
.response;
|
|
133
|
-
}
|
|
134
|
-
: undefined;
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
downloadStorageObject,
|
|
138
|
-
readStorageText,
|
|
139
|
-
resolveFileUrl,
|
|
140
|
-
};
|
|
141
|
-
};
|