@hot-updater/server 0.30.11 → 0.31.0

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.
Files changed (165) hide show
  1. package/dist/_virtual/_rolldown/runtime.cjs +25 -0
  2. package/dist/_virtual/_rolldown/runtime.mjs +6 -0
  3. package/dist/adapters/drizzle.cjs +6 -9
  4. package/dist/adapters/drizzle.d.cts +8 -1
  5. package/dist/adapters/drizzle.d.mts +8 -1
  6. package/dist/adapters/drizzle.mjs +5 -2
  7. package/dist/adapters/kysely.cjs +7 -9
  8. package/dist/adapters/kysely.d.cts +14 -1
  9. package/dist/adapters/kysely.d.mts +14 -1
  10. package/dist/adapters/kysely.mjs +6 -2
  11. package/dist/adapters/mongodb.cjs +7 -9
  12. package/dist/adapters/mongodb.d.cts +9 -1
  13. package/dist/adapters/mongodb.d.mts +9 -1
  14. package/dist/adapters/mongodb.mjs +5 -2
  15. package/dist/adapters/prisma.cjs +6 -9
  16. package/dist/adapters/prisma.d.cts +8 -1
  17. package/dist/adapters/prisma.d.mts +8 -1
  18. package/dist/adapters/prisma.mjs +5 -2
  19. package/dist/db/createBundleDiff.cjs +166 -0
  20. package/dist/db/createBundleDiff.d.cts +20 -0
  21. package/dist/db/createBundleDiff.d.mts +20 -0
  22. package/dist/db/createBundleDiff.mjs +161 -0
  23. package/dist/db/index.cjs +15 -16
  24. package/dist/db/index.d.cts +5 -4
  25. package/dist/db/index.d.mts +5 -4
  26. package/dist/db/index.mjs +14 -16
  27. package/dist/db/ormCore.cjs +173 -65
  28. package/dist/db/ormCore.d.cts +100 -34
  29. package/dist/db/ormCore.d.mts +100 -34
  30. package/dist/db/ormCore.mjs +171 -64
  31. package/dist/db/pluginCore.cjs +37 -3
  32. package/dist/db/pluginCore.mjs +36 -3
  33. package/dist/db/schemaEnhancements.cjs +171 -0
  34. package/dist/db/schemaEnhancements.mjs +167 -0
  35. package/dist/db/types.cjs +6 -0
  36. package/dist/db/types.d.cts +19 -7
  37. package/dist/db/types.d.mts +22 -10
  38. package/dist/db/types.mjs +6 -1
  39. package/dist/db/updateArtifacts.cjs +127 -0
  40. package/dist/db/updateArtifacts.mjs +125 -0
  41. package/dist/handler.cjs +61 -5
  42. package/dist/handler.d.cts +2 -2
  43. package/dist/handler.d.mts +5 -5
  44. package/dist/handler.mjs +59 -5
  45. package/dist/index.cjs +2 -0
  46. package/dist/index.d.cts +3 -2
  47. package/dist/index.d.mts +3 -2
  48. package/dist/index.mjs +2 -1
  49. package/dist/node.d.cts +0 -1
  50. package/dist/node.d.mts +0 -1
  51. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/_u64.cjs +112 -0
  52. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/_u64.mjs +108 -0
  53. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/cryptoNode.cjs +22 -0
  54. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/cryptoNode.mjs +18 -0
  55. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/sha3.cjs +219 -0
  56. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/sha3.mjs +214 -0
  57. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/utils.cjs +275 -0
  58. package/dist/node_modules/.pnpm/@noble_hashes@1.8.0/node_modules/@noble/hashes/utils.mjs +270 -0
  59. package/dist/node_modules/.pnpm/@paralleldrive_cuid2@2.3.1/node_modules/@paralleldrive/cuid2/index.cjs +17 -0
  60. package/dist/node_modules/.pnpm/@paralleldrive_cuid2@2.3.1/node_modules/@paralleldrive/cuid2/index.mjs +13 -0
  61. package/dist/node_modules/.pnpm/@paralleldrive_cuid2@2.3.1/node_modules/@paralleldrive/cuid2/src/index.cjs +69 -0
  62. package/dist/node_modules/.pnpm/@paralleldrive_cuid2@2.3.1/node_modules/@paralleldrive/cuid2/src/index.mjs +65 -0
  63. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/column.cjs +52 -0
  64. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/column.mjs +52 -0
  65. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/entity.cjs +16 -0
  66. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/entity.mjs +15 -0
  67. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/pg-core/columns/enum.cjs +7 -0
  68. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/pg-core/columns/enum.mjs +7 -0
  69. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/expressions/conditions.cjs +92 -0
  70. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/expressions/conditions.mjs +78 -0
  71. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/expressions/select.cjs +11 -0
  72. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/expressions/select.mjs +10 -0
  73. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/sql.cjs +383 -0
  74. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/sql/sql.mjs +366 -0
  75. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/subquery.cjs +17 -0
  76. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/subquery.mjs +17 -0
  77. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/table.cjs +60 -0
  78. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/table.mjs +59 -0
  79. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/table.utils.cjs +4 -0
  80. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/table.utils.mjs +4 -0
  81. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/tracing.cjs +6 -0
  82. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/tracing.mjs +6 -0
  83. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/view-common.cjs +4 -0
  84. package/dist/node_modules/.pnpm/drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pglite@0.2.17_@_31f44b782f9321d71f3ce9d35aa1edf7/node_modules/drizzle-orm/view-common.mjs +4 -0
  85. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/drizzle/index.cjs +383 -0
  86. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/drizzle/index.d.cts +12 -0
  87. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/drizzle/index.d.mts +12 -0
  88. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/drizzle/index.mjs +383 -0
  89. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/kysely/index.cjs +4 -0
  90. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/kysely/index.mjs +5 -0
  91. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/prisma/index.cjs +339 -0
  92. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/prisma/index.d.cts +70 -0
  93. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/prisma/index.d.mts +70 -0
  94. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/adapters/prisma/index.mjs +339 -0
  95. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-7PZK4ONR.cjs +57 -0
  96. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-7PZK4ONR.mjs +56 -0
  97. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-C6OTUURW.cjs +330 -0
  98. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-C6OTUURW.mjs +326 -0
  99. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-CHTIKPQU.cjs +166 -0
  100. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-CHTIKPQU.mjs +163 -0
  101. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-GUE4GMNC.cjs +14 -0
  102. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-GUE4GMNC.mjs +13 -0
  103. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-LHHP6UVP.cjs +24 -0
  104. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-LHHP6UVP.mjs +24 -0
  105. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-LVCPMTAT.cjs +1190 -0
  106. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-LVCPMTAT.mjs +1189 -0
  107. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-PK2W2SQ7.cjs +197 -0
  108. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-PK2W2SQ7.mjs +197 -0
  109. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-ZEQMAIFI.cjs +410 -0
  110. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-ZEQMAIFI.mjs +400 -0
  111. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-ZOCGSAWS.cjs +213 -0
  112. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-ZOCGSAWS.mjs +212 -0
  113. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/create-tg0451Y_.d.cts +285 -0
  114. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/create-tg0451Y_.d.mts +285 -0
  115. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index-CMqePMTF.d.cts +45 -0
  116. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index-CMqePMTF.d.mts +45 -0
  117. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index.cjs +69 -0
  118. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index.d.cts +49 -0
  119. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index.d.mts +49 -0
  120. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/index.mjs +67 -0
  121. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/query/index.d.cts +156 -0
  122. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/query/index.d.mts +156 -0
  123. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/schema/index.cjs +1 -0
  124. package/dist/node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/schema/index.mjs +2 -0
  125. package/dist/{package.cjs → packages/server/package.cjs} +1 -1
  126. package/dist/{package.mjs → packages/server/package.mjs} +1 -1
  127. package/dist/runtime.cjs +13 -13
  128. package/dist/runtime.d.cts +4 -4
  129. package/dist/runtime.d.mts +4 -4
  130. package/dist/runtime.mjs +12 -13
  131. package/dist/schema/v0_21_0.cjs +16 -15
  132. package/dist/schema/v0_21_0.mjs +3 -2
  133. package/dist/schema/v0_29_0.cjs +18 -17
  134. package/dist/schema/v0_29_0.mjs +3 -2
  135. package/dist/schema/v0_31_0.cjs +48 -0
  136. package/dist/schema/v0_31_0.mjs +48 -0
  137. package/dist/storageAccess.cjs +44 -0
  138. package/dist/storageAccess.mjs +44 -0
  139. package/dist/version.cjs +1 -1
  140. package/dist/version.mjs +1 -1
  141. package/package.json +15 -7
  142. package/src/adapters/drizzle.ts +15 -1
  143. package/src/adapters/kysely.ts +24 -1
  144. package/src/adapters/mongodb.ts +19 -1
  145. package/src/adapters/prisma.ts +15 -1
  146. package/src/db/createBundleDiff.spec.ts +402 -0
  147. package/src/db/createBundleDiff.ts +375 -0
  148. package/src/db/index.spec.ts +528 -27
  149. package/src/db/index.ts +22 -36
  150. package/src/db/ormCore.ts +308 -75
  151. package/src/db/pluginCore.spec.ts +385 -0
  152. package/src/db/pluginCore.ts +45 -4
  153. package/src/db/schemaEnhancements.ts +460 -0
  154. package/src/db/types.ts +38 -7
  155. package/src/db/updateArtifacts.ts +388 -0
  156. package/src/handler-standalone.integration.spec.ts +1 -0
  157. package/src/handler.spec.ts +121 -0
  158. package/src/handler.ts +117 -5
  159. package/src/runtime.spec.ts +287 -55
  160. package/src/runtime.ts +21 -37
  161. package/src/schema/v0_21_0.ts +1 -1
  162. package/src/schema/v0_29_0.ts +1 -1
  163. package/src/schema/v0_31_0.ts +58 -0
  164. package/src/storageAccess.spec.ts +57 -0
  165. package/src/storageAccess.ts +90 -0
