create-windy 0.2.7 → 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 (358) hide show
  1. package/README.md +6 -0
  2. package/dist/cli.js +180 -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 +27 -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/catalog-validation.ts +123 -0
  308. package/template/packages/modules/src/composition.test.ts +75 -2
  309. package/template/packages/modules/src/composition.ts +4 -2
  310. package/template/packages/modules/src/manifest.ts +7 -12
  311. package/template/packages/modules/src/menu-composition.ts +1 -1
  312. package/template/packages/modules/src/system-api-binding.ts +1 -1
  313. package/template/packages/modules/src/system-api-data-governance.ts +1 -1
  314. package/template/packages/modules/src/system-api-permissions.ts +1 -1
  315. package/template/packages/modules/src/system-data-governance.ts +1 -1
  316. package/template/packages/modules/src/system-features.ts +1 -1
  317. package/template/packages/modules/src/system-governed-export-bindings.ts +1 -1
  318. package/template/packages/modules/src/system-module-catalog.ts +1 -1
  319. package/template/packages/server-sdk/index.ts +5 -0
  320. package/template/packages/server-sdk/package.json +25 -0
  321. package/template/packages/server-sdk/src/module-host.ts +29 -0
  322. package/template/packages/server-sdk/tsconfig.json +16 -0
  323. package/template/packages/shared/index.ts +2 -0
  324. package/template/packages/shared/package.json +14 -3
  325. package/template/packages/shared/src/audit.ts +22 -1
  326. package/template/packages/shared/src/license-catalog.test.ts +17 -0
  327. package/template/packages/shared/src/license-catalog.ts +33 -0
  328. package/template/packages/shared/src/migration.ts +39 -0
  329. package/template/packages/storage/package.json +14 -3
  330. package/template/packages/license-sdk/index.ts +0 -13
  331. package/template/packages/license-sdk/package.json +0 -18
  332. package/template/packages/license-sdk/src/bounded-json-body.test.ts +0 -65
  333. package/template/packages/license-sdk/src/bounded-json-body.ts +0 -135
  334. package/template/packages/license-sdk/src/canonical-json.test.ts +0 -28
  335. package/template/packages/license-sdk/src/canonical-json.ts +0 -104
  336. package/template/packages/license-sdk/src/client.test.ts +0 -82
  337. package/template/packages/license-sdk/src/client.ts +0 -86
  338. package/template/packages/license-sdk/src/device.ts +0 -46
  339. package/template/packages/license-sdk/src/file-storage.test.ts +0 -80
  340. package/template/packages/license-sdk/src/file-storage.ts +0 -77
  341. package/template/packages/license-sdk/src/installation.test.ts +0 -241
  342. package/template/packages/license-sdk/src/installation.ts +0 -150
  343. package/template/packages/license-sdk/src/license-parser.ts +0 -56
  344. package/template/packages/license-sdk/src/offline-code-base32.ts +0 -57
  345. package/template/packages/license-sdk/src/offline-code-decompression.ts +0 -55
  346. package/template/packages/license-sdk/src/offline-code-limits.ts +0 -10
  347. package/template/packages/license-sdk/src/offline-code-wire.ts +0 -72
  348. package/template/packages/license-sdk/src/offline-code.test.ts +0 -172
  349. package/template/packages/license-sdk/src/offline-code.ts +0 -319
  350. package/template/packages/license-sdk/src/payload.test.ts +0 -72
  351. package/template/packages/license-sdk/src/payload.ts +0 -201
  352. package/template/packages/license-sdk/src/storage.ts +0 -64
  353. package/template/packages/license-sdk/src/test-fixtures.ts +0 -59
  354. package/template/packages/license-sdk/src/v1-test-vector.ts +0 -49
  355. package/template/packages/license-sdk/src/validation.test.ts +0 -62
  356. package/template/packages/license-sdk/src/validation.ts +0 -143
  357. package/template/packages/license-sdk/src/verification.test.ts +0 -97
  358. package/template/packages/license-sdk/src/verification.ts +0 -199
