create-windy 0.2.6 → 0.2.8

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 +14 -3
  2. package/dist/cli.js +191 -87
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/README.md +8 -0
  6. package/template/apps/server/Dockerfile +2 -0
  7. package/template/apps/server/package.json +10 -8
  8. package/template/apps/server/src/access-guards.ts +1 -1
  9. package/template/apps/server/src/admin/routes.test.ts +10 -0
  10. package/template/apps/server/src/admin/routes.ts +4 -2
  11. package/template/apps/server/src/agent/routes.test.ts +1 -1
  12. package/template/apps/server/src/agent/routes.ts +2 -2
  13. package/template/apps/server/src/application-services.ts +3 -0
  14. package/template/apps/server/src/audit/drizzle-log-store.ts +2 -2
  15. package/template/apps/server/src/audit/policy.ts +1 -1
  16. package/template/apps/server/src/audit/recovery-queue.test.ts +1 -1
  17. package/template/apps/server/src/audit/recovery-queue.ts +1 -1
  18. package/template/apps/server/src/audit/redaction.ts +1 -1
  19. package/template/apps/server/src/audit/reliable-writer.test.ts +1 -1
  20. package/template/apps/server/src/audit/reliable-writer.ts +1 -1
  21. package/template/apps/server/src/audit/search-summary.ts +1 -1
  22. package/template/apps/server/src/auth/authentication-provider.test.ts +1 -1
  23. package/template/apps/server/src/auth/credential-service.ts +1 -1
  24. package/template/apps/server/src/auth/drizzle-repository.ts +2 -2
  25. package/template/apps/server/src/auth/drizzle-session-operations.ts +1 -1
  26. package/template/apps/server/src/auth/own-session-routes.test.ts +1 -1
  27. package/template/apps/server/src/auth/password.ts +2 -4
  28. package/template/apps/server/src/auth/repository.ts +1 -1
  29. package/template/apps/server/src/auth/routes.test.ts +1 -1
  30. package/template/apps/server/src/auth/service.ts +1 -1
  31. package/template/apps/server/src/bulk-data/artifact-store.ts +1 -1
  32. package/template/apps/server/src/bulk-data/dictionary-handler.ts +1 -1
  33. package/template/apps/server/src/bulk-data/drizzle-repository.ts +3 -3
  34. package/template/apps/server/src/bulk-data/repository.ts +1 -1
  35. package/template/apps/server/src/bulk-data/routes.test.ts +1 -1
  36. package/template/apps/server/src/bulk-data/routes.ts +1 -1
  37. package/template/apps/server/src/bulk-data/service.test.ts +1 -1
  38. package/template/apps/server/src/bulk-data/service.ts +1 -1
  39. package/template/apps/server/src/bulk-data/types.ts +1 -1
  40. package/template/apps/server/src/configuration/bootstrap.ts +2 -2
  41. package/template/apps/server/src/configuration/definition.ts +2 -2
  42. package/template/apps/server/src/configuration/drizzle-repository.ts +2 -2
  43. package/template/apps/server/src/configuration/repository.ts +1 -1
  44. package/template/apps/server/src/configuration/routes.ts +1 -1
  45. package/template/apps/server/src/configuration/service.ts +1 -1
  46. package/template/apps/server/src/data-access/context.ts +2 -2
  47. package/template/apps/server/src/data-access/query-specification.test.ts +1 -1
  48. package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +1 -1
  49. package/template/apps/server/src/data-access/scoped-repository.ts +1 -1
  50. package/template/apps/server/src/data-access/scoped-resource.test.ts +1 -1
  51. package/template/apps/server/src/data-access/scoped-resource.ts +1 -1
  52. package/template/apps/server/src/data-governance/export/approval-port.ts +1 -1
  53. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -1
  54. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +2 -2
  55. package/template/apps/server/src/data-governance/export/job-runtime.ts +2 -3
  56. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +2 -2
  57. package/template/apps/server/src/data-governance/export/postgres-api.integration.test.ts +1 -1
  58. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +3 -3
  59. package/template/apps/server/src/data-governance/export/routes.ts +1 -1
  60. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +1 -1
  61. package/template/apps/server/src/data-governance/export/service.ts +2 -2
  62. package/template/apps/server/src/data-governance/export/source.ts +1 -1
  63. package/template/apps/server/src/data-governance/export/types.ts +1 -1
  64. package/template/apps/server/src/data-governance/plaintext-access.test.ts +1 -1
  65. package/template/apps/server/src/data-governance/plaintext-access.ts +1 -1
  66. package/template/apps/server/src/data-governance/plaintext-postgres.integration.test.ts +1 -1
  67. package/template/apps/server/src/data-governance/user-plaintext-routes.ts +1 -1
  68. package/template/apps/server/src/data-governance/user-projection.ts +2 -2
  69. package/template/apps/server/src/example-modules.ts +25 -1
  70. package/template/apps/server/src/feature/manifest-feature-repository.test.ts +1 -1
  71. package/template/apps/server/src/feature/manifest-feature-repository.ts +1 -1
  72. package/template/apps/server/src/feature/routes.ts +3 -6
  73. package/template/apps/server/src/foundation-access.test.ts +1 -1
  74. package/template/apps/server/src/foundation-access.ts +1 -1
  75. package/template/apps/server/src/foundation.ts +21 -7
  76. package/template/apps/server/src/guards.test.ts +1 -1
  77. package/template/apps/server/src/guards.ts +3 -3
  78. package/template/apps/server/src/index.ts +27 -3
  79. package/template/{packages/modules → apps/server}/src/installed-business-modules.ts +2 -2
  80. package/template/apps/server/src/jobs/artifact-service.test.ts +1 -1
  81. package/template/apps/server/src/jobs/artifact-service.ts +2 -2
  82. package/template/apps/server/src/jobs/drizzle-artifact.ts +2 -2
  83. package/template/apps/server/src/jobs/drizzle-claim.ts +1 -1
  84. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +2 -2
  85. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +1 -1
  86. package/template/apps/server/src/jobs/drizzle-lease.ts +2 -2
  87. package/template/apps/server/src/jobs/drizzle-mappers.ts +3 -3
  88. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +1 -1
  89. package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -1
  90. package/template/apps/server/src/jobs/drizzle-repository.ts +3 -3
  91. package/template/apps/server/src/jobs/execution-repository.ts +1 -1
  92. package/template/apps/server/src/jobs/execution.test.ts +2 -2
  93. package/template/apps/server/src/jobs/in-memory-claim.ts +1 -1
  94. package/template/apps/server/src/jobs/in-memory-execution.ts +1 -1
  95. package/template/apps/server/src/jobs/in-memory-lease.ts +1 -1
  96. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -1
  97. package/template/apps/server/src/jobs/legacy-adapter.test.ts +1 -1
  98. package/template/apps/server/src/jobs/legacy-adapter.ts +1 -1
  99. package/template/apps/server/src/jobs/legacy-service-runtime.ts +4 -2
  100. package/template/apps/server/src/jobs/repository.ts +2 -2
  101. package/template/apps/server/src/jobs/service.ts +2 -2
  102. package/template/apps/server/src/jobs/worker.ts +3 -2
  103. package/template/apps/server/src/license/legacy-license-resolver.ts +1 -1
  104. package/template/apps/server/src/license/license-resolver.test.ts +1 -1
  105. package/template/apps/server/src/license/license-resolver.ts +3 -3
  106. package/template/apps/server/src/license/routes-offline-code.test.ts +2 -2
  107. package/template/apps/server/src/license/routes.ts +1 -1
  108. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -2
  109. package/template/apps/server/src/license/runtime-reminder.test.ts +2 -2
  110. package/template/apps/server/src/license/runtime-service.test.ts +2 -2
  111. package/template/apps/server/src/license/runtime-service.ts +2 -2
  112. package/template/apps/server/src/module-composition.test.ts +1 -1
  113. package/template/apps/server/src/module-composition.ts +1 -1
  114. package/template/apps/server/src/module-host.test.ts +145 -0
  115. package/template/apps/server/src/module-host.ts +110 -0
  116. package/template/apps/server/src/notification/drizzle-repository.ts +2 -2
  117. package/template/apps/server/src/notification/repository.ts +1 -1
  118. package/template/apps/server/src/notification/routes.ts +1 -1
  119. package/template/apps/server/src/pagination.ts +1 -1
  120. package/template/apps/server/src/persistence.integration.test.ts +1 -1
  121. package/template/apps/server/src/persistence.ts +1 -1
  122. package/template/apps/server/src/profile/routes.ts +1 -1
  123. package/template/apps/server/src/route-guards.test.ts +1 -1
  124. package/template/apps/server/src/route-guards.ts +1 -1
  125. package/template/apps/server/src/runtime-feature.ts +125 -0
  126. package/template/apps/server/src/runtime-license.ts +1 -1
  127. package/template/apps/server/src/runtime.test.ts +48 -1
  128. package/template/apps/server/src/runtime.ts +17 -64
  129. package/template/apps/server/src/scheduler/audit.ts +1 -1
  130. package/template/apps/server/src/scheduler/definitions.test.ts +1 -1
  131. package/template/apps/server/src/scheduler/definitions.ts +2 -2
  132. package/template/apps/server/src/scheduler/drizzle-durable-links.ts +1 -1
  133. package/template/apps/server/src/scheduler/drizzle-mappers.ts +1 -1
  134. package/template/apps/server/src/scheduler/drizzle-repository.ts +1 -1
  135. package/template/apps/server/src/scheduler/execution-recorder.ts +1 -1
  136. package/template/apps/server/src/scheduler/history.ts +1 -1
  137. package/template/apps/server/src/scheduler/overview.ts +1 -1
  138. package/template/apps/server/src/scheduler/recovery-service.test.ts +1 -1
  139. package/template/apps/server/src/scheduler/recovery-service.ts +1 -1
  140. package/template/apps/server/src/scheduler/repository.ts +1 -1
  141. package/template/apps/server/src/scheduler/routes.ts +1 -1
  142. package/template/apps/server/src/scheduler/service-idle.test.ts +1 -1
  143. package/template/apps/server/src/scheduler/service.test.ts +1 -1
  144. package/template/apps/server/src/scheduler/service.ts +1 -1
  145. package/template/apps/server/src/scheduler/types.ts +1 -1
  146. package/template/apps/server/src/search/contracts.ts +2 -2
  147. package/template/apps/server/src/search/host.ts +1 -1
  148. package/template/apps/server/src/search/registry.ts +1 -1
  149. package/template/apps/server/src/search/routes.test.ts +1 -1
  150. package/template/apps/server/src/search/routes.ts +1 -1
  151. package/template/apps/server/src/search/service.ts +2 -2
  152. package/template/apps/server/src/search/test-support.ts +2 -2
  153. package/template/apps/server/src/session/routes.ts +1 -1
  154. package/template/apps/server/src/settings/drizzle-repository.ts +2 -2
  155. package/template/apps/server/src/settings/repository.ts +1 -1
  156. package/template/apps/server/src/settings/routes.test.ts +1 -1
  157. package/template/apps/server/src/settings/routes.ts +1 -1
  158. package/template/apps/server/src/settings/runtime.ts +1 -1
  159. package/template/apps/server/src/storage/blob-reference-registry.test.ts +1 -1
  160. package/template/apps/server/src/storage/blob-reference-registry.ts +1 -1
  161. package/template/apps/server/src/storage/drizzle-upload-repository.integration.test.ts +1 -1
  162. package/template/apps/server/src/storage/drizzle-upload-repository.ts +2 -2
  163. package/template/apps/server/src/storage/route-support.ts +1 -1
  164. package/template/apps/server/src/storage/routes.test.ts +1 -1
  165. package/template/apps/server/src/storage/routes.ts +2 -2
  166. package/template/apps/server/src/storage/runtime.ts +6 -4
  167. package/template/apps/server/src/system/audit-routes.ts +1 -1
  168. package/template/apps/server/src/system/department-routes.ts +1 -1
  169. package/template/apps/server/src/system/drizzle-repository.ts +1 -1
  170. package/template/apps/server/src/system/drizzle-scoped-repository.ts +1 -1
  171. package/template/apps/server/src/system/drizzle-system.ts +1 -1
  172. package/template/apps/server/src/system/entities.ts +1 -1
  173. package/template/apps/server/src/system/identity-route-config.ts +1 -1
  174. package/template/apps/server/src/system/interface-catalog.test.ts +3 -3
  175. package/template/apps/server/src/system/interface-catalog.ts +2 -2
  176. package/template/apps/server/src/system/menu-registry.ts +1 -1
  177. package/template/apps/server/src/system/menu-routes.ts +1 -1
  178. package/template/apps/server/src/system/operations.test.ts +1 -1
  179. package/template/apps/server/src/system/operations.ts +1 -1
  180. package/template/apps/server/src/system/redis-health.ts +1 -1
  181. package/template/apps/server/src/system/repository.ts +1 -1
  182. package/template/apps/server/src/system/resource-route-support.ts +1 -1
  183. package/template/apps/server/src/system/route-helpers.ts +1 -1
  184. package/template/apps/server/src/system/route-types.ts +1 -1
  185. package/template/apps/server/src/system/routes.test.ts +1 -1
  186. package/template/apps/server/src/system/routes.ts +1 -1
  187. package/template/apps/server/src/system/user-projection.ts +1 -1
  188. package/template/apps/server/src/work-order/data-scope.integration.test.ts +1 -1
  189. package/template/apps/server/src/work-order/drizzle-repository.integration.test.ts +1 -1
  190. package/template/apps/server/src/work-order/drizzle-repository.ts +2 -2
  191. package/template/apps/server/src/work-order/foundation-modules.test.ts +15 -5
  192. package/template/apps/server/src/work-order/repository.ts +1 -1
  193. package/template/apps/server/src/work-order/routes.test.ts +2 -2
  194. package/template/apps/server/src/work-order/routes.ts +2 -2
  195. package/template/apps/server/src/work-order/search-api.test.ts +3 -3
  196. package/template/apps/server/src/work-order/search-provider.test.ts +3 -3
  197. package/template/apps/server/src/work-order/service.ts +1 -1
  198. package/template/apps/server/src/work-order/task.test.ts +1 -1
  199. package/template/apps/server/src/work-order/task.ts +2 -2
  200. package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +2 -2
  201. package/template/apps/server/src/workflow/drizzle-repository.ts +3 -3
  202. package/template/apps/server/src/workflow/effects.test.ts +1 -1
  203. package/template/apps/server/src/workflow/effects.ts +1 -1
  204. package/template/apps/server/src/workflow/repository.ts +2 -2
  205. package/template/apps/server/src/workflow/routes.ts +3 -3
  206. package/template/apps/server/src/workflow/service.ts +1 -1
  207. package/template/apps/server/src/workflow/timeout-runtime.test.ts +1 -1
  208. package/template/apps/server/src/workflow/timeout-runtime.ts +2 -2
  209. package/template/apps/web/Dockerfile +2 -0
  210. package/template/apps/web/package.json +4 -4
  211. package/template/apps/web/runtime-server.ts +1 -1
  212. package/template/apps/web/src/app/layouts/AppLayout.vue +6 -0
  213. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +52 -7
  214. package/template/apps/web/src/components/auth/PasswordChangeForm.webtest.ts +43 -0
  215. package/template/apps/web/src/components/common/DataPagination.vue +1 -1
  216. package/template/apps/web/src/components/license/LicenseActivationCodeForm.vue +1 -1
  217. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +2 -2
  218. package/template/apps/web/src/composables/useModuleConfigurations.ts +1 -1
  219. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  220. package/template/apps/web/src/composables/watermark-settings.ts +6 -0
  221. package/template/apps/web/src/composables/watermark-settings.webtest.ts +27 -1
  222. package/template/apps/web/src/layout/AppSidebar.vue +12 -3
  223. package/template/apps/web/src/layout/AppSidebar.webtest.ts +11 -0
  224. package/template/apps/web/src/layout/GlobalWatermark.vue +25 -8
  225. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +109 -0
  226. package/template/apps/web/src/layout/NavigationSearchDialog.vue +1 -1
  227. package/template/apps/web/src/layout/NotificationMenu.vue +1 -1
  228. package/template/apps/web/src/layout/navigation.ts +5 -1
  229. package/template/apps/web/src/layout/navigation.webtest.ts +1 -1
  230. package/template/apps/web/src/pages/configuration/components/ModuleConfigRollbackDialog.vue +1 -1
  231. package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -1
  232. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +1 -1
  233. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.webtest.ts +1 -1
  234. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +1 -1
  235. package/template/apps/web/src/pages/dashboard/dashboard-presenter.webtest.ts +1 -1
  236. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +21 -0
  237. package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +2 -2
  238. package/template/apps/web/src/pages/system/components/AuditActionCell.vue +7 -2
  239. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +7 -2
  240. package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +1 -1
  241. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.vue +1 -1
  242. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.webtest.ts +1 -1
  243. package/template/apps/web/src/pages/system/components/SchedulerRunTable.vue +1 -1
  244. package/template/apps/web/src/pages/system/components/SchedulerRunTable.webtest.ts +1 -1
  245. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.vue +1 -1
  246. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.webtest.ts +1 -1
  247. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -1
  248. package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +10 -3
  249. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.vue +1 -1
  250. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.webtest.ts +1 -1
  251. package/template/apps/web/src/pages/system/components/role-policy/RolePolicyForm.vue +1 -1
  252. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.ts +1 -1
  253. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.webtest.ts +1 -1
  254. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.ts +2 -2
  255. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.webtest.ts +1 -1
  256. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +1 -1
  257. package/template/apps/web/src/router/manifest-routes.ts +1 -1
  258. package/template/apps/web/src/router/manifest-routes.webtest.ts +1 -1
  259. package/template/apps/web/src/services/access-api.ts +10 -1
  260. package/template/apps/web/src/services/configuration-api.ts +1 -1
  261. package/template/apps/web/src/services/license-api.ts +1 -1
  262. package/template/apps/web/src/services/notification-api.ts +2 -2
  263. package/template/apps/web/src/services/operations-api.ts +1 -1
  264. package/template/apps/web/src/services/platform-settings-api.ts +2 -2
  265. package/template/apps/web/src/services/profile-api.ts +2 -2
  266. package/template/docs/architecture/durable-jobs.md +5 -0
  267. package/template/docs/architecture/module-manifest-composition.md +14 -7
  268. package/template/docs/architecture/object-storage.md +1 -1
  269. package/template/docs/development/crud-generator.md +4 -1
  270. package/template/docs/platform/audit-reliability.md +1 -1
  271. package/template/package.json +2 -1
  272. package/template/packages/config/package.json +15 -4
  273. package/template/packages/config/src/platform.ts +1 -1
  274. package/template/packages/crud-generator/package.json +1 -1
  275. package/template/packages/crud-generator/src/generator.test.ts +16 -0
  276. package/template/packages/crud-generator/src/templates/domain.ts +1 -1
  277. package/template/packages/crud-generator/src/templates/manifest.ts +2 -2
  278. package/template/packages/crud-generator/src/templates/project.ts +3 -3
  279. package/template/packages/crud-generator/src/templates/repository.ts +1 -1
  280. package/template/packages/crud-generator/src/templates/routes.ts +2 -2
  281. package/template/packages/crud-generator/src/validation.ts +22 -1
  282. package/template/packages/database/package.json +15 -5
  283. package/template/packages/database/src/bundle-validation.ts +218 -0
  284. package/template/packages/database/src/dialects.ts +1 -1
  285. package/template/packages/database/src/runner.test.ts +160 -13
  286. package/template/packages/database/src/runner.ts +95 -77
  287. package/template/packages/database/src/schema/governed-exports.ts +1 -1
  288. package/template/packages/database/src/schema/identity.ts +1 -1
  289. package/template/packages/database/src/schema/license-v1.ts +1 -1
  290. package/template/packages/database/src/schema/notifications.ts +1 -1
  291. package/template/packages/database/src/schema/platform-settings.ts +1 -1
  292. package/template/packages/database/src/schema/workflow.ts +1 -1
  293. package/template/packages/database/src/sql-store.ts +1 -1
  294. package/template/packages/database/src/store.ts +1 -1
  295. package/template/packages/example-work-order/index.ts +1 -0
  296. package/template/packages/example-work-order/package.json +4 -3
  297. package/template/packages/example-work-order/src/contract.ts +1 -1
  298. package/template/packages/example-work-order/src/manifest.ts +8 -3
  299. package/template/packages/example-work-order/src/migration-bundle.test.ts +43 -0
  300. package/template/packages/example-work-order/src/migration-bundle.ts +64 -0
  301. package/template/packages/jobs/index.ts +2 -0
  302. package/template/packages/jobs/package.json +29 -0
  303. package/template/packages/jobs/src/handlers.test.ts +72 -0
  304. package/template/{apps/server/src/jobs/handler-registry.ts → packages/jobs/src/handlers.ts} +4 -4
  305. package/template/{apps/server/src/jobs → packages/jobs/src}/types.ts +7 -1
  306. package/template/packages/{license-sdk → jobs}/tsconfig.json +0 -1
  307. package/template/packages/modules/index.ts +1 -1
  308. package/template/packages/modules/package.json +15 -5
  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 +4 -2
  312. package/template/packages/modules/src/manifest.ts +7 -12
  313. package/template/packages/modules/src/menu-composition.ts +1 -1
  314. package/template/packages/modules/src/system-api-binding.ts +1 -1
  315. package/template/packages/modules/src/system-api-data-governance.ts +1 -1
  316. package/template/packages/modules/src/system-api-permissions.ts +1 -1
  317. package/template/packages/modules/src/system-data-governance.ts +1 -1
  318. package/template/packages/modules/src/system-features.ts +1 -1
  319. package/template/packages/modules/src/system-governed-export-bindings.ts +1 -1
  320. package/template/packages/modules/src/system-module-catalog.ts +1 -1
  321. package/template/packages/server-sdk/index.ts +5 -0
  322. package/template/packages/server-sdk/package.json +25 -0
  323. package/template/packages/server-sdk/src/module-host.ts +29 -0
  324. package/template/packages/server-sdk/tsconfig.json +16 -0
  325. package/template/packages/shared/index.ts +3 -0
  326. package/template/packages/shared/package.json +14 -3
  327. package/template/packages/shared/src/audit.ts +22 -1
  328. package/template/packages/shared/src/license-catalog.test.ts +17 -0
  329. package/template/packages/shared/src/license-catalog.ts +33 -0
  330. package/template/packages/shared/src/migration.ts +39 -0
  331. package/template/packages/shared/src/password.ts +4 -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,201 +0,0 @@
