create-windy 0.2.7 → 0.2.9

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 (361) hide show
  1. package/README.md +6 -0
  2. package/dist/cli.js +181 -87
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/apps/server/Dockerfile +2 -0
  6. package/template/apps/server/package.json +10 -8
  7. package/template/apps/server/src/access-guards.ts +1 -1
  8. package/template/apps/server/src/admin/routes.test.ts +10 -0
  9. package/template/apps/server/src/admin/routes.ts +4 -2
  10. package/template/apps/server/src/agent/routes.test.ts +1 -1
  11. package/template/apps/server/src/agent/routes.ts +2 -2
  12. package/template/apps/server/src/application-services.ts +3 -0
  13. package/template/apps/server/src/audit/drizzle-log-store.ts +2 -2
  14. package/template/apps/server/src/audit/policy.ts +1 -1
  15. package/template/apps/server/src/audit/recovery-queue.test.ts +1 -1
  16. package/template/apps/server/src/audit/recovery-queue.ts +1 -1
  17. package/template/apps/server/src/audit/redaction.ts +1 -1
  18. package/template/apps/server/src/audit/reliable-writer.test.ts +1 -1
  19. package/template/apps/server/src/audit/reliable-writer.ts +1 -1
  20. package/template/apps/server/src/audit/search-summary.ts +1 -1
  21. package/template/apps/server/src/auth/authentication-provider.test.ts +1 -1
  22. package/template/apps/server/src/auth/credential-service.ts +1 -1
  23. package/template/apps/server/src/auth/drizzle-repository.ts +2 -2
  24. package/template/apps/server/src/auth/drizzle-session-operations.ts +1 -1
  25. package/template/apps/server/src/auth/own-session-routes.test.ts +1 -1
  26. package/template/apps/server/src/auth/password.ts +2 -2
  27. package/template/apps/server/src/auth/repository.ts +1 -1
  28. package/template/apps/server/src/auth/routes.test.ts +1 -1
  29. package/template/apps/server/src/auth/service.ts +1 -1
  30. package/template/apps/server/src/bulk-data/artifact-store.ts +1 -1
  31. package/template/apps/server/src/bulk-data/dictionary-handler.ts +1 -1
  32. package/template/apps/server/src/bulk-data/drizzle-repository.ts +3 -3
  33. package/template/apps/server/src/bulk-data/repository.ts +1 -1
  34. package/template/apps/server/src/bulk-data/routes.test.ts +1 -1
  35. package/template/apps/server/src/bulk-data/routes.ts +1 -1
  36. package/template/apps/server/src/bulk-data/service.test.ts +1 -1
  37. package/template/apps/server/src/bulk-data/service.ts +1 -1
  38. package/template/apps/server/src/bulk-data/types.ts +1 -1
  39. package/template/apps/server/src/configuration/bootstrap.ts +2 -2
  40. package/template/apps/server/src/configuration/definition.ts +2 -2
  41. package/template/apps/server/src/configuration/drizzle-repository.ts +2 -2
  42. package/template/apps/server/src/configuration/repository.ts +1 -1
  43. package/template/apps/server/src/configuration/routes.ts +1 -1
  44. package/template/apps/server/src/configuration/service.ts +1 -1
  45. package/template/apps/server/src/data-access/context.ts +2 -2
  46. package/template/apps/server/src/data-access/query-specification.test.ts +1 -1
  47. package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +1 -1
  48. package/template/apps/server/src/data-access/scoped-repository.ts +1 -1
  49. package/template/apps/server/src/data-access/scoped-resource.test.ts +1 -1
  50. package/template/apps/server/src/data-access/scoped-resource.ts +1 -1
  51. package/template/apps/server/src/data-governance/export/approval-port.ts +1 -1
  52. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -1
  53. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +2 -2
  54. package/template/apps/server/src/data-governance/export/job-runtime.ts +2 -3
  55. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +2 -2
  56. package/template/apps/server/src/data-governance/export/postgres-api.integration.test.ts +1 -1
  57. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +3 -3
  58. package/template/apps/server/src/data-governance/export/routes.ts +1 -1
  59. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +1 -1
  60. package/template/apps/server/src/data-governance/export/service.ts +2 -2
  61. package/template/apps/server/src/data-governance/export/source.ts +1 -1
  62. package/template/apps/server/src/data-governance/export/types.ts +1 -1
  63. package/template/apps/server/src/data-governance/plaintext-access.test.ts +1 -1
  64. package/template/apps/server/src/data-governance/plaintext-access.ts +1 -1
  65. package/template/apps/server/src/data-governance/plaintext-postgres.integration.test.ts +1 -1
  66. package/template/apps/server/src/data-governance/user-plaintext-routes.ts +1 -1
  67. package/template/apps/server/src/data-governance/user-projection.ts +2 -2
  68. package/template/apps/server/src/example-modules.ts +25 -1
  69. package/template/apps/server/src/feature/manifest-feature-repository.test.ts +1 -1
  70. package/template/apps/server/src/feature/manifest-feature-repository.ts +1 -1
  71. package/template/apps/server/src/feature/routes.ts +3 -6
  72. package/template/apps/server/src/foundation-access.test.ts +1 -1
  73. package/template/apps/server/src/foundation-access.ts +1 -1
  74. package/template/apps/server/src/foundation.ts +21 -7
  75. package/template/apps/server/src/guards.test.ts +1 -1
  76. package/template/apps/server/src/guards.ts +3 -3
  77. package/template/apps/server/src/index.ts +28 -3
  78. package/template/{packages/modules → apps/server}/src/installed-business-modules.ts +2 -2
  79. package/template/apps/server/src/jobs/artifact-service.test.ts +1 -1
  80. package/template/apps/server/src/jobs/artifact-service.ts +2 -2
  81. package/template/apps/server/src/jobs/drizzle-artifact.ts +2 -2
  82. package/template/apps/server/src/jobs/drizzle-claim.ts +1 -1
  83. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +2 -2
  84. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +1 -1
  85. package/template/apps/server/src/jobs/drizzle-lease.ts +2 -2
  86. package/template/apps/server/src/jobs/drizzle-mappers.ts +3 -3
  87. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +1 -1
  88. package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -1
  89. package/template/apps/server/src/jobs/drizzle-repository.ts +3 -3
  90. package/template/apps/server/src/jobs/execution-repository.ts +1 -1
  91. package/template/apps/server/src/jobs/execution.test.ts +2 -2
  92. package/template/apps/server/src/jobs/in-memory-claim.ts +1 -1
  93. package/template/apps/server/src/jobs/in-memory-execution.ts +1 -1
  94. package/template/apps/server/src/jobs/in-memory-lease.ts +1 -1
  95. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -1
  96. package/template/apps/server/src/jobs/legacy-adapter.test.ts +1 -1
  97. package/template/apps/server/src/jobs/legacy-adapter.ts +1 -1
  98. package/template/apps/server/src/jobs/legacy-service-runtime.ts +4 -2
  99. package/template/apps/server/src/jobs/repository.ts +2 -2
  100. package/template/apps/server/src/jobs/service.ts +2 -2
  101. package/template/apps/server/src/jobs/worker.ts +3 -2
  102. package/template/apps/server/src/license/legacy-license-resolver.ts +1 -1
  103. package/template/apps/server/src/license/license-resolver.test.ts +1 -1
  104. package/template/apps/server/src/license/license-resolver.ts +3 -3
  105. package/template/apps/server/src/license/routes-offline-code.test.ts +2 -2
  106. package/template/apps/server/src/license/routes.ts +1 -1
  107. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -2
  108. package/template/apps/server/src/license/runtime-reminder.test.ts +2 -2
  109. package/template/apps/server/src/license/runtime-service.test.ts +2 -2
  110. package/template/apps/server/src/license/runtime-service.ts +2 -2
  111. package/template/apps/server/src/module-composition.test.ts +1 -1
  112. package/template/apps/server/src/module-composition.ts +1 -1
  113. package/template/apps/server/src/module-host.test.ts +145 -0
  114. package/template/apps/server/src/module-host.ts +110 -0
  115. package/template/apps/server/src/notification/drizzle-repository.ts +2 -2
  116. package/template/apps/server/src/notification/repository.ts +1 -1
  117. package/template/apps/server/src/notification/routes.ts +1 -1
  118. package/template/apps/server/src/pagination.ts +1 -1
  119. package/template/apps/server/src/persistence.integration.test.ts +1 -1
  120. package/template/apps/server/src/persistence.ts +1 -1
  121. package/template/apps/server/src/profile/routes.ts +1 -1
  122. package/template/apps/server/src/route-guards.test.ts +1 -1
  123. package/template/apps/server/src/route-guards.ts +1 -1
  124. package/template/apps/server/src/runtime-feature.ts +125 -0
  125. package/template/apps/server/src/runtime-license.ts +1 -1
  126. package/template/apps/server/src/runtime.test.ts +48 -1
  127. package/template/apps/server/src/runtime.ts +17 -64
  128. package/template/apps/server/src/scheduler/audit.ts +1 -1
  129. package/template/apps/server/src/scheduler/definitions.test.ts +1 -1
  130. package/template/apps/server/src/scheduler/definitions.ts +2 -2
  131. package/template/apps/server/src/scheduler/drizzle-durable-links.ts +1 -1
  132. package/template/apps/server/src/scheduler/drizzle-mappers.ts +1 -1
  133. package/template/apps/server/src/scheduler/drizzle-repository.ts +1 -1
  134. package/template/apps/server/src/scheduler/execution-recorder.ts +1 -1
  135. package/template/apps/server/src/scheduler/history.ts +1 -1
  136. package/template/apps/server/src/scheduler/overview.ts +1 -1
  137. package/template/apps/server/src/scheduler/recovery-service.test.ts +1 -1
  138. package/template/apps/server/src/scheduler/recovery-service.ts +1 -1
  139. package/template/apps/server/src/scheduler/repository.ts +1 -1
  140. package/template/apps/server/src/scheduler/routes.ts +1 -1
  141. package/template/apps/server/src/scheduler/service-idle.test.ts +1 -1
  142. package/template/apps/server/src/scheduler/service.test.ts +1 -1
  143. package/template/apps/server/src/scheduler/service.ts +1 -1
  144. package/template/apps/server/src/scheduler/types.ts +1 -1
  145. package/template/apps/server/src/search/contracts.ts +2 -2
  146. package/template/apps/server/src/search/host.ts +1 -1
  147. package/template/apps/server/src/search/registry.ts +1 -1
  148. package/template/apps/server/src/search/routes.test.ts +1 -1
  149. package/template/apps/server/src/search/routes.ts +1 -1
  150. package/template/apps/server/src/search/service.ts +2 -2
  151. package/template/apps/server/src/search/test-support.ts +2 -2
  152. package/template/apps/server/src/session/routes.ts +1 -1
  153. package/template/apps/server/src/settings/drizzle-repository.ts +2 -2
  154. package/template/apps/server/src/settings/repository.ts +1 -1
  155. package/template/apps/server/src/settings/routes.test.ts +1 -1
  156. package/template/apps/server/src/settings/routes.ts +1 -1
  157. package/template/apps/server/src/settings/runtime.ts +1 -1
  158. package/template/apps/server/src/storage/blob-reference-registry.test.ts +1 -1
  159. package/template/apps/server/src/storage/blob-reference-registry.ts +1 -1
  160. package/template/apps/server/src/storage/drizzle-upload-repository.integration.test.ts +1 -1
  161. package/template/apps/server/src/storage/drizzle-upload-repository.ts +2 -2
  162. package/template/apps/server/src/storage/route-support.ts +1 -1
  163. package/template/apps/server/src/storage/routes.test.ts +1 -1
  164. package/template/apps/server/src/storage/routes.ts +2 -2
  165. package/template/apps/server/src/storage/runtime.ts +6 -4
  166. package/template/apps/server/src/system/audit-routes.ts +1 -1
  167. package/template/apps/server/src/system/department-routes.ts +1 -1
  168. package/template/apps/server/src/system/drizzle-repository.ts +1 -1
  169. package/template/apps/server/src/system/drizzle-scoped-repository.ts +1 -1
  170. package/template/apps/server/src/system/drizzle-system.ts +1 -1
  171. package/template/apps/server/src/system/entities.ts +1 -1
  172. package/template/apps/server/src/system/identity-route-config.ts +1 -1
  173. package/template/apps/server/src/system/interface-catalog.test.ts +3 -3
  174. package/template/apps/server/src/system/interface-catalog.ts +2 -2
  175. package/template/apps/server/src/system/menu-registry.ts +1 -1
  176. package/template/apps/server/src/system/menu-routes.ts +1 -1
  177. package/template/apps/server/src/system/operations.test.ts +1 -1
  178. package/template/apps/server/src/system/operations.ts +1 -1
  179. package/template/apps/server/src/system/redis-health.ts +1 -1
  180. package/template/apps/server/src/system/repository.ts +1 -1
  181. package/template/apps/server/src/system/resource-route-support.ts +1 -1
  182. package/template/apps/server/src/system/route-helpers.ts +1 -1
  183. package/template/apps/server/src/system/route-types.ts +1 -1
  184. package/template/apps/server/src/system/routes.test.ts +1 -1
  185. package/template/apps/server/src/system/routes.ts +1 -1
  186. package/template/apps/server/src/system/user-projection.ts +1 -1
  187. package/template/apps/server/src/work-order/data-scope.integration.test.ts +1 -1
  188. package/template/apps/server/src/work-order/drizzle-repository.integration.test.ts +1 -1
  189. package/template/apps/server/src/work-order/drizzle-repository.ts +2 -2
  190. package/template/apps/server/src/work-order/foundation-modules.test.ts +15 -5
  191. package/template/apps/server/src/work-order/repository.ts +1 -1
  192. package/template/apps/server/src/work-order/routes.test.ts +2 -2
  193. package/template/apps/server/src/work-order/routes.ts +2 -2
  194. package/template/apps/server/src/work-order/search-api.test.ts +3 -3
  195. package/template/apps/server/src/work-order/search-provider.test.ts +3 -3
  196. package/template/apps/server/src/work-order/service.ts +1 -1
  197. package/template/apps/server/src/work-order/task.test.ts +1 -1
  198. package/template/apps/server/src/work-order/task.ts +2 -2
  199. package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +2 -2
  200. package/template/apps/server/src/workflow/drizzle-repository.ts +3 -3
  201. package/template/apps/server/src/workflow/effects.test.ts +1 -1
  202. package/template/apps/server/src/workflow/effects.ts +1 -1
  203. package/template/apps/server/src/workflow/repository.ts +2 -2
  204. package/template/apps/server/src/workflow/routes.ts +3 -3
  205. package/template/apps/server/src/workflow/service.ts +1 -1
  206. package/template/apps/server/src/workflow/timeout-runtime.test.ts +1 -1
  207. package/template/apps/server/src/workflow/timeout-runtime.ts +2 -2
  208. package/template/apps/web/Dockerfile +2 -0
  209. package/template/apps/web/package.json +4 -4
  210. package/template/apps/web/runtime-server.ts +1 -1
  211. package/template/apps/web/src/app/layouts/AppLayout.vue +6 -0
  212. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +1 -1
  213. package/template/apps/web/src/components/common/DataPagination.vue +1 -1
  214. package/template/apps/web/src/components/license/LicenseActivationCodeForm.vue +1 -1
  215. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +2 -2
  216. package/template/apps/web/src/composables/useModuleConfigurations.ts +1 -1
  217. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  218. package/template/apps/web/src/composables/watermark-settings.ts +6 -0
  219. package/template/apps/web/src/composables/watermark-settings.webtest.ts +27 -1
  220. package/template/apps/web/src/layout/AppSidebar.vue +12 -3
  221. package/template/apps/web/src/layout/AppSidebar.webtest.ts +11 -0
  222. package/template/apps/web/src/layout/GlobalWatermark.vue +25 -8
  223. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +109 -0
  224. package/template/apps/web/src/layout/NavigationSearchDialog.vue +1 -1
  225. package/template/apps/web/src/layout/NotificationMenu.vue +1 -1
  226. package/template/apps/web/src/layout/navigation.ts +5 -1
  227. package/template/apps/web/src/layout/navigation.webtest.ts +1 -1
  228. package/template/apps/web/src/pages/configuration/components/ModuleConfigRollbackDialog.vue +1 -1
  229. package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -1
  230. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +1 -1
  231. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.webtest.ts +1 -1
  232. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +1 -1
  233. package/template/apps/web/src/pages/dashboard/dashboard-presenter.webtest.ts +1 -1
  234. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +21 -0
  235. package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +2 -2
  236. package/template/apps/web/src/pages/system/components/AuditActionCell.vue +7 -2
  237. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +7 -2
  238. package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +1 -1
  239. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.vue +1 -1
  240. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.webtest.ts +1 -1
  241. package/template/apps/web/src/pages/system/components/SchedulerRunTable.vue +1 -1
  242. package/template/apps/web/src/pages/system/components/SchedulerRunTable.webtest.ts +1 -1
  243. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.vue +1 -1
  244. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.webtest.ts +1 -1
  245. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -1
  246. package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +10 -3
  247. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.vue +1 -1
  248. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.webtest.ts +1 -1
  249. package/template/apps/web/src/pages/system/components/role-policy/RolePolicyForm.vue +1 -1
  250. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.ts +1 -1
  251. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.webtest.ts +1 -1
  252. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.ts +2 -2
  253. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.webtest.ts +1 -1
  254. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +1 -1
  255. package/template/apps/web/src/router/manifest-routes.ts +1 -1
  256. package/template/apps/web/src/router/manifest-routes.webtest.ts +1 -1
  257. package/template/apps/web/src/services/access-api.ts +10 -1
  258. package/template/apps/web/src/services/configuration-api.ts +1 -1
  259. package/template/apps/web/src/services/license-api.ts +1 -1
  260. package/template/apps/web/src/services/notification-api.ts +2 -2
  261. package/template/apps/web/src/services/operations-api.ts +1 -1
  262. package/template/apps/web/src/services/platform-settings-api.ts +2 -2
  263. package/template/apps/web/src/services/profile-api.ts +2 -2
  264. package/template/docs/architecture/durable-jobs.md +5 -0
  265. package/template/docs/architecture/module-manifest-composition.md +14 -7
  266. package/template/docs/architecture/object-storage.md +1 -1
  267. package/template/docs/development/crud-generator.md +4 -1
  268. package/template/docs/platform/audit-reliability.md +1 -1
  269. package/template/package.json +2 -1
  270. package/template/packages/config/package.json +15 -4
  271. package/template/packages/config/src/platform.ts +1 -1
  272. package/template/packages/crud-generator/package.json +1 -1
  273. package/template/packages/crud-generator/src/generator.test.ts +16 -0
  274. package/template/packages/crud-generator/src/templates/domain.ts +1 -1
  275. package/template/packages/crud-generator/src/templates/manifest.ts +2 -2
  276. package/template/packages/crud-generator/src/templates/project.ts +3 -3
  277. package/template/packages/crud-generator/src/templates/repository.ts +1 -1
  278. package/template/packages/crud-generator/src/templates/routes.ts +2 -2
  279. package/template/packages/crud-generator/src/validation.ts +22 -1
  280. package/template/packages/database/package.json +15 -5
  281. package/template/packages/database/src/bundle-validation.ts +218 -0
  282. package/template/packages/database/src/dialects.ts +1 -1
  283. package/template/packages/database/src/runner.test.ts +160 -13
  284. package/template/packages/database/src/runner.ts +95 -77
  285. package/template/packages/database/src/schema/governed-exports.ts +1 -1
  286. package/template/packages/database/src/schema/identity.ts +1 -1
  287. package/template/packages/database/src/schema/license-v1.ts +1 -1
  288. package/template/packages/database/src/schema/notifications.ts +1 -1
  289. package/template/packages/database/src/schema/platform-settings.ts +1 -1
  290. package/template/packages/database/src/schema/workflow.ts +1 -1
  291. package/template/packages/database/src/sql-store.ts +1 -1
  292. package/template/packages/database/src/store.ts +1 -1
  293. package/template/packages/example-work-order/index.ts +1 -0
  294. package/template/packages/example-work-order/package.json +4 -3
  295. package/template/packages/example-work-order/src/contract.ts +1 -1
  296. package/template/packages/example-work-order/src/manifest.ts +8 -3
  297. package/template/packages/example-work-order/src/migration-bundle.test.ts +43 -0
  298. package/template/packages/example-work-order/src/migration-bundle.ts +64 -0
  299. package/template/packages/jobs/index.ts +2 -0
  300. package/template/packages/jobs/package.json +29 -0
  301. package/template/packages/jobs/src/handlers.test.ts +72 -0
  302. package/template/{apps/server/src/jobs/handler-registry.ts → packages/jobs/src/handlers.ts} +4 -4
  303. package/template/{apps/server/src/jobs → packages/jobs/src}/types.ts +7 -1
  304. package/template/packages/{license-sdk → jobs}/tsconfig.json +0 -1
  305. package/template/packages/modules/index.ts +1 -1
  306. package/template/packages/modules/package.json +15 -5
  307. package/template/packages/modules/src/ai-operation-composition.test.ts +195 -0
  308. package/template/packages/modules/src/ai-operation-composition.ts +145 -0
  309. package/template/packages/modules/src/catalog-validation.ts +123 -0
  310. package/template/packages/modules/src/composition.test.ts +75 -2
  311. package/template/packages/modules/src/composition.ts +23 -39
  312. package/template/packages/modules/src/implementation-bindings.ts +70 -0
  313. package/template/packages/modules/src/manifest.ts +43 -12
  314. package/template/packages/modules/src/menu-composition.ts +1 -1
  315. package/template/packages/modules/src/system-api-binding.ts +1 -1
  316. package/template/packages/modules/src/system-api-data-governance.ts +1 -1
  317. package/template/packages/modules/src/system-api-permissions.ts +1 -1
  318. package/template/packages/modules/src/system-data-governance.ts +1 -1
  319. package/template/packages/modules/src/system-features.ts +1 -1
  320. package/template/packages/modules/src/system-governed-export-bindings.ts +1 -1
  321. package/template/packages/modules/src/system-module-catalog.ts +1 -1
  322. package/template/packages/server-sdk/index.ts +5 -0
  323. package/template/packages/server-sdk/package.json +25 -0
  324. package/template/packages/server-sdk/src/module-host.ts +29 -0
  325. package/template/packages/server-sdk/tsconfig.json +16 -0
  326. package/template/packages/shared/index.ts +2 -0
  327. package/template/packages/shared/package.json +14 -3
  328. package/template/packages/shared/src/audit.ts +22 -1
  329. package/template/packages/shared/src/license-catalog.test.ts +17 -0
  330. package/template/packages/shared/src/license-catalog.ts +33 -0
  331. package/template/packages/shared/src/migration.ts +39 -0
  332. package/template/packages/storage/package.json +14 -3
  333. package/template/packages/license-sdk/index.ts +0 -13
  334. package/template/packages/license-sdk/package.json +0 -18
  335. package/template/packages/license-sdk/src/bounded-json-body.test.ts +0 -65
  336. package/template/packages/license-sdk/src/bounded-json-body.ts +0 -135
  337. package/template/packages/license-sdk/src/canonical-json.test.ts +0 -28
  338. package/template/packages/license-sdk/src/canonical-json.ts +0 -104
  339. package/template/packages/license-sdk/src/client.test.ts +0 -82
  340. package/template/packages/license-sdk/src/client.ts +0 -86
  341. package/template/packages/license-sdk/src/device.ts +0 -46
  342. package/template/packages/license-sdk/src/file-storage.test.ts +0 -80
  343. package/template/packages/license-sdk/src/file-storage.ts +0 -77
  344. package/template/packages/license-sdk/src/installation.test.ts +0 -241
  345. package/template/packages/license-sdk/src/installation.ts +0 -150
  346. package/template/packages/license-sdk/src/license-parser.ts +0 -56
  347. package/template/packages/license-sdk/src/offline-code-base32.ts +0 -57
  348. package/template/packages/license-sdk/src/offline-code-decompression.ts +0 -55
  349. package/template/packages/license-sdk/src/offline-code-limits.ts +0 -10
  350. package/template/packages/license-sdk/src/offline-code-wire.ts +0 -72
  351. package/template/packages/license-sdk/src/offline-code.test.ts +0 -172
  352. package/template/packages/license-sdk/src/offline-code.ts +0 -319
  353. package/template/packages/license-sdk/src/payload.test.ts +0 -72
  354. package/template/packages/license-sdk/src/payload.ts +0 -201
  355. package/template/packages/license-sdk/src/storage.ts +0 -64
  356. package/template/packages/license-sdk/src/test-fixtures.ts +0 -59
  357. package/template/packages/license-sdk/src/v1-test-vector.ts +0 -49
  358. package/template/packages/license-sdk/src/validation.test.ts +0 -62
  359. package/template/packages/license-sdk/src/validation.ts +0 -143
  360. package/template/packages/license-sdk/src/verification.test.ts +0 -97
  361. package/template/packages/license-sdk/src/verification.ts +0 -199