@@ -0,0 +1,48 @@
1
+ import { column, idColumn, schema, table } from "../node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/chunk-ZEQMAIFI.mjs";
2
+ import "../node_modules/.pnpm/fumadb@0.2.2_drizzle-orm@0.44.7_@cloudflare_workers-types@4.20260313.1_@electric-sql_pg_c72c8c754becd21f6d6662e8fbd28e7f/node_modules/fumadb/dist/schema/index.mjs";
3
+ //#region src/schema/v0_31_0.ts
4
+ const v0_31_0 = schema({
5
+ version: "0.31.0",
6
+ tables: {
7
+ bundles: table("bundles", {
8
+ id: idColumn("id", "uuid"),
9
+ platform: column("platform", "string"),
10
+ should_force_update: column("should_force_update", "bool"),
11
+ enabled: column("enabled", "bool"),
12
+ file_hash: column("file_hash", "string"),
13
+ git_commit_hash: column("git_commit_hash", "string").nullable(),
14
+ message: column("message", "string").nullable(),
15
+ channel: column("channel", "string").defaultTo("production"),
16
+ storage_uri: column("storage_uri", "string"),
17
+ target_app_version: column("target_app_version", "string").nullable(),
18
+ fingerprint_hash: column("fingerprint_hash", "string").nullable(),
19
+ metadata: column("metadata", "json"),
20
+ manifest_storage_uri: column("manifest_storage_uri", "string").nullable(),
21
+ manifest_file_hash: column("manifest_file_hash", "string").nullable(),
22
+ asset_base_storage_uri: column("asset_base_storage_uri", "string").nullable(),
23
+ rollout_cohort_count: column("rollout_cohort_count", "integer").defaultTo(1e3),
24
+ target_cohorts: column("target_cohorts", "json").nullable()
25
+ }),
26
+ bundle_patches: table("bundle_patches", {
27
+ id: idColumn("id", "varchar(255)"),
28
+ bundle_id: column("bundle_id", "uuid"),
29
+ base_bundle_id: column("base_bundle_id", "uuid"),
30
+ base_file_hash: column("base_file_hash", "string"),
31
+ patch_file_hash: column("patch_file_hash", "string"),
32
+ patch_storage_uri: column("patch_storage_uri", "string"),
33
+ order_index: column("order_index", "integer").defaultTo(0)
34
+ })
35
+ },
36
+ relations: { bundle_patches: (builder) => ({
37
+ bundle: builder.one("bundles", ["bundle_id", "id"]).imply("patches").foreignKey({
38
+ name: "bundle_patches_bundle_id_fk",
39
+ onDelete: "CASCADE"
40
+ }),
41
+ baseBundle: builder.one("bundles", ["base_bundle_id", "id"]).imply("baseForPatches").foreignKey({
42
+ name: "bundle_patches_base_bundle_id_fk",
43
+ onDelete: "CASCADE"
44
+ })
45
+ }) }
46
+ });
47
+ //#endregion
48
+ export { v0_31_0 };
@@ -0,0 +1,44 @@
1
+ //#region src/storageAccess.ts
2
+ const assertRemoteDownloadUrl = (fileUrl) => {
3
+ try {
4
+ const protocol = new URL(fileUrl).protocol.replace(":", "");
5
+ if (protocol === "http" || protocol === "https") return fileUrl;
6
+ } catch {}
7
+ throw new Error("Storage plugin returned a local file path; runtime update checks require an HTTP(S) download URL.");
8
+ };
9
+ const getStorageProtocol = (storageUri) => new URL(storageUri).protocol.replace(":", "");
10
+ const isRemoteUrlProtocol = (protocol) => protocol === "http" || protocol === "https";
11
+ const createStorageAccess = (storagePlugins) => {
12
+ const findStoragePlugin = (protocol) => {
13
+ return storagePlugins.find((item) => item.supportedProtocol === protocol);
14
+ };
15
+ const resolveFileUrl = async (storageUri, context) => {
16
+ if (!storageUri) return null;
17
+ const protocol = getStorageProtocol(storageUri);
18
+ const plugin = findStoragePlugin(protocol);
19
+ if (plugin) {
20
+ const { fileUrl } = await plugin.profiles.runtime.getDownloadUrl(storageUri, context);
21
+ if (!fileUrl) throw new Error("Storage plugin returned empty fileUrl");
22
+ return assertRemoteDownloadUrl(fileUrl);
23
+ }
24
+ if (isRemoteUrlProtocol(protocol)) return storageUri;
25
+ throw new Error(`No storage plugin for protocol: ${protocol}`);
26
+ };
27
+ const readStorageText = async (storageUri, context) => {
28
+ const protocol = getStorageProtocol(storageUri);
29
+ const plugin = findStoragePlugin(protocol);
30
+ if (plugin) return plugin.profiles.runtime.readText(storageUri, context);
31
+ if (isRemoteUrlProtocol(protocol)) {
32
+ const response = await fetch(storageUri);
33
+ if (!response.ok) return null;
34
+ return response.text();
35
+ }
36
+ throw new Error(`No storage plugin for protocol: ${protocol}`);
37
+ };
38
+ return {
39
+ readStorageText,
40
+ resolveFileUrl
41
+ };
42
+ };
43
+ //#endregion
44
+ exports.createStorageAccess = createStorageAccess;
@@ -0,0 +1,44 @@
1
+ //#region src/storageAccess.ts
2
+ const assertRemoteDownloadUrl = (fileUrl) => {
3
+ try {
4
+ const protocol = new URL(fileUrl).protocol.replace(":", "");
5
+ if (protocol === "http" || protocol === "https") return fileUrl;
6
+ } catch {}
7
+ throw new Error("Storage plugin returned a local file path; runtime update checks require an HTTP(S) download URL.");
8
+ };
9
+ const getStorageProtocol = (storageUri) => new URL(storageUri).protocol.replace(":", "");
10
+ const isRemoteUrlProtocol = (protocol) => protocol === "http" || protocol === "https";
11
+ const createStorageAccess = (storagePlugins) => {
12
+ const findStoragePlugin = (protocol) => {
13
+ return storagePlugins.find((item) => item.supportedProtocol === protocol);
14
+ };
15
+ const resolveFileUrl = async (storageUri, context) => {
16
+ if (!storageUri) return null;
17
+ const protocol = getStorageProtocol(storageUri);
18
+ const plugin = findStoragePlugin(protocol);
19
+ if (plugin) {
20
+ const { fileUrl } = await plugin.profiles.runtime.getDownloadUrl(storageUri, context);
21
+ if (!fileUrl) throw new Error("Storage plugin returned empty fileUrl");
22
+ return assertRemoteDownloadUrl(fileUrl);
23
+ }
24
+ if (isRemoteUrlProtocol(protocol)) return storageUri;
25
+ throw new Error(`No storage plugin for protocol: ${protocol}`);
26
+ };
27
+ const readStorageText = async (storageUri, context) => {
28
+ const protocol = getStorageProtocol(storageUri);
29
+ const plugin = findStoragePlugin(protocol);
30
+ if (plugin) return plugin.profiles.runtime.readText(storageUri, context);
31
+ if (isRemoteUrlProtocol(protocol)) {
32
+ const response = await fetch(storageUri);
33
+ if (!response.ok) return null;
34
+ return response.text();
35
+ }
36
+ throw new Error(`No storage plugin for protocol: ${protocol}`);
37
+ };
38
+ return {
39
+ readStorageText,
40
+ resolveFileUrl
41
+ };
42
+ };
43
+ //#endregion
44
+ export { createStorageAccess };
package/dist/version.cjs CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region src/version.ts
2
- const HOT_UPDATER_SERVER_VERSION = require("./package.cjs").version;
2
+ const HOT_UPDATER_SERVER_VERSION = require("./packages/server/package.cjs").version;
3
3
  //#endregion
