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,5 +1,11 @@
1
1
  import type { EntityId, ISODateTime } from "./primitives.js";
2
2
 
3
+ export interface AuditActionDefinition {
4
+ key: AuditActionType;
5
+ label: string;
6
+ description: string;
7
+ }
8
+
3
9
  export const AUDIT_ACTION_DEFINITIONS = [
4
10
  { key: "auth.login", label: "账号登录", description: "用户登录平台。" },
5
11
  { key: "auth.logout", label: "账号退出", description: "用户退出平台。" },
@@ -200,7 +206,22 @@ export const AUDIT_ACTION_DEFINITIONS = [
200
206
  },
201
207
  ] as const;
202
208
 
203
- export type AuditActionType = (typeof AUDIT_ACTION_DEFINITIONS)[number]["key"];
209
+ export type BuiltInAuditActionType =
210
+ (typeof AUDIT_ACTION_DEFINITIONS)[number]["key"];
211
+
212
+ /**
213
+ * 审计日志持久化保持字符串协议;业务动作是否有效由 Module Composition
214
+ * 对 AuditActionDefinition 目录进行 fail-fast 校验。
215
+ */
216
+ export type AuditActionType = BuiltInAuditActionType | (string & {});
217
+
218
+ export type AuditActionRegistration = AuditActionType | AuditActionDefinition;
219
+
220
+ export function isAuditActionDefinition(
221
+ registration: AuditActionRegistration,
222
+ ): registration is AuditActionDefinition {
223
+ return typeof registration !== "string";
224
+ }
204
225
 
205
226
  export type AuditOutcome = "attempted" | "success" | "failure" | "denied";
206
227
 
@@ -0,0 +1,17 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { LICENSE_CATALOG } from "./license-catalog.js";
3
+
4
+ describe("License Catalog", () => {
5
+ test("提供稳定且有序的默认产品版本", () => {
6
+ expect(LICENSE_CATALOG.map(({ key }) => key)).toEqual([
7
+ "free-trial",
8
+ "standard",
9
+ "advanced",
10
+ ]);
11
+ expect(
12
+ LICENSE_CATALOG.every(
13
+ ({ label, description }) => label.length > 0 && description.length > 0,
14
+ ),
15
+ ).toBe(true);
16
+ });
17
+ });
@@ -0,0 +1,33 @@
1
+ import { licenseVersionKey, type LicenseVersionKey } from "./license.js";
2
+
3
+ export interface LicenseCatalogDefinition {
4
+ key: LicenseVersionKey;
5
+ label: string;
6
+ description: string;
7
+ order: number;
8
+ }
9
+
10
+ export const LICENSE_CATALOG = [
11
+ {
12
+ key: licenseVersionKey("free-trial"),
13
+ label: "免费试用版",
14
+ description: "用于产品体验和非生产验证的基础版本。",
15
+ order: 10,
16
+ },
17
+ {
18
+ key: licenseVersionKey("standard"),
19
+ label: "标准版",
20
+ description: "面向常规企业业务场景的标准能力版本。",
21
+ order: 20,
22
+ },
23
+ {
24
+ key: licenseVersionKey("advanced"),
25
+ label: "高级版",
26
+ description: "包含高级治理、自动化和大批量处理能力。",
27
+ order: 30,
28
+ },
29
+ ] as const satisfies readonly LicenseCatalogDefinition[];
30
+
31
+ export function licenseCatalogKeys(): ReadonlySet<LicenseVersionKey> {
32
+ return new Set(LICENSE_CATALOG.map(({ key }) => key));
33
+ }
@@ -0,0 +1,39 @@
1
+ import type { MigrationExecutionContext } from "./database.js";
2
+
3
+ export const MIGRATION_BUNDLE_FORMAT_VERSION = "1" as const;
4
+
5
+ export type MigrationDirection = "up" | "down";
6
+
7
+ export interface MigrationDefinition {
8
+ id: string;
9
+ module: string;
10
+ description: string;
11
+ direction: MigrationDirection;
12
+ checksum?: string;
13
+ dependsOn?: readonly string[];
14
+ run(context: MigrationExecutionContext): Promise<void>;
15
+ }
16
+
17
+ export interface BundleMigrationDefinition extends MigrationDefinition {
18
+ checksum: string;
19
+ }
20
+
21
+ export interface MigrationBundle {
22
+ formatVersion: typeof MIGRATION_BUNDLE_FORMAT_VERSION;
23
+ module: string;
24
+ version: string;
25
+ dependencies?: readonly string[];
26
+ migrations: readonly BundleMigrationDefinition[];
27
+ }
28
+
29
+ export interface MigrationManifestSource {
30
+ name: string;
31
+ version: string;
32
+ dependencies?: readonly string[];
33
+ migrations: readonly MigrationDefinition[];
34
+ schemaExports?: readonly {
35
+ key: string;
36
+ migrationIds: readonly string[];
37
+ }[];
38
+ migrationBundle?: MigrationBundle;
39
+ }
@@ -1,12 +1,23 @@
1
1
  {
2
- "name": "@windy/storage",
3
- "version": "0.1.0",
4
- "private": true,
2
+ "name": "@southwind-ai/storage",
3
+ "version": "0.1.1",
4
+ "license": "UNLICENSED",
5
5
  "type": "module",
6
+ "engines": {
7
+ "bun": ">=1.3.0"
8
+ },
9
+ "files": [
10
+ "index.ts",
11
+ "src/**/*.ts",
12
+ "!src/**/*.test.ts"
13
+ ],
6
14
  "scripts": {
7
15
  "typecheck": "tsc --noEmit --project tsconfig.json"
8
16
  },
9
17
  "exports": {
10
18
  ".": "./index.ts"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
11
22
  }
12
23
  }