1
- import type {
2
- LicenseFormatVersion,
3
- LicenseV1Claims,
4
- SignedLicenseV1,
5
- } from "@windy/shared";
6
- import { canonicalizeJson } from "./canonical-json.js";
7
-
8
- export type LicensePayloadErrorCode =
9
- | "INVALID_LICENSE_PAYLOAD"
10
- | "UNSUPPORTED_LICENSE_FORMAT";
11
-
12
- export class LicensePayloadError extends Error {
13
- constructor(
14
- readonly code: LicensePayloadErrorCode,
15
- message: string,
16
- options?: ErrorOptions,
17
- ) {
18
- super(message, options);
19
- this.name = "LicensePayloadError";
20
- }
21
- }
22
-
23
- export interface LicenseV1SigningDocument {
24
- formatVersion: 1;
25
- algorithm: "ED25519";
26
- publicKeyId: string;
27
- claims: LicenseV1Claims;
28
- }
29
-
30
- export interface LicensePayloadHandler<
31
- TVersion extends LicenseFormatVersion,
32
- TDocument,
33
- > {
34
- readonly formatVersion: TVersion;
35
- serialize(document: TDocument): string;
36
- parse(payload: string): TDocument;
37
- }
38
-
39
- export const licenseV1PayloadHandler: LicensePayloadHandler<
40
- 1,
41
- LicenseV1SigningDocument
42
- > = {
43
- formatVersion: 1,
44
- serialize(document) {
45
- try {
46
- assertV1Document(document);
47
- return canonicalizeJson(document);
48
- } catch (error) {
49
- throw normalizePayloadError(error);
50
- }
51
- },
52
- parse(payload) {
53
- try {
54
- const document = parseJson(payload);
55
- assertV1Document(document);
56
- if (canonicalizeJson(document) !== payload) {
57
- invalidPayload("V1 signed payload 必须使用 RFC 8785 canonical JSON");
58
- }
59
- return document;
60
- } catch (error) {
61
- throw normalizePayloadError(error);
62
- }
63
- },
64
- };
65
-
66
- export function getLicensePayloadHandler(
67
- formatVersion: unknown,
68
- ): LicensePayloadHandler<1, LicenseV1SigningDocument> {
69
- if (formatVersion === 1) return licenseV1PayloadHandler;
70
- throw unsupported(formatVersion);
71
- }
72
-
73
- export function createLicenseSigningPayload(
74
- license: Omit<SignedLicenseV1, "signature">,
75
- ): string {
76
- return licenseV1PayloadHandler.serialize(license);
77
- }
78
-
79
- export function parseLicenseSigningPayload(
80
- formatVersion: unknown,
81
- payload: string,
82
- ): LicenseV1SigningDocument {
83
- return getLicensePayloadHandler(formatVersion).parse(payload);
84
- }
85
-
86
- export const parseLicenseSigningDocument = parseLicenseSigningPayload;
87
-
88
- function assertV1Document(
89
- value: unknown,
90
- ): asserts value is LicenseV1SigningDocument {
91
- const document = object(value, "V1 signing document");
92
- exactKeys(document, ["algorithm", "claims", "formatVersion", "publicKeyId"]);
93
- if (document.formatVersion !== 1) invalidPayload("formatVersion 必须为 1");
94
- if (document.algorithm !== "ED25519") {
95
- invalidPayload("algorithm 必须为 ED25519");
96
- }
97
- text(document.publicKeyId, "publicKeyId");
98
- assertV1Claims(document.claims);
99
- }
100
-
101
- function assertV1Claims(value: unknown): asserts value is LicenseV1Claims {
102
- const claims = object(value, "claims");
103
- exactKeys(claims, [
104
- "contract",
105
- "customer",
106
- "expiresAt",
107
- "issuedAt",
108
- "licenseId",
109
- "licenseVersion",
110
- "machineCode",
111
- "productCode",
112
- "project",
113
- ]);
114
- for (const key of [
115
- "licenseId",
116
- "productCode",
117
- "machineCode",
118
- "issuedAt",
119
- "expiresAt",
120
- ] as const) {
121
- text(claims[key], key);
122
- }
123
- reference(claims.customer, "customer");
124
- reference(claims.project, "project");
125
- const contract = object(claims.contract, "contract");
126
- exactKeys(contract, ["contractNumber", "id", "version"]);
127
- for (const key of ["id", "contractNumber", "version"] as const) {
128
- text(contract[key], `contract.${key}`);
129
- }
130
- const version = object(claims.licenseVersion, "licenseVersion");
131
- exactKeys(version, ["key", "name"]);
132
- text(version.key, "licenseVersion.key");
133
- text(version.name, "licenseVersion.name");
134
- }
135
-
136
- function reference(value: unknown, label: string): void {
137
- const ref = object(value, label);
138
- exactKeys(ref, ["code", "id", "name"]);
139
- for (const key of ["id", "code", "name"] as const) {
140
- text(ref[key], `${label}.${key}`);
141
- }
142
- }
143
-
144
- function object(value: unknown, label: string): Record<string, unknown> {
145
- if (!value || typeof value !== "object" || Array.isArray(value)) {
146
- invalidPayload(`${label} 必须为 JSON 对象`);
147
- }
148
- return value as Record<string, unknown>;
149
- }
150
-
151
- function exactKeys(
152
- value: Record<string, unknown>,
153
- allowed: readonly string[],
154
- ): void {
155
- const actual = Object.keys(value);
156
- const unexpected = actual.filter((key) => !allowed.includes(key));
157
- const missing = allowed.filter((key) => !Object.hasOwn(value, key));
158
- if (unexpected.length || missing.length) {
159
- invalidPayload(
160
- `字段不匹配(缺少:${missing.join(",") || "无"};未知:${unexpected.join(",") || "无"})`,
161
- );
162
- }
163
- }
164
-
165
- function text(value: unknown, label: string): void {
166
- if (typeof value !== "string" || !value.length) {
167
- invalidPayload(`${label} 必须为非空字符串`);
168
- }
169
- }
170
-
171
- function parseJson(payload: string): unknown {
172
- try {
173
- return JSON.parse(payload);
174
- } catch (error) {
175
- throw new LicensePayloadError(
176
- "INVALID_LICENSE_PAYLOAD",
177
- "signed payload 不是有效 JSON",
178
- { cause: error },
179
- );
180
- }
181
- }
182
-
183
- function invalidPayload(message: string): never {
184
- throw new LicensePayloadError("INVALID_LICENSE_PAYLOAD", message);
185
- }
186
-
187
- function unsupported(formatVersion: unknown): never {
188
- throw new LicensePayloadError(
189
- "UNSUPPORTED_LICENSE_FORMAT",
190
- `不支持的 License 格式版本:${String(formatVersion)}`,
191
- );
192
- }
193
-
194
- function normalizePayloadError(error: unknown): LicensePayloadError {
195
- if (error instanceof LicensePayloadError) return error;
196
- return new LicensePayloadError(
197
- "INVALID_LICENSE_PAYLOAD",
198
- "License payload 不是有效的 canonical JSON",
199
- { cause: error },
200
- );
201
- }
@@ -1,64 +0,0 @@
1
- export interface LicenseStorage {
2
- read(key: string): string | undefined | Promise<string | undefined>;
3
- write(key: string, value: string): void | Promise<void>;
4
- }
5
-
6
- export interface LicenseSource {
7
- readCurrent(): string | undefined | Promise<string | undefined>;
8
- }
9
-
10
- /**
11
- * Adapter boundary for a single-current-License store.
12
- * Implementations must leave either the old or the new complete document readable.
13
- */
14
- export interface AtomicLicenseStore extends LicenseSource {
15
- replaceCurrentAtomically(value: string): void | Promise<void>;
16
- }
17
-
18
- export class MemoryLicenseStorage implements LicenseStorage {
19
- private readonly values = new Map<string, string>();
20
-
21
- read(key: string): string | undefined {
22
- return this.values.get(key);
23
- }
24
-
25
- write(key: string, value: string): void {
26
- this.values.set(key, value);
27
- }
28
- }
29
-
30
- export class MemoryAtomicLicenseStore implements AtomicLicenseStore {
31
- constructor(private current?: string) {}
32
-
33
- readCurrent(): string | undefined {
34
- return this.current;
35
- }
36
-
37
- replaceCurrentAtomically(value: string): void {
38
- this.current = value;
39
- }
40
- }
41
-
42
- export class BrowserLicenseStorage implements LicenseStorage {
43
- constructor(private readonly prefix = "windy-license") {}
44
-
45
- read(key: string): string | undefined {
46
- if (typeof window === "undefined") {
47
- return undefined;
48
- }
49
-
50
- return window.localStorage.getItem(this.storageKey(key)) || undefined;
51
- }
52
-
53
- write(key: string, value: string): void {
54
- if (typeof window === "undefined") {
55
- return;
56
- }
57
-
58
- window.localStorage.setItem(this.storageKey(key), value);
59
- }
60
-
61
- private storageKey(key: string): string {
62
- return `${this.prefix}:${key}`;
63
- }
64
- }
@@ -1,59 +0,0 @@
1
- import { generateKeyPairSync, sign } from "node:crypto";
2
- import type {
3
- LegacyPrototypeLicenseClaims as LicenseClaims,
4
- LegacyPrototypeSignedLicense as SignedLicense,
5
- LicenseV1Claims,
6
- SignedLicenseV1,
7
- } from "@windy/shared";
8
- import { createLicenseSigningPayload } from "./payload.js";
9
- import { legacyPrototypeLicensePayloadReader } from "./verification.js";
10
-
11
- export function createSignedLicense(inputClaims: LicenseClaims): {
12
- license: SignedLicense;
13
- publicKeyPem: string;
14
- } {
15
- const { privateKey, publicKey } = generateKeyPairSync("ed25519");
16
- const signature = sign(
17
- null,
18
- Buffer.from(
19
- legacyPrototypeLicensePayloadReader.serializeForVerification(inputClaims),
20
- ),
21
- privateKey,
22
- ).toString("base64");
23
-
24
- return {
25
- publicKeyPem: publicKey.export({ type: "spki", format: "pem" }).toString(),
26
- license: {
27
- claims: inputClaims,
28
- signature,
29
- publicKeyId: "key_1",
30
- algorithm: "ED25519",
31
- },
32
- };
33
- }
34
-
35
- export function createSignedLicenseV1(
36
- claims: LicenseV1Claims,
37
- keyId = "key_v1",
38
- ): {
39
- license: SignedLicenseV1;
40
- publicKeyPem: string;
41
- } {
42
- const { privateKey, publicKey } = generateKeyPairSync("ed25519");
43
- const unsigned = {
44
- formatVersion: 1,
45
- algorithm: "ED25519",
46
- publicKeyId: keyId,
47
- claims,
48
- } as const;
49
- const signature = sign(
50
- null,
51
- Buffer.from(createLicenseSigningPayload(unsigned)),
52
- privateKey,
53
- ).toString("base64");
54
-
55
- return {
56
- publicKeyPem: publicKey.export({ type: "spki", format: "pem" }).toString(),
57
- license: { ...unsigned, signature },
58
- };
59
- }
@@ -1,49 +0,0 @@
1
- import { licenseVersionKey, type LicenseV1Claims } from "@windy/shared";
2
-
3
- /** Public, non-secret interoperability vector shared by SDK and issuer tests. */
4
- export const LICENSE_V1_TEST_VECTOR: {
5
- claims: LicenseV1Claims;
6
- keyId: string;
7
- publicKeyPem: string;
8
- signedPayload: string;
9
- payloadDigest: string;
10
- signature: string;
11
- } = {
12
- claims: {
13
- licenseId: "license_vector_v1",
14
- productCode: "windy-platform",
15
- customer: {
16
- id: "customer_vector",
17
- code: "C-001",
18
- name: "固定向量客户",
19
- },
20
- project: {
21
- id: "project_vector",
22
- code: "P-001",
23
- name: "固定向量项目",
24
- },
25
- contract: {
26
- id: "contract_vector",
27
- contractNumber: "HT-2026-001",
28
- version: "1",
29
- },
30
- machineCode: "machine-vector-001",
31
- issuedAt: "2026-07-10T00:00:00.000Z",
32
- expiresAt: "2030-07-10T00:00:00.000Z",
33
- licenseVersion: {
34
- key: licenseVersionKey("advanced"),
35
- name: "高级付费版",
36
- },
37
- },
38
- keyId: "development-version-model-v1",
39
- publicKeyPem:
40
- "-----BEGIN PUBLIC KEY-----\n" +
41
- "MCowBQYDK2VwAyEAbYwI4vgDmeP9rj+Vu5QkqW83LZsKJJCs1athjcE5J5s=\n" +
42
- "-----END PUBLIC KEY-----\n",
43
- signedPayload:
44
- '{"algorithm":"ED25519","claims":{"contract":{"contractNumber":"HT-2026-001","id":"contract_vector","version":"1"},"customer":{"code":"C-001","id":"customer_vector","name":"固定向量客户"},"expiresAt":"2030-07-10T00:00:00.000Z","issuedAt":"2026-07-10T00:00:00.000Z","licenseId":"license_vector_v1","licenseVersion":{"key":"advanced","name":"高级付费版"},"machineCode":"machine-vector-001","productCode":"windy-platform","project":{"code":"P-001","id":"project_vector","name":"固定向量项目"}},"formatVersion":1,"publicKeyId":"development-version-model-v1"}',
45
- payloadDigest:
46
- "1cbe2a782b74cb79ba03bc987b0736155db56742a9bc619f612af2a162ec535b",
47
- signature:
48
- "y1tMY82+iYlAfzRvC+IdZoLlufZ3b7EW0TysskwZHi7fXye07K6vQ9cTSvkg3dn+vhMeLeyt+3VjpOLhxFTQBQ==",
49
- };
@@ -1,62 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { licenseVersionKey, type LicenseV1Claims } from "@windy/shared";
3
- import { createSignedLicenseV1 } from "./test-fixtures.js";
4
- import { validateLicenseDocument } from "./validation.js";
5
-
6
- function claims(overrides: Partial<LicenseV1Claims> = {}): LicenseV1Claims {
7
- return {
8
- licenseId: "license_1",
9
- productCode: "windy-platform",
10
- customer: { id: "customer_1", code: "C-1", name: "客户一" },
11
- project: { id: "project_1", code: "P-1", name: "项目一" },
12
- contract: { id: "contract_1", contractNumber: "HT-1", version: "1" },
13
- machineCode: "machine_1",
14
- issuedAt: "2026-07-10T00:00:00.000Z",
15
- expiresAt: "2027-07-10T00:00:00.000Z",
16
- licenseVersion: {
17
- key: licenseVersionKey("standard"),
18
- name: "普通付费版",
19
- },
20
- ...overrides,
21
- };
22
- }
23
-
24
- describe("V1 runtime validation", () => {
25
- test.each([
26
- ["productCode", { productCode: "other-product" }, "product-mismatch"],
27
- ["machineCode", { machineCode: "other-machine" }, "machine-mismatch"],
28
- ["expiresAt", { expiresAt: "2026-07-10T09:00:00.000Z" }, "expired"],
29
- ] as const)("拒绝不匹配或过期的 %s", async (_label, overrides, failure) => {
30
- const signed = createSignedLicenseV1(claims(overrides));
31
- const result = await validateLicenseDocument(
32
- JSON.stringify(signed.license),
33
- {
34
- productCode: "windy-platform",
35
- machineCode: "machine_1",
36
- publicKeys: { key_v1: signed.publicKeyPem },
37
- now: new Date("2026-07-10T10:00:00.000Z"),
38
- },
39
- );
40
-
41
- expect(result.valid).toBe(false);
42
- expect(result.failures).toContain(failure);
43
- });
44
-
45
- test("严格拒绝 V1 未声明的 notBefore 字段", async () => {
46
- const signed = createSignedLicenseV1(claims());
47
- const raw = JSON.stringify({
48
- ...signed.license,
49
- notBefore: "2026-07-10T00:00:00.000Z",
50
- });
51
-
52
- const result = await validateLicenseDocument(raw, {
53
- productCode: "windy-platform",
54
- machineCode: "machine_1",
55
- publicKeys: { key_v1: signed.publicKeyPem },
56
- now: new Date("2026-07-10T10:00:00.000Z"),
57
- });
58
-
59
- expect(result.valid).toBe(false);
60
- expect(result.failures).toEqual(["invalid-format"]);
61
- });
62
- });
@@ -1,143 +0,0 @@
1
- import type {
2
- LicenseFailureReason,
3
- LicenseUsageRecord,
4
- SignedLicense,
5
- } from "@windy/shared";
6
- import { parseSignedLicense } from "./license-parser.js";
7
- import type { LicensePublicKeySource } from "./verification.js";
8
- import { verifySignedLicense } from "./verification.js";
9
-
10
- export { parseSignedLicense, parseSignedLicenseV1 } from "./license-parser.js";
11
-
12
- const DEFAULT_CLOCK_TOLERANCE_MS = 5 * 60 * 1000;
13
- const RFC_3339 =
14
- /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
15
-
16
- export interface LicenseValidationOptions {
17
- productCode: string;
18
- machineCode: string;
19
- publicKeys: LicensePublicKeySource;
20
- now?: Date;
21
- lastTrustedAt?: string;
22
- clockToleranceMs?: number;
23
- }
24
-
25
- export interface LicenseDocumentVerificationResult {
26
- valid: boolean;
27
- license?: SignedLicense;
28
- failures: LicenseFailureReason[];
29
- }
30
-
31
- export interface LicenseRuntimeValidationResult extends LicenseDocumentVerificationResult {
32
- usage: LicenseUsageRecord;
33
- nextTrustedAt?: string;
34
- }
35
-
36
- export async function verifyLicenseDocument(
37
- rawLicense: string,
38
- publicKeys: LicensePublicKeySource,
39
- ): Promise<LicenseDocumentVerificationResult> {
40
- const license = parseSignedLicense(rawLicense);
41
- if (!license) return { valid: false, failures: ["invalid-format"] };
42
-
43
- const result = await verifySignedLicense(license, publicKeys);
44
- return result.valid
45
- ? { valid: true, license, failures: [] }
46
- : { valid: false, failures: result.failures };
47
- }
48
-
49
- export async function validateLicenseDocument(
50
- rawLicense: string,
51
- options: LicenseValidationOptions,
52
- ): Promise<LicenseRuntimeValidationResult> {
53
- const now = options.now ?? new Date();
54
- const openedAt = validNow(now);
55
- const verified = await verifyLicenseDocument(rawLicense, options.publicKeys);
56
- const failures = [...verified.failures];
57
- const license = verified.license;
58
-
59
- if (license) {
60
- const issuedAt = parseDateTime(license.claims.issuedAt);
61
- const expiresAt = parseDateTime(license.claims.expiresAt);
62
- if (
63
- issuedAt === undefined ||
64
- expiresAt === undefined ||
65
- expiresAt <= issuedAt
66
- ) {
67
- failures.push("invalid-format");
68
- } else if (now.getTime() > expiresAt) {
69
- failures.push("expired");
70
- }
71
- if (license.claims.productCode !== options.productCode) {
72
- failures.push("product-mismatch");
73
- }
74
- if (license.claims.machineCode !== options.machineCode) {
75
- failures.push("machine-mismatch");
76
- }
77
- }
78
-
79
- const clock = evaluateClock(
80
- now.getTime(),
81
- options.lastTrustedAt,
82
- options.clockToleranceMs ?? DEFAULT_CLOCK_TOLERANCE_MS,
83
- );
84
- if (clock.rollback) failures.push("clock-rollback");
85
-
86
- const uniqueFailures = [...new Set(failures)];
87
- const valid = Boolean(license) && uniqueFailures.length === 0;
88
- return {
89
- valid,
90
- license,
91
- failures: uniqueFailures,
92
- nextTrustedAt: clock.nextTrustedAt,
93
- usage: {
94
- machineCode: options.machineCode,
95
- openedAt,
96
- previousOpenedAt: options.lastTrustedAt,
97
- status: uniqueFailures.includes("clock-rollback")
98
- ? "clock-rollback"
99
- : valid
100
- ? "valid"
101
- : uniqueFailures.includes("expired")
102
- ? "expired"
103
- : "invalid",
104
- reason: uniqueFailures[0],
105
- },
106
- };
107
- }
108
-
109
- function evaluateClock(
110
- now: number,
111
- lastTrustedAt: string | undefined,
112
- toleranceMs: number,
113
- ): { rollback: boolean; nextTrustedAt?: string } {
114
- if (!Number.isFinite(toleranceMs) || toleranceMs < 0) {
115
- throw new RangeError("clockToleranceMs 必须为非负有限数");
116
- }
117
- if (!lastTrustedAt) {
118
- return { rollback: false, nextTrustedAt: new Date(now).toISOString() };
119
- }
120
- const previous = parseDateTime(lastTrustedAt);
121
- if (previous === undefined) {
122
- return { rollback: true };
123
- }
124
- if (now + toleranceMs < previous) {
125
- return { rollback: true, nextTrustedAt: lastTrustedAt };
126
- }
127
- return {
128
- rollback: false,
129
- nextTrustedAt: new Date(Math.max(now, previous)).toISOString(),
130
- };
131
- }
132
-
133
- function parseDateTime(value: string): number | undefined {
134
- if (!RFC_3339.test(value)) return undefined;
135
- const timestamp = Date.parse(value);
136
- return Number.isFinite(timestamp) ? timestamp : undefined;
137
- }
138
-
139
- function validNow(now: Date): string {
140
- if (!Number.isFinite(now.getTime()))
141
- throw new RangeError("now 必须是有效时间");
142
- return now.toISOString();
143
- }
@@ -1,97 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import type {
3
- LegacyPrototypeLicenseClaims as LicenseClaims,
4
- LegacyPrototypeSignedLicense as SignedLicense,
5
- } from "@windy/shared";
6
- import { createSignedLicense } from "./test-fixtures.js";
7
- import { LICENSE_V1_TEST_VECTOR } from "./v1-test-vector.js";
8
- import {
9
- legacyPrototypeLicensePayloadReader,
10
- verifyLicensePayloadSignature,
11
- verifyLegacyPrototypeSignedLicense,
12
- verifySignedLicense,
13
- } from "./verification.js";
14
-
15
- const claims: LicenseClaims = {
16
- licenseId: "license_1",
17
- subject: "演示客户",
18
- edition: "standard",
19
- issuedAt: "2026-07-09T00:00:00.000Z",
20
- machineCode: "machine_1",
21
- limits: { maxUsers: 10 },
22
- modules: [{ module: "system", enabled: true }],
23
- };
24
-
25
- describe("License verification", () => {
26
- test("使用 ED25519 公钥验签", async () => {
27
- const { license, publicKeyPem } = createSignedLicense(claims);
28
-
29
- const result = await verifyLegacyPrototypeSignedLicense(license, {
30
- key_1: publicKeyPem,
31
- });
32
-
33
- expect(result.valid).toBe(true);
34
- expect(result.claims?.licenseId).toBe("license_1");
35
- expect(result.failures).toEqual([]);
36
- });
37
-
38
- test("拒绝 claims 被篡改的 License", async () => {
39
- const { license, publicKeyPem } = createSignedLicense(claims);
40
- const tampered: SignedLicense = {
41
- ...license,
42
- claims: {
43
- ...license.claims,
44
- machineCode: "other_machine",
45
- },
46
- };
47
-
48
- const result = await verifyLegacyPrototypeSignedLicense(tampered, {
49
- key_1: publicKeyPem,
50
- });
51
-
52
- expect(result.valid).toBe(false);
53
- expect(result.failures).toContain("invalid-signature");
54
- });
55
-
56
- test("拒绝缺少公钥的 License", async () => {
57
- const { license } = createSignedLicense(claims);
58
-
59
- const result = await verifyLegacyPrototypeSignedLicense(license, {});
60
-
61
- expect(result.valid).toBe(false);
62
- expect(result.failures).toContain("invalid-signature");
63
- });
64
-
65
- test("支持原始 payload 验签,避免 JSONB 重新序列化", async () => {
66
- const { license, publicKeyPem } = createSignedLicense(claims);
67
- const payload =
68
- legacyPrototypeLicensePayloadReader.serializeForVerification(claims);
69
-
70
- await expect(
71
- verifyLicensePayloadSignature(payload, license.signature, publicKeyPem),
72
- ).resolves.toBe(true);
73
- await expect(
74
- verifyLicensePayloadSignature(
75
- payload.replace("machine_1", "machine_2"),
76
- license.signature,
77
- publicKeyPem,
78
- ),
79
- ).resolves.toBe(false);
80
- });
81
-
82
- test("SDK 可验证签发端共享的 V1 固定向量", async () => {
83
- const vector = LICENSE_V1_TEST_VECTOR;
84
- await expect(
85
- verifySignedLicense(
86
- {
87
- formatVersion: 1,
88
- algorithm: "ED25519",
89
- publicKeyId: vector.keyId,
90
- claims: vector.claims,
91
- signature: vector.signature,
92
- },
93
- { [vector.keyId]: vector.publicKeyPem },
94
- ),
95
- ).resolves.toMatchObject({ valid: true, failures: [] });
96
- });
97
- });