4
4
  exports.HOT_UPDATER_SERVER_VERSION = HOT_UPDATER_SERVER_VERSION;
package/dist/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { version } from "./package.mjs";
1
+ import { version } from "./packages/server/package.mjs";
2
2
  //#region src/version.ts
3
3
  const HOT_UPDATER_SERVER_VERSION = version;
4
4
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/server",
3
- "version": "0.30.11",
3
+ "version": "0.31.0",
4
4
  "type": "module",
5
5
  "description": "React Native OTA solution for self-hosted",
6
6
  "sideEffects": false,
@@ -51,23 +51,31 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "fumadb": "0.2.2",
54
+ "kysely": "0.28.16",
55
+ "mongodb": "6.20.0",
54
56
  "rou3": "0.7.9",
55
57
  "semver": "^7.7.2",
56
- "@hot-updater/core": "0.30.11",
57
- "@hot-updater/js": "0.30.11",
58
- "@hot-updater/plugin-core": "0.30.11"
58
+ "@hot-updater/bsdiff": "0.31.0",
59
+ "@hot-updater/core": "0.31.0",
60
+ "@hot-updater/plugin-core": "0.31.0",
61
+ "@hot-updater/js": "0.31.0"
59
62
  },
60
63
  "devDependencies": {
61
64
  "@electric-sql/pglite": "0.2.17",
62
65
  "@types/node": "^20",
63
66
  "@types/semver": "^7.5.8",
64
67
  "execa": "9.5.2",
65
- "kysely": "0.28.16",
66
68
  "kysely-pglite-dialect": "1.2.0",
67
69
  "msw": "^2.7.0",
68
70
  "uuidv7": "^1.0.2",
69
- "@hot-updater/standalone": "0.30.11",
70
- "@hot-updater/test-utils": "0.30.11"
71
+ "@hot-updater/standalone": "0.31.0",
72
+ "@hot-updater/test-utils": "0.31.0"
73
+ },
74
+ "inlinedDependencies": {
75
+ "@noble/hashes": "1.8.0",
76
+ "@paralleldrive/cuid2": "2.3.1",
77
+ "drizzle-orm": "0.44.7",
78
+ "fumadb": "0.2.2"
71
79
  },