@@ -1,77 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { basename, dirname, join } from "node:path";
3
- import { mkdir, open, readFile, rename, unlink } from "node:fs/promises";
4
- import type { AtomicLicenseStore, LicenseSource } from "./storage.js";
5
-
6
- export class FixedPathLicenseSource implements LicenseSource {
7
- constructor(protected readonly licensePath: string) {}
8
-
9
- async readCurrent(): Promise<string | undefined> {
10
- try {
11
- return await readFile(this.licensePath, "utf8");
12
- } catch (error) {
13
- if (isMissingFile(error)) return undefined;
14
- throw error;
15
- }
16
- }
17
- }
18
-
19
- /**
20
- * Atomic filesystem adapter. Temporary data is synced before the same-directory
21
- * rename; the directory is synced afterwards so a crash cannot expose a partial file.
22
- */
23
- export class FileSystemAtomicLicenseStore
24
- extends FixedPathLicenseSource
25
- implements AtomicLicenseStore
26
- {
27
- constructor(
28
- licensePath: string,
29
- private readonly fileMode = 0o600,
30
- ) {
31
- super(licensePath);
32
- }
33
-
34
- async replaceCurrentAtomically(value: string): Promise<void> {
35
- const directory = dirname(this.licensePath);
36
- const temporaryPath = join(
37
- directory,
38
- `.${basename(this.licensePath)}.${process.pid}.${randomUUID()}.tmp`,
39
- );
40
- await mkdir(directory, { recursive: true });
41
-
42
- let temporaryFile: Awaited<ReturnType<typeof open>> | undefined;
43
- try {
44
- temporaryFile = await open(temporaryPath, "wx", this.fileMode);
45
- await temporaryFile.writeFile(value, "utf8");
46
- await temporaryFile.sync();
47
- await temporaryFile.close();
48
- temporaryFile = undefined;
49
- await this.beforeCommit();
50
- await rename(temporaryPath, this.licensePath);
51
- await syncDirectory(directory);
52
- } finally {
53
- await temporaryFile?.close().catch(() => undefined);
54
- await unlink(temporaryPath).catch(() => undefined);
55
- }
56
- }
57
-
58
- /** Test seam for simulating a process failure before the atomic commit. */
59
- protected async beforeCommit(): Promise<void> {}
60
- }
61
-
62
- async function syncDirectory(directory: string): Promise<void> {
63
- const handle = await open(directory, "r");
64
- try {
65
- await handle.sync();
66
- } finally {
67
- await handle.close();
68
- }
69
- }
70
-
71
- function isMissingFile(error: unknown): boolean {
72
- return (
73
- error instanceof Error &&
74
- "code" in error &&
75
- (error as NodeJS.ErrnoException).code === "ENOENT"
76
- );
77
- }
@@ -1,241 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { licenseVersionKey, type LicenseV1Claims } from "@windy/shared";
3
- import { LicenseInstaller } from "./installation.js";
4
- import type { AtomicLicenseStore } from "./storage.js";
5
- import { MemoryAtomicLicenseStore } from "./storage.js";
6
- import { createSignedLicenseV1 } from "./test-fixtures.js";
7
-
8
- function claims(overrides: Partial<LicenseV1Claims> = {}): LicenseV1Claims {
9
- return {
10
- licenseId: "license_1",
11
- productCode: "windy-platform",
12
- customer: { id: "customer_1", code: "C-1", name: "客户一" },
13
- project: { id: "project_1", code: "P-1", name: "项目一" },
14
- contract: { id: "contract_1", contractNumber: "HT-1", version: "1" },
15
- machineCode: "machine_1",
16
- issuedAt: "2026-07-10T00:00:00.000Z",
17
- expiresAt: "2030-07-10T00:00:00.000Z",
18
- licenseVersion: {
19
- key: licenseVersionKey("standard"),
20
- name: "普通付费版",
21
- },
22
- ...overrides,
23
- };
24
- }
25
-
26
- function installer(
27
- store: AtomicLicenseStore,
28
- publicKeys: Record<string, string>,
29
- trustedReplacementAuthority?: ConstructorParameters<
30
- typeof LicenseInstaller
31
- >[0]["trustedReplacementAuthority"],
32
- trustedInvalidCurrentRecoveryAuthority?: ConstructorParameters<
33
- typeof LicenseInstaller
34
- >[0]["trustedInvalidCurrentRecoveryAuthority"],
35
- ): LicenseInstaller {
36
- return new LicenseInstaller({
37
- store,
38
- productCode: "windy-platform",
39
- machineCode: "machine_1",
40
- publicKeys,
41
- now: new Date("2026-08-01T00:00:00.000Z"),
42
- trustedReplacementAuthority,
43
- trustedInvalidCurrentRecoveryAuthority,
44
- });
45
- }
46
-
47
- describe("LicenseInstaller", () => {
48
- test("签发时间更新的 License 立即替换,即使新到期时间更短", async () => {
49
- const current = createSignedLicenseV1(claims(), "key_current");
50
- const candidate = createSignedLicenseV1(
51
- claims({
52
- licenseId: "license_2",
53
- issuedAt: "2026-07-20T00:00:00.000Z",
54
- expiresAt: "2027-07-20T00:00:00.000Z",
55
- licenseVersion: {
56
- key: licenseVersionKey("advanced"),
57
- name: "高级付费版",
58
- },
59
- }),
60
- "key_candidate",
61
- );
62
- const store = new MemoryAtomicLicenseStore(JSON.stringify(current.license));
63
-
64
- const result = await installer(store, {
65
- key_current: current.publicKeyPem,
66
- key_candidate: candidate.publicKeyPem,
67
- }).install(JSON.stringify(candidate.license));
68
-
69
- expect(result.status).toBe("installed");
70
- expect(result.license?.claims.expiresAt).toBe("2027-07-20T00:00:00.000Z");
71
- expect(JSON.parse((await store.readCurrent())!).claims.licenseId).toBe(
72
- "license_2",
73
- );
74
- });
75
-
76
- test("历史 License 不能覆盖签发时间更晚的当前 License", async () => {
77
- const current = createSignedLicenseV1(
78
- claims({
79
- licenseId: "license_new",
80
- issuedAt: "2026-07-20T00:00:00.000Z",
81
- }),
82
- "key_current",
83
- );
84
- const old = createSignedLicenseV1(
85
- claims({
86
- licenseId: "license_old",
87
- issuedAt: "2026-07-10T00:00:00.000Z",
88
- }),
89
- "key_old",
90
- );
91
- const original = JSON.stringify(current.license);
92
- const store = new MemoryAtomicLicenseStore(original);
93
-
94
- const result = await installer(store, {
95
- key_current: current.publicKeyPem,
96
- key_old: old.publicKeyPem,
97
- }).install(JSON.stringify(old.license));
98
-
99
- expect(result).toMatchObject({
100
- status: "rejected",
101
- failures: ["license-not-newer"],
102
- });
103
- expect(await store.readCurrent()).toBe(original);
104
- });
105
-
106
- test("篡改 signed issuedAt 不能伪装成新 License", async () => {
107
- const current = createSignedLicenseV1(claims(), "key_current");
108
- const candidate = createSignedLicenseV1(
109
- claims({ licenseId: "license_2", issuedAt: "2026-07-05T00:00:00.000Z" }),
110
- "key_candidate",
111
- );
112
- const original = JSON.stringify(current.license);
113
- const store = new MemoryAtomicLicenseStore(original);
114
- const tampered = {
115
- ...candidate.license,
116
- claims: {
117
- ...candidate.license.claims,
118
- issuedAt: "2026-07-20T00:00:00.000Z",
119
- },
120
- };
121
-
122
- const result = await installer(store, {
123
- key_current: current.publicKeyPem,
124
- key_candidate: candidate.publicKeyPem,
125
- }).install(JSON.stringify(tampered));
126
-
127
- expect(result.failures).toEqual(["invalid-signature"]);
128
- expect(await store.readCurrent()).toBe(original);
129
- });
130
-
131
- test("可信替换关系可显式授权非递增 issuedAt 的替换", async () => {
132
- const current = createSignedLicenseV1(
133
- claims({
134
- licenseId: "license_current",
135
- issuedAt: "2026-07-20T00:00:00.000Z",
136
- }),
137
- "key_current",
138
- );
139
- const replacement = createSignedLicenseV1(
140
- claims({
141
- licenseId: "license_replacement",
142
- issuedAt: "2026-07-19T00:00:00.000Z",
143
- }),
144
- "key_replacement",
145
- );
146
- const store = new MemoryAtomicLicenseStore(JSON.stringify(current.license));
147
-
148
- const result = await installer(
149
- store,
150
- {
151
- key_current: current.publicKeyPem,
152
- key_replacement: replacement.publicKeyPem,
153
- },
154
- ({ current: installed, candidate }) =>
155
- installed.claims.licenseId === "license_current" &&
156
- candidate.claims.licenseId === "license_replacement",
157
- ).install(JSON.stringify(replacement.license));
158
-
159
- expect(result.status).toBe("installed");
160
- });
161
-
162
- test("未知公钥和写入失败都不覆盖当前 License", async () => {
163
- const current = createSignedLicenseV1(claims(), "key_current");
164
- const candidate = createSignedLicenseV1(
165
- claims({ licenseId: "license_2", issuedAt: "2026-07-20T00:00:00.000Z" }),
166
- "key_unknown",
167
- );
168
- const original = JSON.stringify(current.license);
169
- const unknownKeyStore = new MemoryAtomicLicenseStore(original);
170
-
171
- const unknownKeyResult = await installer(unknownKeyStore, {
172
- key_current: current.publicKeyPem,
173
- }).install(JSON.stringify(candidate.license));
174
- expect(unknownKeyResult.failures).toContain("unknown-public-key");
175
- expect(await unknownKeyStore.readCurrent()).toBe(original);
176
-
177
- const failingStore = new FailingAtomicStore(original, false);
178
- const storageResult = await installer(failingStore, {
179
- key_current: current.publicKeyPem,
180
- key_unknown: candidate.publicKeyPem,
181
- }).install(JSON.stringify(candidate.license));
182
- expect(storageResult.failures).toEqual(["storage-failure"]);
183
- expect(await failingStore.readCurrent()).toBe(original);
184
- });
185
-
186
- test("提交完成后适配器报错时通过回读确认新文件完整", async () => {
187
- const candidate = createSignedLicenseV1(claims(), "key_candidate");
188
- const store = new FailingAtomicStore(undefined, true);
189
-
190
- const result = await installer(store, {
191
- key_candidate: candidate.publicKeyPem,
192
- }).install(JSON.stringify(candidate.license));
193
-
194
- expect(result.status).toBe("installed");
195
- expect(JSON.parse((await store.readCurrent())!).claims.licenseId).toBe(
196
- "license_1",
197
- );
198
- });
199
-
200
- test("损坏 current 只在可信恢复授权后由同一安装器原子替换", async () => {
201
- const candidate = createSignedLicenseV1(claims(), "key_candidate");
202
- const store = new MemoryAtomicLicenseStore("{broken");
203
- const publicKeys = { key_candidate: candidate.publicKeyPem };
204
-
205
- const rejected = await installer(store, publicKeys).install(
206
- JSON.stringify(candidate.license),
207
- );
208
- expect(rejected.failures).toEqual(["current-license-invalid"]);
209
- expect(await store.readCurrent()).toBe("{broken");
210
-
211
- const recovered = await installer(
212
- store,
213
- publicKeys,
214
- undefined,
215
- ({ rawCurrent, candidate: verifiedCandidate }) =>
216
- rawCurrent === "{broken" &&
217
- verifiedCandidate.claims.licenseId === "license_1",
218
- ).install(JSON.stringify(candidate.license));
219
-
220
- expect(recovered.status).toBe("installed");
221
- expect(JSON.parse((await store.readCurrent())!).claims.licenseId).toBe(
222
- "license_1",
223
- );
224
- });
225
- });
226
-
227
- class FailingAtomicStore implements AtomicLicenseStore {
228
- constructor(
229
- private value: string | undefined,
230
- private readonly commitBeforeFailure: boolean,
231
- ) {}
232
-
233
- readCurrent(): string | undefined {
234
- return this.value;
235
- }
236
-
237
- replaceCurrentAtomically(value: string): never {
238
- if (this.commitBeforeFailure) this.value = value;
239
- throw new Error("模拟磁盘写入失败");
240
- }
241
- }
@@ -1,150 +0,0 @@
1
- import type { LicenseFailureReason, SignedLicense } from "@windy/shared";
2
- import { canonicalizeJson } from "./canonical-json.js";
3
- import type { AtomicLicenseStore } from "./storage.js";
4
- import type { LicenseValidationOptions } from "./validation.js";
5
- import {
6
- validateLicenseDocument,
7
- verifyLicenseDocument,
8
- } from "./validation.js";
9
-
10
- export type LicenseInstallFailure =
11
- | LicenseFailureReason
12
- | "current-license-invalid"
13
- | "license-not-newer"
14
- | "storage-failure";
15
-
16
- export interface LicenseInstallResult {
17
- status: "installed" | "unchanged" | "rejected";
18
- license?: SignedLicense;
19
- failures: LicenseInstallFailure[];
20
- }
21
-
22
- export type TrustedReplacementAuthority = (input: {
23
- current: SignedLicense;
24
- candidate: SignedLicense;
25
- }) => boolean | Promise<boolean>;
26
-
27
- export type TrustedInvalidCurrentRecoveryAuthority = (input: {
28
- rawCurrent: string;
29
- candidate: SignedLicense;
30
- }) => boolean | Promise<boolean>;
31
-
32
- export interface LicenseInstallerOptions extends Omit<
33
- LicenseValidationOptions,
34
- "lastTrustedAt"
35
- > {
36
- store: AtomicLicenseStore;
37
- /** Must resolve from authenticated, locally trusted replacement metadata. */
38
- trustedReplacementAuthority?: TrustedReplacementAuthority;
39
- /** Must only be enabled by an authenticated, audited local recovery flow. */
40
- trustedInvalidCurrentRecoveryAuthority?: TrustedInvalidCurrentRecoveryAuthority;
41
- }
42
-
43
- export class LicenseInstaller {
44
- constructor(private readonly options: LicenseInstallerOptions) {}
45
-
46
- async install(rawCandidate: string): Promise<LicenseInstallResult> {
47
- const candidateResult = await validateLicenseDocument(rawCandidate, {
48
- productCode: this.options.productCode,
49
- machineCode: this.options.machineCode,
50
- publicKeys: this.options.publicKeys,
51
- now: this.options.now,
52
- clockToleranceMs: this.options.clockToleranceMs,
53
- });
54
- const candidate = candidateResult.license;
55
- if (!candidateResult.valid || !candidate) {
56
- return {
57
- status: "rejected",
58
- failures: candidateResult.failures,
59
- };
60
- }
61
-
62
- let rawCurrent: string | undefined;
63
- try {
64
- rawCurrent = await this.options.store.readCurrent();
65
- } catch {
66
- return { status: "rejected", failures: ["storage-failure"] };
67
- }
68
-
69
- if (rawCurrent) {
70
- const currentResult = await verifyLicenseDocument(
71
- rawCurrent,
72
- this.options.publicKeys,
73
- );
74
- const current = currentResult.license;
75
- if (!currentResult.valid || !current) {
76
- const recoveryAllowed = Boolean(
77
- await this.options.trustedInvalidCurrentRecoveryAuthority?.({
78
- rawCurrent,
79
- candidate,
80
- }),
81
- );
82
- if (!recoveryAllowed) {
83
- return {
84
- status: "rejected",
85
- failures: ["current-license-invalid"],
86
- };
87
- }
88
- } else {
89
- if (sameLicense(current, candidate)) {
90
- return { status: "unchanged", license: current, failures: [] };
91
- }
92
- if (!(await this.isNewerOrReplacement(current, candidate))) {
93
- return {
94
- status: "rejected",
95
- license: current,
96
- failures: ["license-not-newer"],
97
- };
98
- }
99
- }
100
- }
101
-
102
- const serializedCandidate = canonicalizeJson(candidate);
103
- try {
104
- await this.options.store.replaceCurrentAtomically(serializedCandidate);
105
- return { status: "installed", license: candidate, failures: [] };
106
- } catch {
107
- return this.resolveUncertainCommit(candidate);
108
- }
109
- }
110
-
111
- private async isNewerOrReplacement(
112
- current: SignedLicense,
113
- candidate: SignedLicense,
114
- ): Promise<boolean> {
115
- if (
116
- Date.parse(candidate.claims.issuedAt) >
117
- Date.parse(current.claims.issuedAt)
118
- ) {
119
- return true;
120
- }
121
- return Boolean(
122
- await this.options.trustedReplacementAuthority?.({ current, candidate }),
123
- );
124
- }
125
-
126
- private async resolveUncertainCommit(
127
- candidate: SignedLicense,
128
- ): Promise<LicenseInstallResult> {
129
- try {
130
- const committedRaw = await this.options.store.readCurrent();
131
- const committed = committedRaw
132
- ? await verifyLicenseDocument(committedRaw, this.options.publicKeys)
133
- : undefined;
134
- if (committed?.license && sameLicense(committed.license, candidate)) {
135
- return { status: "installed", license: candidate, failures: [] };
136
- }
137
- } catch {
138
- // The adapter could not prove which complete value survived.
139
- }
140
- return { status: "rejected", failures: ["storage-failure"] };
141
- }
142
- }
143
-
144
- function sameLicense(left: SignedLicense, right: SignedLicense): boolean {
145
- return (
146
- left.claims.licenseId === right.claims.licenseId &&
147
- left.publicKeyId === right.publicKeyId &&
148
- left.signature === right.signature
149
- );
150
- }
@@ -1,56 +0,0 @@
1
- import type { SignedLicense, SignedLicenseV1 } from "@windy/shared";
2
- import { createLicenseSigningPayload } from "./payload.js";
3
-
4
- export function parseSignedLicenseV1(
5
- rawLicense: string,
6
- ): SignedLicenseV1 | undefined {
7
- try {
8
- const value = JSON.parse(rawLicense) as unknown;
9
- if (!isRecord(value)) return undefined;
10
- if (
11
- !hasExactKeys(value, [
12
- "algorithm",
13
- "claims",
14
- "formatVersion",
15
- "publicKeyId",
16
- "signature",
17
- ])
18
- ) {
19
- return undefined;
20
- }
21
- if (typeof value.signature !== "string" || !value.signature.length) {
22
- return undefined;
23
- }
24
- const license = value as unknown as SignedLicenseV1;
25
- createLicenseSigningPayload({
26
- formatVersion: license.formatVersion,
27
- algorithm: license.algorithm,
28
- publicKeyId: license.publicKeyId,
29
- claims: license.claims,
30
- });
31
- return license;
32
- } catch {
33
- return undefined;
34
- }
35
- }
36
-
37
- export function parseSignedLicense(
38
- rawLicense: string,
39
- ): SignedLicense | undefined {
40
- return parseSignedLicenseV1(rawLicense);
41
- }
42
-
43
- function isRecord(value: unknown): value is Record<string, unknown> {
44
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
45
- }
46
-
47
- function hasExactKeys(
48
- value: Record<string, unknown>,
49
- expected: readonly string[],
50
- ): boolean {
51
- const keys = Object.keys(value);
52
- return (
53
- keys.length === expected.length &&
54
- keys.every((key) => expected.includes(key))
55
- );
56
- }
@@ -1,57 +0,0 @@
1
- const ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
2
- const VALUES = new Map(
3
- [...ALPHABET].map((character, index) => [character, index]),
4
- );
5
-
6
- export function encodeBase32(bytes: Uint8Array): string {
7
- let output = "";
8
- let buffer = 0;
9
- let bits = 0;
10
- for (const byte of bytes) {
11
- buffer = (buffer << 8) | byte;
12
- bits += 8;
13
- while (bits >= 5) {
14
- bits -= 5;
15
- output += ALPHABET[(buffer >>> bits) & 31];
16
- buffer &= (1 << bits) - 1;
17
- }
18
- }
19
- if (bits > 0) output += ALPHABET[(buffer << (5 - bits)) & 31];
20
- return output;
21
- }
22
-
23
- export function decodeBase32(value: string): Uint8Array {
24
- const output: number[] = [];
25
- let buffer = 0;
26
- let bits = 0;
27
- for (const character of value) {
28
- const decoded = VALUES.get(character);
29
- if (decoded === undefined)
30
- throw new Error(`非法 Base32 字符:${character}`);
31
- buffer = (buffer << 5) | decoded;
32
- bits += 5;
33
- if (bits >= 8) {
34
- bits -= 8;
35
- output.push((buffer >>> bits) & 255);
36
- buffer &= (1 << bits) - 1;
37
- }
38
- }
39
- if (bits >= 5 || buffer !== 0) throw new Error("Base32 末尾填充位无效");
40
- return Uint8Array.from(output);
41
- }
42
-
43
- export function segmentChecksum(data: string, segmentIndex: number): string {
44
- let primary = (segmentIndex * 11 + 7) & 31;
45
- let secondary = (segmentIndex * 17 + 19) & 31;
46
- for (let index = 0; index < data.length; index += 1) {
47
- const value = VALUES.get(data[index] || "");
48
- if (value === undefined) throw new Error("分段包含非法字符");
49
- primary = (primary + value * (index * 2 + 1)) & 31;
50
- secondary = (secondary + value * ((index * 6 + 5) & 31)) & 31;
51
- }
52
- return (ALPHABET[primary] || "0") + (ALPHABET[secondary] || "0");
53
- }
54
-
55
- export function isBase32Character(character: string): boolean {
56
- return VALUES.has(character);
57
- }
@@ -1,55 +0,0 @@
1
- import { Unzlib } from "fflate";
2
- import { OFFLINE_ACTIVATION_CODE_LIMITS } from "./offline-code-limits.js";
3
-
4
- const COMPRESSED_CHUNK_BYTES = 64;
5
-
6
- export class OfflineCodeDecompressionLimitError extends Error {
7
- constructor() {
8
- super("离线激活码解压结果超过安全上限");
9
- this.name = "OfflineCodeDecompressionLimitError";
10
- }
11
- }
12
-
13
- /**
14
- * 分块驱动解压器,并在累计输出越过信封声明或协议上限时立即中断。
15
- */
16
- export function decompressOfflineCode(
17
- compressed: Uint8Array,
18
- expectedLength: number,
19
- ): Uint8Array {
20
- if (
21
- compressed.byteLength === 0 ||
22
- compressed.byteLength > OFFLINE_ACTIVATION_CODE_LIMITS.compressedBytes
23
- ) {
24
- throw new OfflineCodeDecompressionLimitError();
25
- }
26
-
27
- const output = new Uint8Array(expectedLength);
28
- let offset = 0;
29
- const stream = new Unzlib((chunk) => {
30
- const remaining = expectedLength - offset;
31
- if (
32
- chunk.byteLength > remaining ||
33
- offset + chunk.byteLength >
34
- OFFLINE_ACTIVATION_CODE_LIMITS.decompressedBytes
35
- ) {
36
- throw new OfflineCodeDecompressionLimitError();
37
- }
38
- output.set(chunk, offset);
39
- offset += chunk.byteLength;
40
- });
41
-
42
- for (
43
- let start = 0;
44
- start < compressed.byteLength;
45
- start += COMPRESSED_CHUNK_BYTES
46
- ) {
47
- const end = Math.min(start + COMPRESSED_CHUNK_BYTES, compressed.byteLength);
48
- stream.push(compressed.subarray(start, end), end === compressed.byteLength);
49
- }
50
-
51
- if (offset !== expectedLength) {
52
- throw new Error("离线激活码解压结果长度不一致");
53
- }
54
- return output;
55
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * W1 安全上限。常见激活码不足 1 KiB;打印与请求上限保留了数十倍余量。
3
- */
4
- export const OFFLINE_ACTIVATION_CODE_LIMITS = {
5
- printedCharacters: 32 * 1024,
6
- normalizedCharacters: 28 * 1024,
7
- compressedBytes: 16 * 1024,
8
- decompressedBytes: 64 * 1024,
9
- requestBytes: 64 * 1024,
10
- } as const;