@@ -1,13 +0,0 @@
1
- export * from "./src/canonical-json.js";
2
- export * from "./src/client.js";
3
- export * from "./src/device.js";
4
- export * from "./src/file-storage.js";
5
- export * from "./src/installation.js";
6
- export * from "./src/offline-code.js";
7
- export * from "./src/offline-code-limits.js";
8
- export * from "./src/bounded-json-body.js";
9
- export * from "./src/payload.js";
10
- export * from "./src/storage.js";
11
- export * from "./src/v1-test-vector.js";
12
- export * from "./src/validation.js";
13
- export * from "./src/verification.js";
@@ -1,18 +0,0 @@
1
- {
2
- "name": "@windy/license-sdk",
3
- "version": "0.1.0",
4
- "private": true,
5
- "type": "module",
6
- "scripts": {
7
- "typecheck": "tsc --noEmit --project tsconfig.json"
8
- },
9
- "dependencies": {
10
- "@windy/shared": "workspace:*",
11
- "fflate": "^0.8.3"
12
- },
13
- "exports": {
14
- ".": "./index.ts",
15
- "./offline-code": "./src/offline-code.ts",
16
- "./bounded-json-body": "./src/bounded-json-body.ts"
17
- }
18
- }
@@ -1,65 +0,0 @@
1
- import { expect, test } from "bun:test";
2
- import {
3
- BoundedJsonBodyError,
4
- readBoundedJsonObject,
5
- } from "./bounded-json-body.js";
6
- import { OFFLINE_ACTIVATION_CODE_LIMITS } from "./offline-code-limits.js";
7
-
8
- test("Content-Length 超限时在读取请求流前稳定拒绝", async () => {
9
- const request = new Request("http://localhost/api/license/runtime/install", {
10
- method: "POST",
11
- headers: {
12
- "content-type": "application/json",
13
- "content-length": String(OFFLINE_ACTIVATION_CODE_LIMITS.requestBytes + 1),
14
- },
15
- body: "{}",
16
- });
17
-
18
- await expect(readBoundedJsonObject(request)).rejects.toMatchObject({
19
- code: "body-too-large",
20
- status: 413,
21
- message: "请求体过大,离线激活请求最多允许 64 KiB",
22
- } satisfies Partial<BoundedJsonBodyError>);
23
- });
24
-
25
- test("拒绝与实际请求体不一致的 Content-Length", async () => {
26
- const request = new Request("http://localhost/api/license/runtime/install", {
27
- method: "POST",
28
- headers: {
29
- "content-type": "application/json",
30
- "content-length": "1",
31
- },
32
- body: "{}",
33
- });
34
-
35
- await expect(readBoundedJsonObject(request)).rejects.toMatchObject({
36
- code: "invalid-content-length",
37
- status: 400,
38
- message: "请求体长度与 Content-Length 不一致",
39
- } satisfies Partial<BoundedJsonBodyError>);
40
- });
41
-
42
- test("无 Content-Length 的分块请求超过上限时停止读取", async () => {
43
- const chunk = new Uint8Array(40 * 1024).fill(65);
44
- let cancelled = false;
45
- const body = new ReadableStream<Uint8Array>({
46
- start(controller) {
47
- controller.enqueue(chunk);
48
- controller.enqueue(chunk);
49
- },
50
- cancel() {
51
- cancelled = true;
52
- },
53
- });
54
- const request = new Request("http://localhost/api/license/runtime/install", {
55
- method: "POST",
56
- headers: { "content-type": "application/json" },
57
- body,
58
- });
59
-
60
- await expect(readBoundedJsonObject(request)).rejects.toMatchObject({
61
- code: "body-too-large",
62
- status: 413,
63
- } satisfies Partial<BoundedJsonBodyError>);
64
- expect(cancelled).toBe(true);
65
- });
@@ -1,135 +0,0 @@
1
- import { OFFLINE_ACTIVATION_CODE_LIMITS } from "./offline-code-limits.js";
2
-
3
- export type BoundedJsonBodyErrorCode =
4
- | "body-too-large"
5
- | "invalid-content-length"
6
- | "invalid-json"
7
- | "invalid-object";
8
-
9
- export class BoundedJsonBodyError extends Error {
10
- constructor(
11
- readonly code: BoundedJsonBodyErrorCode,
12
- message: string,
13
- readonly status: 400 | 413,
14
- ) {
15
- super(message);
16
- this.name = "BoundedJsonBodyError";
17
- }
18
- }
19
-
20
- export async function readBoundedJsonObject(
21
- request: Request,
22
- maximumBytes = OFFLINE_ACTIVATION_CODE_LIMITS.requestBytes,
23
- ): Promise<Record<string, unknown>> {
24
- const declaredLength = parseContentLength(
25
- request.headers.get("content-length"),
26
- );
27
- if (declaredLength !== undefined && declaredLength > maximumBytes) {
28
- throw bodyTooLarge();
29
- }
30
-
31
- const bytes = await readBoundedBytes(request, maximumBytes);
32
- if (declaredLength !== undefined && declaredLength !== bytes.byteLength) {
33
- throw new BoundedJsonBodyError(
34
- "invalid-content-length",
35
- "请求体长度与 Content-Length 不一致",
36
- 400,
37
- );
38
- }
39
-
40
- let value: unknown;
41
- try {
42
- const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
43
- value = JSON.parse(text);
44
- } catch {
45
- throw new BoundedJsonBodyError(
46
- "invalid-json",
47
- "请求体必须是有效的 JSON",
48
- 400,
49
- );
50
- }
51
- if (!value || typeof value !== "object" || Array.isArray(value)) {
52
- throw new BoundedJsonBodyError(
53
- "invalid-object",
54
- "请求体必须是 JSON 对象",
55
- 400,
56
- );
57
- }
58
- return value as Record<string, unknown>;
59
- }
60
-
61
- export function boundedJsonBodyErrorResponse(
62
- error: unknown,
63
- ): Response | undefined {
64
- if (!(error instanceof BoundedJsonBodyError)) return undefined;
65
- return Response.json(
66
- {
67
- error:
68
- error.code === "body-too-large"
69
- ? "LICENSE_REQUEST_BODY_TOO_LARGE"
70
- : "LICENSE_REQUEST_BODY_INVALID",
71
- reason: error.message,
72
- },
73
- { status: error.status },
74
- );
75
- }
76
-
77
- function parseContentLength(value: string | null): number | undefined {
78
- if (value === null) return undefined;
79
- if (!/^(0|[1-9]\d*)$/.test(value)) {
80
- throw new BoundedJsonBodyError(
81
- "invalid-content-length",
82
- "Content-Length 必须是有效的非负整数",
83
- 400,
84
- );
85
- }
86
- const parsed = Number(value);
87
- if (!Number.isSafeInteger(parsed)) {
88
- throw new BoundedJsonBodyError(
89
- "invalid-content-length",
90
- "Content-Length 超出安全范围",
91
- 400,
92
- );
93
- }
94
- return parsed;
95
- }
96
-
97
- async function readBoundedBytes(
98
- request: Request,
99
- maximumBytes: number,
100
- ): Promise<Uint8Array> {
101
- if (!request.body) return new Uint8Array();
102
- const reader = request.body.getReader();
103
- const chunks: Uint8Array[] = [];
104
- let total = 0;
105
- try {
106
- while (true) {
107
- const { value, done } = await reader.read();
108
- if (done) break;
109
- if (value.byteLength > maximumBytes - total) {
110
- await reader.cancel();
111
- throw bodyTooLarge();
112
- }
113
- chunks.push(value);
114
- total += value.byteLength;
115
- }
116
- } finally {
117
- reader.releaseLock();
118
- }
119
-
120
- const joined = new Uint8Array(total);
121
- let offset = 0;
122
- for (const chunk of chunks) {
123
- joined.set(chunk, offset);
124
- offset += chunk.byteLength;
125
- }
126
- return joined;
127
- }
128
-
129
- function bodyTooLarge(): BoundedJsonBodyError {
130
- return new BoundedJsonBodyError(
131
- "body-too-large",
132
- "请求体过大,离线激活请求最多允许 64 KiB",
133
- 413,
134
- );
135
- }
@@ -1,28 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { CanonicalJsonError, canonicalizeJson } from "./canonical-json.js";
3
-
4
- describe("RFC 8785 canonical JSON", () => {
5
- test("按 UTF-16 key 排序并使用 ECMAScript primitive 序列化", () => {
6
- expect(
7
- canonicalizeJson({
8
- numbers: [Number("333333333.33333329"), 1e30, 4.5, 0.002, 1e-27],
9
- string: '€$\u000f\nA\'B"\\"/',
10
- literals: [null, true, false],
11
- }),
12
- ).toBe(
13
- '{"literals":[null,true,false],"numbers":[333333333.3333333,1e+30,4.5,0.002,1e-27],"string":"€$\\u000f\\nA\'B\\"\\\\\\"/"}',
14
- );
15
- });
16
-
17
- test("拒绝非 JSON 值、非有限数与非法 Unicode", () => {
18
- expect(() => canonicalizeJson({ value: undefined })).toThrow(
19
- CanonicalJsonError,
20
- );
21
- expect(() => canonicalizeJson(Number.NaN)).toThrow(
22
- "JCS 不支持 NaN 或无穷大",
23
- );
24
- expect(() => canonicalizeJson("\ud800")).toThrow(
25
- "JCS 不支持未配对的 UTF-16 surrogate",
26
- );
27
- });
28
- });
@@ -1,104 +0,0 @@
1
- export type CanonicalJsonPrimitive = string | number | boolean | null;
2
- export type CanonicalJsonValue =
3
- | CanonicalJsonPrimitive
4
- | CanonicalJsonValue[]
5
- | { [key: string]: CanonicalJsonValue };
6
-
7
- export type CanonicalJsonErrorCode =
8
- | "CANONICAL_JSON_INVALID_TYPE"
9
- | "CANONICAL_JSON_INVALID_NUMBER"
10
- | "CANONICAL_JSON_INVALID_UNICODE"
11
- | "CANONICAL_JSON_CYCLE";
12
-
13
- export class CanonicalJsonError extends Error {
14
- constructor(
15
- readonly code: CanonicalJsonErrorCode,
16
- message: string,
17
- ) {
18
- super(message);
19
- this.name = "CanonicalJsonError";
20
- }
21
- }
22
-
23
- /**
24
- * RFC 8785 JSON Canonicalization Scheme (JCS) serializer.
25
- *
26
- * V1 intentionally accepts JSON values only: no undefined, BigInt, custom
27
- * toJSON conversion, non-finite numbers, or unpaired UTF-16 surrogates.
28
- */
29
- export function canonicalizeJson(value: unknown): string {
30
- return serialize(value, new Set<object>());
31
- }
32
-
33
- function serialize(value: unknown, ancestors: Set<object>): string {
34
- if (value === null) return "null";
35
- if (typeof value === "boolean") return value ? "true" : "false";
36
- if (typeof value === "string") return serializeString(value);
37
- if (typeof value === "number") return serializeNumber(value);
38
- if (typeof value !== "object") {
39
- throw new CanonicalJsonError(
40
- "CANONICAL_JSON_INVALID_TYPE",
41
- `JCS 不支持 ${typeof value} 值`,
42
- );
43
- }
44
- if (ancestors.has(value)) {
45
- throw new CanonicalJsonError("CANONICAL_JSON_CYCLE", "JCS 不支持循环引用");
46
- }
47
-
48
- ancestors.add(value);
49
- try {
50
- if (Array.isArray(value)) {
51
- return `[${value.map((item) => serialize(item, ancestors)).join(",")}]`;
52
- }
53
- const object = value as Record<string, unknown>;
54
- const members = Object.keys(object)
55
- .sort(compareUtf16)
56
- .map(
57
- (key) => `${serializeString(key)}:${serialize(object[key], ancestors)}`,
58
- );
59
- return `{${members.join(",")}}`;
60
- } finally {
61
- ancestors.delete(value);
62
- }
63
- }
64
-
65
- function serializeNumber(value: number): string {
66
- if (!Number.isFinite(value)) {
67
- throw new CanonicalJsonError(
68
- "CANONICAL_JSON_INVALID_NUMBER",
69
- "JCS 不支持 NaN 或无穷大",
70
- );
71
- }
72
- return JSON.stringify(value);
73
- }
74
-
75
- function serializeString(value: string): string {
76
- assertUnicodeScalarSequence(value);
77
- return JSON.stringify(value);
78
- }
79
-
80
- function assertUnicodeScalarSequence(value: string): void {
81
- for (let index = 0; index < value.length; index += 1) {
82
- const code = value.charCodeAt(index);
83
- if (code >= 0xd800 && code <= 0xdbff) {
84
- const next = value.charCodeAt(index + 1);
85
- if (!Number.isFinite(next) || next < 0xdc00 || next > 0xdfff) {
86
- invalidUnicode();
87
- }
88
- index += 1;
89
- } else if (code >= 0xdc00 && code <= 0xdfff) {
90
- invalidUnicode();
91
- }
92
- }
93
- }
94
-
95
- function invalidUnicode(): never {
96
- throw new CanonicalJsonError(
97
- "CANONICAL_JSON_INVALID_UNICODE",
98
- "JCS 不支持未配对的 UTF-16 surrogate",
99
- );
100
- }
101
-
102
- function compareUtf16(left: string, right: string): number {
103
- return left < right ? -1 : left > right ? 1 : 0;
104
- }
@@ -1,82 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { WindyLicenseClient } from "./client.js";
3
- import { MemoryAtomicLicenseStore, MemoryLicenseStorage } from "./storage.js";
4
- import { LICENSE_V1_TEST_VECTOR } from "./v1-test-vector.js";
5
-
6
- const vectorLicense = JSON.stringify({
7
- formatVersion: 1,
8
- algorithm: "ED25519",
9
- publicKeyId: LICENSE_V1_TEST_VECTOR.keyId,
10
- claims: LICENSE_V1_TEST_VECTOR.claims,
11
- signature: LICENSE_V1_TEST_VECTOR.signature,
12
- });
13
-
14
- describe("WindyLicenseClient", () => {
15
- test("使用固定向量和多公钥环打开 V1 License", async () => {
16
- const client = new WindyLicenseClient({
17
- licenseSource: new MemoryAtomicLicenseStore(vectorLicense),
18
- stateStorage: new MemoryLicenseStorage(),
19
- productCode: "windy-platform",
20
- machineCode: "machine-vector-001",
21
- publicKeys: {
22
- unrelated: LICENSE_V1_TEST_VECTOR.publicKeyPem,
23
- [LICENSE_V1_TEST_VECTOR.keyId]: LICENSE_V1_TEST_VECTOR.publicKeyPem,
24
- },
25
- now: () => new Date("2026-07-10T10:00:00.000Z"),
26
- });
27
-
28
- const result = await client.recordOpen();
29
-
30
- expect(result.valid).toBe(true);
31
- expect(result.failures).toEqual([]);
32
- expect(result.license?.claims.licenseId).toBe("license_vector_v1");
33
- });
34
-
35
- test("未知 publicKeyId 时拒绝运行", async () => {
36
- const client = new WindyLicenseClient({
37
- licenseSource: new MemoryAtomicLicenseStore(vectorLicense),
38
- stateStorage: new MemoryLicenseStorage(),
39
- productCode: "windy-platform",
40
- machineCode: "machine-vector-001",
41
- publicKeys: {},
42
- now: () => new Date("2026-07-10T10:00:00.000Z"),
43
- });
44
-
45
- const result = await client.recordOpen();
46
-
47
- expect(result.valid).toBe(false);
48
- expect(result.failures).toEqual(["unknown-public-key"]);
49
- });
50
-
51
- test("明显回拨持续暂停,小幅 NTP 偏差不误判且时间恢复后自动恢复", async () => {
52
- let now = new Date("2026-07-10T10:00:00.000Z");
53
- const state = new MemoryLicenseStorage();
54
- const client = new WindyLicenseClient({
55
- licenseSource: new MemoryAtomicLicenseStore(vectorLicense),
56
- stateStorage: state,
57
- productCode: "windy-platform",
58
- machineCode: "machine-vector-001",
59
- publicKeys: {
60
- [LICENSE_V1_TEST_VECTOR.keyId]: LICENSE_V1_TEST_VECTOR.publicKeyPem,
61
- },
62
- clockToleranceMs: 5 * 60 * 1000,
63
- now: () => now,
64
- });
65
-
66
- expect((await client.recordOpen()).valid).toBe(true);
67
- now = new Date("2026-07-10T09:54:00.000Z");
68
- expect((await client.recordOpen()).failures).toContain("clock-rollback");
69
- expect(await state.read("last-trusted-at")).toBe(
70
- "2026-07-10T10:00:00.000Z",
71
- );
72
- expect((await client.recordOpen()).failures).toContain("clock-rollback");
73
-
74
- now = new Date("2026-07-10T09:58:00.000Z");
75
- const recovered = await client.recordOpen();
76
- expect(recovered.valid).toBe(true);
77
- expect(recovered.failures).not.toContain("clock-rollback");
78
- expect(await state.read("last-trusted-at")).toBe(
79
- "2026-07-10T10:00:00.000Z",
80
- );
81
- });
82
- });
@@ -1,86 +0,0 @@
1
- import type {
2
- LicenseFailureReason,
3
- LicenseUsageRecord,
4
- SignedLicense,
5
- } from "@windy/shared";
6
- import type { LicenseSource, LicenseStorage } from "./storage.js";
7
- import type { LicensePublicKeySource } from "./verification.js";
8
- import { parseSignedLicense, validateLicenseDocument } from "./validation.js";
9
-
10
- export interface LicenseClientOptions {
11
- licenseSource: LicenseSource;
12
- stateStorage: LicenseStorage;
13
- productCode: string;
14
- machineCode: string;
15
- publicKeys: LicensePublicKeySource;
16
- now?: () => Date;
17
- clockToleranceMs?: number;
18
- }
19
-
20
- export interface LicenseOpenResult {
21
- valid: boolean;
22
- license?: SignedLicense;
23
- usage: LicenseUsageRecord;
24
- failures: LicenseFailureReason[];
25
- }
26
-
27
- const LAST_TRUSTED_AT_KEY = "last-trusted-at";
28
-
29
- export class WindyLicenseClient {
30
- private readonly now: () => Date;
31
-
32
- constructor(private readonly options: LicenseClientOptions) {
33
- this.now = options.now ?? (() => new Date());
34
- }
35
-
36
- async readLicense(): Promise<SignedLicense | undefined> {
37
- const rawLicense = await this.options.licenseSource.readCurrent();
38
- return rawLicense ? parseSignedLicense(rawLicense) : undefined;
39
- }
40
-
41
- async recordOpen(): Promise<LicenseOpenResult> {
42
- const openedAt = this.now();
43
- const rawLicense = await this.options.licenseSource.readCurrent();
44
- const previousOpenedAt =
45
- await this.options.stateStorage.read(LAST_TRUSTED_AT_KEY);
46
-
47
- if (!rawLicense) {
48
- return {
49
- valid: false,
50
- failures: ["invalid-format"],
51
- usage: {
52
- machineCode: this.options.machineCode,
53
- openedAt: openedAt.toISOString(),
54
- previousOpenedAt,
55
- status: "invalid",
56
- reason: "invalid-format",
57
- },
58
- };
59
- }
60
-
61
- const result = await validateLicenseDocument(rawLicense, {
62
- productCode: this.options.productCode,
63
- machineCode: this.options.machineCode,
64
- publicKeys: this.options.publicKeys,
65
- now: openedAt,
66
- lastTrustedAt: previousOpenedAt,
67
- clockToleranceMs: this.options.clockToleranceMs,
68
- });
69
- if (
70
- !result.failures.includes("clock-rollback") &&
71
- result.nextTrustedAt &&
72
- result.nextTrustedAt !== previousOpenedAt
73
- ) {
74
- await this.options.stateStorage.write(
75
- LAST_TRUSTED_AT_KEY,
76
- result.nextTrustedAt,
77
- );
78
- }
79
- return {
80
- valid: result.valid,
81
- license: result.license,
82
- failures: result.failures,
83
- usage: result.usage,
84
- };
85
- }
86
- }