@@ -1,46 +0,0 @@
1
- export interface DeviceCodeParts {
2
- hostname?: string;
3
- platform?: string;
4
- arch?: string;
5
- tenant?: string;
6
- }
7
-
8
- export function createDeviceCode(parts: DeviceCodeParts): string {
9
- const stableParts = [
10
- parts.hostname || "unknown-host",
11
- parts.platform || "unknown-platform",
12
- parts.arch || "unknown-arch",
13
- parts.tenant || "default",
14
- ];
15
-
16
- return btoaSafe(stableParts.join("|"));
17
- }
18
-
19
- export function createDeviceQrPayload(machineCode: string): string {
20
- return JSON.stringify({
21
- type: "windy-device-code",
22
- version: 1,
23
- machineCode,
24
- });
25
- }
26
-
27
- export function readDeviceCodeFromQrPayload(
28
- payload: string,
29
- ): string | undefined {
30
- try {
31
- const parsed = JSON.parse(payload) as { machineCode?: unknown };
32
- return typeof parsed.machineCode === "string"
33
- ? parsed.machineCode
34
- : undefined;
35
- } catch {
36
- return payload.trim() || undefined;
37
- }
38
- }
39
-
40
- function btoaSafe(value: string): string {
41
- if (typeof btoa === "function") {
42
- return btoa(value);
43
- }
44
-
45
- return Buffer.from(value, "utf8").toString("base64");
46
- }
@@ -1,80 +0,0 @@
1
- import { afterEach, describe, expect, test } from "bun:test";
2
- import { mkdtemp, readdir, rm, writeFile } from "node:fs/promises";
3
- import { join } from "node:path";
4
- import { tmpdir } from "node:os";
5
- import { WindyLicenseClient } from "./client.js";
6
- import {
7
- FileSystemAtomicLicenseStore,
8
- FixedPathLicenseSource,
9
- } from "./file-storage.js";
10
- import { MemoryLicenseStorage } from "./storage.js";
11
- import { LICENSE_V1_TEST_VECTOR } from "./v1-test-vector.js";
12
-
13
- const temporaryDirectories: string[] = [];
14
-
15
- afterEach(async () => {
16
- await Promise.all(
17
- temporaryDirectories
18
- .splice(0)
19
- .map((directory) => rm(directory, { force: true, recursive: true })),
20
- );
21
- });
22
-
23
- describe("filesystem License adapters", () => {
24
- test("固定路径挂载复用统一 V1 校验器", async () => {
25
- const directory = await temporaryDirectory();
26
- const path = join(directory, "runtime", "current.license.json");
27
- const store = new FileSystemAtomicLicenseStore(path);
28
- await store.replaceCurrentAtomically(
29
- JSON.stringify({
30
- formatVersion: 1,
31
- algorithm: "ED25519",
32
- publicKeyId: LICENSE_V1_TEST_VECTOR.keyId,
33
- claims: LICENSE_V1_TEST_VECTOR.claims,
34
- signature: LICENSE_V1_TEST_VECTOR.signature,
35
- }),
36
- );
37
-
38
- const client = new WindyLicenseClient({
39
- licenseSource: new FixedPathLicenseSource(path),
40
- stateStorage: new MemoryLicenseStorage(),
41
- productCode: "windy-platform",
42
- machineCode: "machine-vector-001",
43
- publicKeys: {
44
- [LICENSE_V1_TEST_VECTOR.keyId]: LICENSE_V1_TEST_VECTOR.publicKeyPem,
45
- },
46
- now: () => new Date("2026-07-10T10:00:00.000Z"),
47
- });
48
-
49
- await expect(client.recordOpen()).resolves.toMatchObject({
50
- valid: true,
51
- failures: [],
52
- });
53
- });
54
-
55
- test("模拟原子提交前崩溃时旧文件保持完整且临时文件被清理", async () => {
56
- const directory = await temporaryDirectory();
57
- const path = join(directory, "current.license.json");
58
- await writeFile(path, '{"license":"old"}', "utf8");
59
- const store = new CrashBeforeCommitStore(path);
60
-
61
- await expect(
62
- store.replaceCurrentAtomically('{"license":"new"}'),
63
- ).rejects.toThrow("模拟进程崩溃");
64
-
65
- await expect(store.readCurrent()).resolves.toBe('{"license":"old"}');
66
- expect(await readdir(directory)).toEqual(["current.license.json"]);
67
- });
68
- });
69
-
70
- class CrashBeforeCommitStore extends FileSystemAtomicLicenseStore {
71
- protected override async beforeCommit(): Promise<void> {
72
- throw new Error("模拟进程崩溃");
73
- }
74
- }
75
-
76
- async function temporaryDirectory(): Promise<string> {
77
- const directory = await mkdtemp(join(tmpdir(), "windy-license-sdk-"));
78
- temporaryDirectories.push(directory);
79
- return directory;
80
- }
@@ -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
- }