72
80
  "scripts": {
73
81
  "build": "tsdown",
@@ -1 +1,15 @@
1
- export * from "fumadb/adapters/drizzle";
1
+ import { drizzleAdapter as fumadbDrizzleAdapter } from "fumadb/adapters/drizzle";
2
+
3
+ import type { ORMDatabaseAdapter } from "../db/types";
4
+
5
+ export type DrizzleConfig = Parameters<typeof fumadbDrizzleAdapter>[0];
6
+
7
+ export const drizzleAdapter = (config: DrizzleConfig): ORMDatabaseAdapter =>
8
+ Object.assign(
9
+ fumadbDrizzleAdapter(
10
+ config as unknown as Parameters<typeof fumadbDrizzleAdapter>[0],
11
+ ) as unknown as ORMDatabaseAdapter,
12
+ {
13
+ provider: config.provider,
14
+ },
15
+ );
@@ -1 +1,24 @@
1
- export * from "fumadb/adapters/kysely";
1
+ import { kyselyAdapter as fumadbKyselyAdapter } from "fumadb/adapters/kysely";
2
+
3
+ import type { ORMDatabaseAdapter } from "../db/types";
4
+
5
+ export type RelationMode = import("fumadb").RelationMode;
6
+ export type SQLProvider = import("fumadb").SQLProvider;
7
+
8
+ export interface KyselyConfig<TDatabase extends object = object> {
9
+ db: TDatabase;
10
+ provider: SQLProvider;
11
+ relationMode?: RelationMode;
12
+ }
13
+
14
+ export const kyselyAdapter = <TDatabase extends object>(
15
+ config: KyselyConfig<TDatabase>,
16
+ ): ORMDatabaseAdapter =>
17
+ Object.assign(
18
+ fumadbKyselyAdapter(
19
+ config as unknown as Parameters<typeof fumadbKyselyAdapter>[0],
20
+ ) as unknown as ORMDatabaseAdapter,
21
+ {
22
+ provider: config.provider,
23
+ },
24
+ );
@@ -1 +1,19 @@
1
- export * from "fumadb/adapters/mongodb";
1
+ import { mongoAdapter as fumadbMongoAdapter } from "fumadb/adapters/mongodb";
2
+
3
+ import type { ORMDatabaseAdapter } from "../db/types";
4
+
5
+ export interface MongoDBConfig<TClient extends object = object> {
6
+ client: TClient;
7
+ }
8
+
9
+ export const mongoAdapter = <TClient extends object>(
10
+ options: MongoDBConfig<TClient>,
11
+ ): ORMDatabaseAdapter =>
12
+ Object.assign(
13
+ fumadbMongoAdapter(
14
+ options as unknown as Parameters<typeof fumadbMongoAdapter>[0],
15
+ ) as unknown as ORMDatabaseAdapter,
16
+ {
17
+ provider: "mongodb",
18
+ },
19
+ );
@@ -1 +1,15 @@
1
- export * from "fumadb/adapters/prisma";
1
+ import { prismaAdapter as fumadbPrismaAdapter } from "fumadb/adapters/prisma";
2
+
3
+ import type { ORMDatabaseAdapter } from "../db/types";
4
+
5
+ export type PrismaConfig = Parameters<typeof fumadbPrismaAdapter>[0];
6
+
7
+ export const prismaAdapter = (config: PrismaConfig): ORMDatabaseAdapter =>
8
+ Object.assign(
9
+ fumadbPrismaAdapter(
10
+ config as unknown as Parameters<typeof fumadbPrismaAdapter>[0],
11
+ ) as unknown as ORMDatabaseAdapter,
12
+ {
13
+ provider: config.provider,
14
+ },
15
+ );
@@ -0,0 +1,402 @@
1
+ import fs from "node:fs/promises";
2
+ import { brotliCompressSync } from "node:zlib";
3
+
4
+ import type {
5
+ Bundle,
6
+ DatabasePlugin,
7
+ NodeStoragePlugin,
8
+ NodeStorageProfile,
9
+ } from "@hot-updater/plugin-core";
10
+ import { afterEach, describe, expect, it, vi } from "vitest";
11
+
12
+ vi.mock("@hot-updater/bsdiff", () => ({
13
+ hdiff: vi.fn(async () => new Uint8Array([1, 2, 3, 4])),
14
+ }));
15
+
16
+ import { createBundleDiff } from "./createBundleDiff";
17
+
18
+ const createBundle = (id: string, overrides: Partial<Bundle> = {}): Bundle => ({
19
+ channel: "production",
20
+ enabled: true,
21
+ fileHash: `${id}-file-hash`,
22
+ fingerprintHash: null,
23
+ gitCommitHash: null,
24
+ id,
25
+ message: id,
26
+ assetBaseStorageUri: `s3://test-bucket/releases/${id}/files`,
27
+ manifestStorageUri: `s3://test-bucket/releases/${id}/manifest.json`,
28
+ metadata: {},
29
+ platform: "ios",
30
+ shouldForceUpdate: false,
31
+ storageUri: `s3://test-bucket/releases/${id}/bundle.zip`,
32
+ targetAppVersion: "1.0.0",
33
+ ...overrides,
34
+ });
35
+
36
+ const createDatabasePlugin = (
37
+ bundles: Map<string, Bundle>,
38
+ ): DatabasePlugin => ({
39
+ name: "mockDatabase",
40
+ async appendBundle() {},
41
+ async commitBundle() {},
42
+ async deleteBundle() {},
43
+ async getBundleById(bundleId) {
44
+ return bundles.get(bundleId) ?? null;
45
+ },
46
+ async getBundles() {
47
+ return {
48
+ data: Array.from(bundles.values()),
49
+ pagination: {
50
+ currentPage: 1,
51
+ hasNextPage: false,
52
+ hasPreviousPage: false,
53
+ total: bundles.size,
54
+ totalPages: 1,
55
+ },
56
+ };
57
+ },
58
+ async getChannels() {
59
+ return ["production"];
60
+ },
61
+ async onUnmount() {},
62
+ async updateBundle(bundleId, nextBundle) {
63
+ const currentBundle = bundles.get(bundleId);
64
+ if (!currentBundle) {
65
+ return;
66
+ }
67
+ bundles.set(bundleId, {
68
+ ...currentBundle,
69
+ ...nextBundle,
70
+ metadata: {
71
+ ...currentBundle.metadata,
72
+ ...nextBundle.metadata,
73
+ },
74
+ });
75
+ },
76
+ });
77
+
78
+ const createStoragePlugin = (
79
+ upload: NodeStorageProfile["upload"],
80
+ ): NodeStoragePlugin => ({
81
+ name: "mockStorage",
82
+ supportedProtocol: "s3",
83
+ profiles: {
84
+ node: {
85
+ async delete() {},
86
+ async downloadFile(storageUri, filePath) {
87
+ const storageUrl = new URL(storageUri);
88
+ const response = await fetch(
89
+ `https://assets.example.com${storageUrl.pathname}`,
90
+ );
91
+ await fs.writeFile(
92
+ filePath,
93
+ new Uint8Array(await response.arrayBuffer()),
94
+ );
95
+ },
96
+ upload,
97
+ },
98
+ },
99
+ });
100
+
101
+ describe("createBundleDiff", () => {
102
+ afterEach(() => {
103
+ vi.restoreAllMocks();
104
+ });
105
+
106
+ it("uploads a Hermes patch and stores patch metadata on the target bundle", async () => {
107
+ const baseBundle = createBundle("00000000-0000-0000-0000-000000000001", {
108
+ message: "base",
109
+ });
110
+ const targetBundle = createBundle("00000000-0000-0000-0000-000000000002", {
111
+ message: "target",
112
+ });
113
+ const bundles = new Map([
114
+ [baseBundle.id, baseBundle],
115
+ [targetBundle.id, targetBundle],
116
+ ]);
117
+ const upload = vi.fn<NodeStorageProfile["upload"]>(
118
+ async (key, filePath) => ({
119
+ storageUri: `s3://test-bucket/${key}/${filePath.split("/").pop()}`,
120
+ }),
121
+ );
122
+
123
+ vi.stubGlobal(
124
+ "fetch",
125
+ vi.fn(async (input: Request | URL | string) => {
126
+ const url = String(input);
127
+
128
+ if (url.endsWith(`${baseBundle.id}/manifest.json`)) {
129
+ return new Response(
130
+ JSON.stringify({
131
+ assets: {
132
+ "index.ios.bundle": {
133
+ fileHash: "hash-old",
134
+ },
135
+ },
136
+ bundleId: baseBundle.id,
137
+ }),
138
+ );
139
+ }
140
+
141
+ if (url.endsWith(`${targetBundle.id}/manifest.json`)) {
142
+ return new Response(
143
+ JSON.stringify({
144
+ assets: {
145
+ "index.ios.bundle": {
146
+ fileHash: "hash-new",
147
+ },
148
+ },
149
+ bundleId: targetBundle.id,
150
+ }),
151
+ );
152
+ }
153
+
154
+ if (url.endsWith(`${baseBundle.id}/files/index.ios.bundle.br`)) {
155
+ return new Response(brotliCompressSync(new Uint8Array([1, 2, 3])));
156
+ }
157
+
158
+ if (url.endsWith(`${baseBundle.id}/files/index.ios.bundle`)) {
159
+ return new Response(new Uint8Array([1, 2, 3]));
160
+ }
161
+
162
+ if (url.endsWith(`${targetBundle.id}/files/index.ios.bundle.br`)) {
163
+ return new Response(brotliCompressSync(new Uint8Array([1, 9, 3])));
164
+ }
165
+
166
+ if (url.endsWith(`${targetBundle.id}/files/index.ios.bundle`)) {
167
+ return new Response(new Uint8Array([1, 9, 3]));
168
+ }
169
+
170
+ return new Response("not found", { status: 404 });
171
+ }),
172
+ );
173
+
174
+ try {
175
+ const updatedBundle = await createBundleDiff(
176
+ {
177
+ baseBundleId: baseBundle.id,
178
+ bundleId: targetBundle.id,
179
+ },
180
+ {
181
+ databasePlugin: createDatabasePlugin(bundles),
182
+ storagePlugin: createStoragePlugin(upload),
183
+ },
184
+ );
185
+
186
+ expect(upload).toHaveBeenCalledOnce();
187
+ expect(updatedBundle).toMatchObject({
188
+ patchBaseBundleId: baseBundle.id,
189
+ patchBaseFileHash: "hash-old",
190
+ });
191
+ expect(updatedBundle.patchFileHash).toMatch(/[a-f0-9]{64}/);
192
+ expect(updatedBundle.patchStorageUri).toContain(
193
+ `${targetBundle.id}/patches/${baseBundle.id}`,
194
+ );
195
+ expect(updatedBundle.patches).toEqual([
196
+ {
197
+ baseBundleId: baseBundle.id,
198
+ baseFileHash: "hash-old",
199
+ patchFileHash: updatedBundle.patchFileHash,
200
+ patchStorageUri: updatedBundle.patchStorageUri,
201
+ },
202
+ ]);
203
+ } finally {
204
+ vi.unstubAllGlobals();
205
+ }
206
+ });
207
+
208
+ it("rejects ambiguous Hermes bundle assets in manifests", async () => {
209
+ const baseBundle = createBundle("00000000-0000-0000-0000-000000000001");
210
+ const targetBundle = createBundle("00000000-0000-0000-0000-000000000002");
211
+ const bundles = new Map([
212
+ [baseBundle.id, baseBundle],
213
+ [targetBundle.id, targetBundle],
214
+ ]);
215
+ const upload = vi.fn<NodeStorageProfile["upload"]>(
216
+ async (key, filePath) => ({
217
+ storageUri: `s3://test-bucket/${key}/${filePath.split("/").pop()}`,
218
+ }),
219
+ );
220
+
221
+ vi.stubGlobal(
222
+ "fetch",
223
+ vi.fn(async (input: Request | URL | string) => {
224
+ const url = String(input);
225
+
226
+ if (url.endsWith(`${baseBundle.id}/manifest.json`)) {
227
+ return new Response(
228
+ JSON.stringify({
229
+ assets: {
230
+ "index.ios.bundle": {
231
+ fileHash: "hash-old",
232
+ },
233
+ "secondary.ios.bundle": {
234
+ fileHash: "hash-secondary-old",
235
+ },
236
+ },
237
+ bundleId: baseBundle.id,
238
+ }),
239
+ );
240
+ }
241
+
242
+ if (url.endsWith(`${targetBundle.id}/manifest.json`)) {
243
+ return new Response(
244
+ JSON.stringify({
245
+ assets: {
246
+ "index.ios.bundle": {
247
+ fileHash: "hash-new",
248
+ },
249
+ "secondary.ios.bundle": {
250
+ fileHash: "hash-secondary-new",
251
+ },
252
+ },
253
+ bundleId: targetBundle.id,
254
+ }),
255
+ );
256
+ }
257
+
258
+ return new Response("not found", { status: 404 });
259
+ }),
260
+ );
261
+
262
+ try {
263
+ await expect(
264
+ createBundleDiff(
265
+ {
266
+ baseBundleId: baseBundle.id,
267
+ bundleId: targetBundle.id,
268
+ },
269
+ {
270
+ databasePlugin: createDatabasePlugin(bundles),
271
+ storagePlugin: createStoragePlugin(upload),
272
+ },
273
+ ),
274
+ ).rejects.toThrow("Expected exactly one Hermes bundle asset in manifest");
275
+ expect(upload).not.toHaveBeenCalled();
276
+ } finally {
277
+ vi.unstubAllGlobals();
278
+ }
279
+ });
280
+
281
+ it("appends additional patch bases without replacing the primary patch when requested", async () => {
282
+ const primaryBaseBundle = createBundle(
283
+ "00000000-0000-0000-0000-000000000001",
284
+ );
285
+ const secondaryBaseBundle = createBundle(
286
+ "00000000-0000-0000-0000-000000000002",
287
+ );
288
+ const targetBundle = createBundle("00000000-0000-0000-0000-000000000003", {
289
+ patches: [
290
+ {
291
+ baseBundleId: primaryBaseBundle.id,
292
+ baseFileHash: "hash-primary-old",
293
+ patchFileHash: "hash-primary-patch",
294
+ patchStorageUri: `s3://test-bucket/${primaryBaseBundle.id}/existing.bsdiff`,
295
+ },
296
+ ],
297
+ patchBaseBundleId: primaryBaseBundle.id,
298
+ patchBaseFileHash: "hash-primary-old",
299
+ patchFileHash: "hash-primary-patch",
300
+ patchStorageUri: `s3://test-bucket/${primaryBaseBundle.id}/existing.bsdiff`,
301
+ });
302
+ const bundles = new Map([
303
+ [primaryBaseBundle.id, primaryBaseBundle],
304
+ [secondaryBaseBundle.id, secondaryBaseBundle],
305
+ [targetBundle.id, targetBundle],
306
+ ]);
307
+ const upload = vi.fn<NodeStorageProfile["upload"]>(
308
+ async (key, filePath) => ({
309
+ storageUri: `s3://test-bucket/${key}/${filePath.split("/").pop()}`,
310
+ }),
311
+ );
312
+
313
+ vi.stubGlobal(
314
+ "fetch",
315
+ vi.fn(async (input: Request | URL | string) => {
316
+ const url = String(input);
317
+
318
+ if (url.endsWith(`${secondaryBaseBundle.id}/manifest.json`)) {
319
+ return new Response(
320
+ JSON.stringify({
321
+ assets: {
322
+ "index.ios.bundle": {
323
+ fileHash: "hash-secondary-old",
324
+ },
325
+ },
326
+ bundleId: secondaryBaseBundle.id,
327
+ }),
328
+ );
329
+ }
330
+
331
+ if (url.endsWith(`${targetBundle.id}/manifest.json`)) {
332
+ return new Response(
333
+ JSON.stringify({
334
+ assets: {
335
+ "index.ios.bundle": {
336
+ fileHash: "hash-target-new",
337
+ },
338
+ },
339
+ bundleId: targetBundle.id,
340
+ }),
341
+ );
342
+ }
343
+
344
+ if (
345
+ url.endsWith(`${secondaryBaseBundle.id}/files/index.ios.bundle.br`)
346
+ ) {
347
+ return new Response(brotliCompressSync(new Uint8Array([1, 2, 3])));
348
+ }
349
+
350
+ if (url.endsWith(`${secondaryBaseBundle.id}/files/index.ios.bundle`)) {
351
+ return new Response(new Uint8Array([1, 2, 3]));
352
+ }
353
+
354
+ if (url.endsWith(`${targetBundle.id}/files/index.ios.bundle.br`)) {
355
+ return new Response(brotliCompressSync(new Uint8Array([1, 4, 3])));
356
+ }
357
+
358
+ if (url.endsWith(`${targetBundle.id}/files/index.ios.bundle`)) {
359
+ return new Response(new Uint8Array([1, 4, 3]));
360
+ }
361
+
362
+ return new Response("not found", { status: 404 });
363
+ }),
364
+ );
365
+
366
+ try {
367
+ const updatedBundle = await createBundleDiff(
368
+ {
369
+ baseBundleId: secondaryBaseBundle.id,
370
+ bundleId: targetBundle.id,
371
+ },
372
+ {
373
+ databasePlugin: createDatabasePlugin(bundles),
374
+ storagePlugin: createStoragePlugin(upload),
375
+ },
376
+ {
377
+ makePrimary: false,
378
+ },
379
+ );
380
+
381
+ expect(updatedBundle.patchBaseBundleId).toBe(primaryBaseBundle.id);
382
+ expect(updatedBundle.patches).toMatchObject([
383
+ {
384
+ baseBundleId: primaryBaseBundle.id,
385
+ baseFileHash: "hash-primary-old",
386
+ patchFileHash: "hash-primary-patch",
387
+ patchStorageUri: `s3://test-bucket/${primaryBaseBundle.id}/existing.bsdiff`,
388
+ },
389
+ {
390
+ baseBundleId: secondaryBaseBundle.id,
391
+ baseFileHash: "hash-secondary-old",
392
+ patchFileHash: expect.any(String),
393
+ patchStorageUri: expect.stringContaining(
394
+ `${targetBundle.id}/patches/${secondaryBaseBundle.id}`,
395
+ ),
396
+ },
397
+ ]);
398
+ } finally {
399
+ vi.unstubAllGlobals();
400
+ }
401
+ });
402
+ });