create-windy 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/README.md +6 -0
  2. package/dist/cli.js +181 -87
  3. package/package.json +1 -1
  4. package/template/.windy-template.json +2 -2
  5. package/template/apps/server/Dockerfile +2 -0
  6. package/template/apps/server/package.json +10 -8
  7. package/template/apps/server/src/access-guards.ts +1 -1
  8. package/template/apps/server/src/admin/routes.test.ts +10 -0
  9. package/template/apps/server/src/admin/routes.ts +4 -2
  10. package/template/apps/server/src/agent/routes.test.ts +1 -1
  11. package/template/apps/server/src/agent/routes.ts +2 -2
  12. package/template/apps/server/src/application-services.ts +3 -0
  13. package/template/apps/server/src/audit/drizzle-log-store.ts +2 -2
  14. package/template/apps/server/src/audit/policy.ts +1 -1
  15. package/template/apps/server/src/audit/recovery-queue.test.ts +1 -1
  16. package/template/apps/server/src/audit/recovery-queue.ts +1 -1
  17. package/template/apps/server/src/audit/redaction.ts +1 -1
  18. package/template/apps/server/src/audit/reliable-writer.test.ts +1 -1
  19. package/template/apps/server/src/audit/reliable-writer.ts +1 -1
  20. package/template/apps/server/src/audit/search-summary.ts +1 -1
  21. package/template/apps/server/src/auth/authentication-provider.test.ts +1 -1
  22. package/template/apps/server/src/auth/credential-service.ts +1 -1
  23. package/template/apps/server/src/auth/drizzle-repository.ts +2 -2
  24. package/template/apps/server/src/auth/drizzle-session-operations.ts +1 -1
  25. package/template/apps/server/src/auth/own-session-routes.test.ts +1 -1
  26. package/template/apps/server/src/auth/password.ts +2 -2
  27. package/template/apps/server/src/auth/repository.ts +1 -1
  28. package/template/apps/server/src/auth/routes.test.ts +1 -1
  29. package/template/apps/server/src/auth/service.ts +1 -1
  30. package/template/apps/server/src/bulk-data/artifact-store.ts +1 -1
  31. package/template/apps/server/src/bulk-data/dictionary-handler.ts +1 -1
  32. package/template/apps/server/src/bulk-data/drizzle-repository.ts +3 -3
  33. package/template/apps/server/src/bulk-data/repository.ts +1 -1
  34. package/template/apps/server/src/bulk-data/routes.test.ts +1 -1
  35. package/template/apps/server/src/bulk-data/routes.ts +1 -1
  36. package/template/apps/server/src/bulk-data/service.test.ts +1 -1
  37. package/template/apps/server/src/bulk-data/service.ts +1 -1
  38. package/template/apps/server/src/bulk-data/types.ts +1 -1
  39. package/template/apps/server/src/configuration/bootstrap.ts +2 -2
  40. package/template/apps/server/src/configuration/definition.ts +2 -2
  41. package/template/apps/server/src/configuration/drizzle-repository.ts +2 -2
  42. package/template/apps/server/src/configuration/repository.ts +1 -1
  43. package/template/apps/server/src/configuration/routes.ts +1 -1
  44. package/template/apps/server/src/configuration/service.ts +1 -1
  45. package/template/apps/server/src/data-access/context.ts +2 -2
  46. package/template/apps/server/src/data-access/query-specification.test.ts +1 -1
  47. package/template/apps/server/src/data-access/scoped-repository.integration.test.ts +1 -1
  48. package/template/apps/server/src/data-access/scoped-repository.ts +1 -1
  49. package/template/apps/server/src/data-access/scoped-resource.test.ts +1 -1
  50. package/template/apps/server/src/data-access/scoped-resource.ts +1 -1
  51. package/template/apps/server/src/data-governance/export/approval-port.ts +1 -1
  52. package/template/apps/server/src/data-governance/export/drizzle-repository.ts +1 -1
  53. package/template/apps/server/src/data-governance/export/job-runtime.test.ts +2 -2
  54. package/template/apps/server/src/data-governance/export/job-runtime.ts +2 -3
  55. package/template/apps/server/src/data-governance/export/postgres-api-fixture.ts +2 -2
  56. package/template/apps/server/src/data-governance/export/postgres-api.integration.test.ts +1 -1
  57. package/template/apps/server/src/data-governance/export/routes-test-fixture.ts +3 -3
  58. package/template/apps/server/src/data-governance/export/routes.ts +1 -1
  59. package/template/apps/server/src/data-governance/export/scope-snapshot.ts +1 -1
  60. package/template/apps/server/src/data-governance/export/service.ts +2 -2
  61. package/template/apps/server/src/data-governance/export/source.ts +1 -1
  62. package/template/apps/server/src/data-governance/export/types.ts +1 -1
  63. package/template/apps/server/src/data-governance/plaintext-access.test.ts +1 -1
  64. package/template/apps/server/src/data-governance/plaintext-access.ts +1 -1
  65. package/template/apps/server/src/data-governance/plaintext-postgres.integration.test.ts +1 -1
  66. package/template/apps/server/src/data-governance/user-plaintext-routes.ts +1 -1
  67. package/template/apps/server/src/data-governance/user-projection.ts +2 -2
  68. package/template/apps/server/src/example-modules.ts +25 -1
  69. package/template/apps/server/src/feature/manifest-feature-repository.test.ts +1 -1
  70. package/template/apps/server/src/feature/manifest-feature-repository.ts +1 -1
  71. package/template/apps/server/src/feature/routes.ts +3 -6
  72. package/template/apps/server/src/foundation-access.test.ts +1 -1
  73. package/template/apps/server/src/foundation-access.ts +1 -1
  74. package/template/apps/server/src/foundation.ts +21 -7
  75. package/template/apps/server/src/guards.test.ts +1 -1
  76. package/template/apps/server/src/guards.ts +3 -3
  77. package/template/apps/server/src/index.ts +28 -3
  78. package/template/{packages/modules → apps/server}/src/installed-business-modules.ts +2 -2
  79. package/template/apps/server/src/jobs/artifact-service.test.ts +1 -1
  80. package/template/apps/server/src/jobs/artifact-service.ts +2 -2
  81. package/template/apps/server/src/jobs/drizzle-artifact.ts +2 -2
  82. package/template/apps/server/src/jobs/drizzle-claim.ts +1 -1
  83. package/template/apps/server/src/jobs/drizzle-execution-repository.ts +2 -2
  84. package/template/apps/server/src/jobs/drizzle-execution.integration.test.ts +1 -1
  85. package/template/apps/server/src/jobs/drizzle-lease.ts +2 -2
  86. package/template/apps/server/src/jobs/drizzle-mappers.ts +3 -3
  87. package/template/apps/server/src/jobs/drizzle-recovery-repository.ts +1 -1
  88. package/template/apps/server/src/jobs/drizzle-repository.integration.test.ts +1 -1
  89. package/template/apps/server/src/jobs/drizzle-repository.ts +3 -3
  90. package/template/apps/server/src/jobs/execution-repository.ts +1 -1
  91. package/template/apps/server/src/jobs/execution.test.ts +2 -2
  92. package/template/apps/server/src/jobs/in-memory-claim.ts +1 -1
  93. package/template/apps/server/src/jobs/in-memory-execution.ts +1 -1
  94. package/template/apps/server/src/jobs/in-memory-lease.ts +1 -1
  95. package/template/apps/server/src/jobs/legacy-adapter.integration.test.ts +1 -1
  96. package/template/apps/server/src/jobs/legacy-adapter.test.ts +1 -1
  97. package/template/apps/server/src/jobs/legacy-adapter.ts +1 -1
  98. package/template/apps/server/src/jobs/legacy-service-runtime.ts +4 -2
  99. package/template/apps/server/src/jobs/repository.ts +2 -2
  100. package/template/apps/server/src/jobs/service.ts +2 -2
  101. package/template/apps/server/src/jobs/worker.ts +3 -2
  102. package/template/apps/server/src/license/legacy-license-resolver.ts +1 -1
  103. package/template/apps/server/src/license/license-resolver.test.ts +1 -1
  104. package/template/apps/server/src/license/license-resolver.ts +3 -3
  105. package/template/apps/server/src/license/routes-offline-code.test.ts +2 -2
  106. package/template/apps/server/src/license/routes.ts +1 -1
  107. package/template/apps/server/src/license/runtime-audit-reliability.test.ts +2 -2
  108. package/template/apps/server/src/license/runtime-reminder.test.ts +2 -2
  109. package/template/apps/server/src/license/runtime-service.test.ts +2 -2
  110. package/template/apps/server/src/license/runtime-service.ts +2 -2
  111. package/template/apps/server/src/module-composition.test.ts +1 -1
  112. package/template/apps/server/src/module-composition.ts +1 -1
  113. package/template/apps/server/src/module-host.test.ts +145 -0
  114. package/template/apps/server/src/module-host.ts +110 -0
  115. package/template/apps/server/src/notification/drizzle-repository.ts +2 -2
  116. package/template/apps/server/src/notification/repository.ts +1 -1
  117. package/template/apps/server/src/notification/routes.ts +1 -1
  118. package/template/apps/server/src/pagination.ts +1 -1
  119. package/template/apps/server/src/persistence.integration.test.ts +1 -1
  120. package/template/apps/server/src/persistence.ts +1 -1
  121. package/template/apps/server/src/profile/routes.ts +1 -1
  122. package/template/apps/server/src/route-guards.test.ts +1 -1
  123. package/template/apps/server/src/route-guards.ts +1 -1
  124. package/template/apps/server/src/runtime-feature.ts +125 -0
  125. package/template/apps/server/src/runtime-license.ts +1 -1
  126. package/template/apps/server/src/runtime.test.ts +48 -1
  127. package/template/apps/server/src/runtime.ts +17 -64
  128. package/template/apps/server/src/scheduler/audit.ts +1 -1
  129. package/template/apps/server/src/scheduler/definitions.test.ts +1 -1
  130. package/template/apps/server/src/scheduler/definitions.ts +2 -2
  131. package/template/apps/server/src/scheduler/drizzle-durable-links.ts +1 -1
  132. package/template/apps/server/src/scheduler/drizzle-mappers.ts +1 -1
  133. package/template/apps/server/src/scheduler/drizzle-repository.ts +1 -1
  134. package/template/apps/server/src/scheduler/execution-recorder.ts +1 -1
  135. package/template/apps/server/src/scheduler/history.ts +1 -1
  136. package/template/apps/server/src/scheduler/overview.ts +1 -1
  137. package/template/apps/server/src/scheduler/recovery-service.test.ts +1 -1
  138. package/template/apps/server/src/scheduler/recovery-service.ts +1 -1
  139. package/template/apps/server/src/scheduler/repository.ts +1 -1
  140. package/template/apps/server/src/scheduler/routes.ts +1 -1
  141. package/template/apps/server/src/scheduler/service-idle.test.ts +1 -1
  142. package/template/apps/server/src/scheduler/service.test.ts +1 -1
  143. package/template/apps/server/src/scheduler/service.ts +1 -1
  144. package/template/apps/server/src/scheduler/types.ts +1 -1
  145. package/template/apps/server/src/search/contracts.ts +2 -2
  146. package/template/apps/server/src/search/host.ts +1 -1
  147. package/template/apps/server/src/search/registry.ts +1 -1
  148. package/template/apps/server/src/search/routes.test.ts +1 -1
  149. package/template/apps/server/src/search/routes.ts +1 -1
  150. package/template/apps/server/src/search/service.ts +2 -2
  151. package/template/apps/server/src/search/test-support.ts +2 -2
  152. package/template/apps/server/src/session/routes.ts +1 -1
  153. package/template/apps/server/src/settings/drizzle-repository.ts +2 -2
  154. package/template/apps/server/src/settings/repository.ts +1 -1
  155. package/template/apps/server/src/settings/routes.test.ts +1 -1
  156. package/template/apps/server/src/settings/routes.ts +1 -1
  157. package/template/apps/server/src/settings/runtime.ts +1 -1
  158. package/template/apps/server/src/storage/blob-reference-registry.test.ts +1 -1
  159. package/template/apps/server/src/storage/blob-reference-registry.ts +1 -1
  160. package/template/apps/server/src/storage/drizzle-upload-repository.integration.test.ts +1 -1
  161. package/template/apps/server/src/storage/drizzle-upload-repository.ts +2 -2
  162. package/template/apps/server/src/storage/route-support.ts +1 -1
  163. package/template/apps/server/src/storage/routes.test.ts +1 -1
  164. package/template/apps/server/src/storage/routes.ts +2 -2
  165. package/template/apps/server/src/storage/runtime.ts +6 -4
  166. package/template/apps/server/src/system/audit-routes.ts +1 -1
  167. package/template/apps/server/src/system/department-routes.ts +1 -1
  168. package/template/apps/server/src/system/drizzle-repository.ts +1 -1
  169. package/template/apps/server/src/system/drizzle-scoped-repository.ts +1 -1
  170. package/template/apps/server/src/system/drizzle-system.ts +1 -1
  171. package/template/apps/server/src/system/entities.ts +1 -1
  172. package/template/apps/server/src/system/identity-route-config.ts +1 -1
  173. package/template/apps/server/src/system/interface-catalog.test.ts +3 -3
  174. package/template/apps/server/src/system/interface-catalog.ts +2 -2
  175. package/template/apps/server/src/system/menu-registry.ts +1 -1
  176. package/template/apps/server/src/system/menu-routes.ts +1 -1
  177. package/template/apps/server/src/system/operations.test.ts +1 -1
  178. package/template/apps/server/src/system/operations.ts +1 -1
  179. package/template/apps/server/src/system/redis-health.ts +1 -1
  180. package/template/apps/server/src/system/repository.ts +1 -1
  181. package/template/apps/server/src/system/resource-route-support.ts +1 -1
  182. package/template/apps/server/src/system/route-helpers.ts +1 -1
  183. package/template/apps/server/src/system/route-types.ts +1 -1
  184. package/template/apps/server/src/system/routes.test.ts +1 -1
  185. package/template/apps/server/src/system/routes.ts +1 -1
  186. package/template/apps/server/src/system/user-projection.ts +1 -1
  187. package/template/apps/server/src/work-order/data-scope.integration.test.ts +1 -1
  188. package/template/apps/server/src/work-order/drizzle-repository.integration.test.ts +1 -1
  189. package/template/apps/server/src/work-order/drizzle-repository.ts +2 -2
  190. package/template/apps/server/src/work-order/foundation-modules.test.ts +15 -5
  191. package/template/apps/server/src/work-order/repository.ts +1 -1
  192. package/template/apps/server/src/work-order/routes.test.ts +2 -2
  193. package/template/apps/server/src/work-order/routes.ts +2 -2
  194. package/template/apps/server/src/work-order/search-api.test.ts +3 -3
  195. package/template/apps/server/src/work-order/search-provider.test.ts +3 -3
  196. package/template/apps/server/src/work-order/service.ts +1 -1
  197. package/template/apps/server/src/work-order/task.test.ts +1 -1
  198. package/template/apps/server/src/work-order/task.ts +2 -2
  199. package/template/apps/server/src/workflow/drizzle-repository.integration.test.ts +2 -2
  200. package/template/apps/server/src/workflow/drizzle-repository.ts +3 -3
  201. package/template/apps/server/src/workflow/effects.test.ts +1 -1
  202. package/template/apps/server/src/workflow/effects.ts +1 -1
  203. package/template/apps/server/src/workflow/repository.ts +2 -2
  204. package/template/apps/server/src/workflow/routes.ts +3 -3
  205. package/template/apps/server/src/workflow/service.ts +1 -1
  206. package/template/apps/server/src/workflow/timeout-runtime.test.ts +1 -1
  207. package/template/apps/server/src/workflow/timeout-runtime.ts +2 -2
  208. package/template/apps/web/Dockerfile +2 -0
  209. package/template/apps/web/package.json +4 -4
  210. package/template/apps/web/runtime-server.ts +1 -1
  211. package/template/apps/web/src/app/layouts/AppLayout.vue +6 -0
  212. package/template/apps/web/src/components/auth/PasswordChangeForm.vue +1 -1
  213. package/template/apps/web/src/components/common/DataPagination.vue +1 -1
  214. package/template/apps/web/src/components/license/LicenseActivationCodeForm.vue +1 -1
  215. package/template/apps/web/src/components/license/LicenseFlow.webtest.ts +2 -2
  216. package/template/apps/web/src/composables/useModuleConfigurations.ts +1 -1
  217. package/template/apps/web/src/composables/useWatermarkSettings.ts +1 -0
  218. package/template/apps/web/src/composables/watermark-settings.ts +6 -0
  219. package/template/apps/web/src/composables/watermark-settings.webtest.ts +27 -1
  220. package/template/apps/web/src/layout/AppSidebar.vue +12 -3
  221. package/template/apps/web/src/layout/AppSidebar.webtest.ts +11 -0
  222. package/template/apps/web/src/layout/GlobalWatermark.vue +25 -8
  223. package/template/apps/web/src/layout/GlobalWatermark.webtest.ts +109 -0
  224. package/template/apps/web/src/layout/NavigationSearchDialog.vue +1 -1
  225. package/template/apps/web/src/layout/NotificationMenu.vue +1 -1
  226. package/template/apps/web/src/layout/navigation.ts +5 -1
  227. package/template/apps/web/src/layout/navigation.webtest.ts +1 -1
  228. package/template/apps/web/src/pages/configuration/components/ModuleConfigRollbackDialog.vue +1 -1
  229. package/template/apps/web/src/pages/configuration/components/ModuleConfigVersionList.vue +1 -1
  230. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.vue +1 -1
  231. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationEditor.webtest.ts +1 -1
  232. package/template/apps/web/src/pages/configuration/components/ModuleConfigurationWorkbench.vue +1 -1
  233. package/template/apps/web/src/pages/dashboard/dashboard-presenter.webtest.ts +1 -1
  234. package/template/apps/web/src/pages/settings/WatermarkSettingsSection.vue +21 -0
  235. package/template/apps/web/src/pages/system/SystemNotificationsPage.vue +2 -2
  236. package/template/apps/web/src/pages/system/components/AuditActionCell.vue +7 -2
  237. package/template/apps/web/src/pages/system/components/AuditLogFilters.vue +7 -2
  238. package/template/apps/web/src/pages/system/components/NotificationPublishForm.vue +1 -1
  239. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.vue +1 -1
  240. package/template/apps/web/src/pages/system/components/SchedulerOperationsPanel.webtest.ts +1 -1
  241. package/template/apps/web/src/pages/system/components/SchedulerRunTable.vue +1 -1
  242. package/template/apps/web/src/pages/system/components/SchedulerRunTable.webtest.ts +1 -1
  243. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.vue +1 -1
  244. package/template/apps/web/src/pages/system/components/SchedulerStaleRecoveryForm.webtest.ts +1 -1
  245. package/template/apps/web/src/pages/system/components/SystemResourcePage.vue +1 -1
  246. package/template/apps/web/src/pages/system/components/SystemResourceTable.vue +10 -3
  247. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.vue +1 -1
  248. package/template/apps/web/src/pages/system/components/role-policy/RolePermissionTree.webtest.ts +1 -1
  249. package/template/apps/web/src/pages/system/components/role-policy/RolePolicyForm.vue +1 -1
  250. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.ts +1 -1
  251. package/template/apps/web/src/pages/system/components/role-policy/permission-tree.webtest.ts +1 -1
  252. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.ts +2 -2
  253. package/template/apps/web/src/pages/system/composables/useSchedulerOperations.webtest.ts +1 -1
  254. package/template/apps/web/src/pages/system/composables/useSystemResource.ts +1 -1
  255. package/template/apps/web/src/router/manifest-routes.ts +1 -1
  256. package/template/apps/web/src/router/manifest-routes.webtest.ts +1 -1
  257. package/template/apps/web/src/services/access-api.ts +10 -1
  258. package/template/apps/web/src/services/configuration-api.ts +1 -1
  259. package/template/apps/web/src/services/license-api.ts +1 -1
  260. package/template/apps/web/src/services/notification-api.ts +2 -2
  261. package/template/apps/web/src/services/operations-api.ts +1 -1
  262. package/template/apps/web/src/services/platform-settings-api.ts +2 -2
  263. package/template/apps/web/src/services/profile-api.ts +2 -2
  264. package/template/docs/architecture/durable-jobs.md +5 -0
  265. package/template/docs/architecture/module-manifest-composition.md +14 -7
  266. package/template/docs/architecture/object-storage.md +1 -1
  267. package/template/docs/development/crud-generator.md +4 -1
  268. package/template/docs/platform/audit-reliability.md +1 -1
  269. package/template/package.json +2 -1
  270. package/template/packages/config/package.json +15 -4
  271. package/template/packages/config/src/platform.ts +1 -1
  272. package/template/packages/crud-generator/package.json +1 -1
  273. package/template/packages/crud-generator/src/generator.test.ts +16 -0
  274. package/template/packages/crud-generator/src/templates/domain.ts +1 -1
  275. package/template/packages/crud-generator/src/templates/manifest.ts +2 -2
  276. package/template/packages/crud-generator/src/templates/project.ts +3 -3
  277. package/template/packages/crud-generator/src/templates/repository.ts +1 -1
  278. package/template/packages/crud-generator/src/templates/routes.ts +2 -2
  279. package/template/packages/crud-generator/src/validation.ts +22 -1
  280. package/template/packages/database/package.json +15 -5
  281. package/template/packages/database/src/bundle-validation.ts +218 -0
  282. package/template/packages/database/src/dialects.ts +1 -1
  283. package/template/packages/database/src/runner.test.ts +160 -13
  284. package/template/packages/database/src/runner.ts +95 -77
  285. package/template/packages/database/src/schema/governed-exports.ts +1 -1
  286. package/template/packages/database/src/schema/identity.ts +1 -1
  287. package/template/packages/database/src/schema/license-v1.ts +1 -1
  288. package/template/packages/database/src/schema/notifications.ts +1 -1
  289. package/template/packages/database/src/schema/platform-settings.ts +1 -1
  290. package/template/packages/database/src/schema/workflow.ts +1 -1
  291. package/template/packages/database/src/sql-store.ts +1 -1
  292. package/template/packages/database/src/store.ts +1 -1
  293. package/template/packages/example-work-order/index.ts +1 -0
  294. package/template/packages/example-work-order/package.json +4 -3
  295. package/template/packages/example-work-order/src/contract.ts +1 -1
  296. package/template/packages/example-work-order/src/manifest.ts +8 -3
  297. package/template/packages/example-work-order/src/migration-bundle.test.ts +43 -0
  298. package/template/packages/example-work-order/src/migration-bundle.ts +64 -0
  299. package/template/packages/jobs/index.ts +2 -0
  300. package/template/packages/jobs/package.json +29 -0
  301. package/template/packages/jobs/src/handlers.test.ts +72 -0
  302. package/template/{apps/server/src/jobs/handler-registry.ts → packages/jobs/src/handlers.ts} +4 -4
  303. package/template/{apps/server/src/jobs → packages/jobs/src}/types.ts +7 -1
  304. package/template/packages/{license-sdk → jobs}/tsconfig.json +0 -1
  305. package/template/packages/modules/index.ts +1 -1
  306. package/template/packages/modules/package.json +15 -5
  307. package/template/packages/modules/src/ai-operation-composition.test.ts +195 -0
  308. package/template/packages/modules/src/ai-operation-composition.ts +145 -0
  309. package/template/packages/modules/src/catalog-validation.ts +123 -0
  310. package/template/packages/modules/src/composition.test.ts +75 -2
  311. package/template/packages/modules/src/composition.ts +23 -39
  312. package/template/packages/modules/src/implementation-bindings.ts +70 -0
  313. package/template/packages/modules/src/manifest.ts +43 -12
  314. package/template/packages/modules/src/menu-composition.ts +1 -1
  315. package/template/packages/modules/src/system-api-binding.ts +1 -1
  316. package/template/packages/modules/src/system-api-data-governance.ts +1 -1
  317. package/template/packages/modules/src/system-api-permissions.ts +1 -1
  318. package/template/packages/modules/src/system-data-governance.ts +1 -1
  319. package/template/packages/modules/src/system-features.ts +1 -1
  320. package/template/packages/modules/src/system-governed-export-bindings.ts +1 -1
  321. package/template/packages/modules/src/system-module-catalog.ts +1 -1
  322. package/template/packages/server-sdk/index.ts +5 -0
  323. package/template/packages/server-sdk/package.json +25 -0
  324. package/template/packages/server-sdk/src/module-host.ts +29 -0
  325. package/template/packages/server-sdk/tsconfig.json +16 -0
  326. package/template/packages/shared/index.ts +2 -0
  327. package/template/packages/shared/package.json +14 -3
  328. package/template/packages/shared/src/audit.ts +22 -1
  329. package/template/packages/shared/src/license-catalog.test.ts +17 -0
  330. package/template/packages/shared/src/license-catalog.ts +33 -0
  331. package/template/packages/shared/src/migration.ts +39 -0
  332. package/template/packages/storage/package.json +14 -3
  333. package/template/packages/license-sdk/index.ts +0 -13
  334. package/template/packages/license-sdk/package.json +0 -18
  335. package/template/packages/license-sdk/src/bounded-json-body.test.ts +0 -65
  336. package/template/packages/license-sdk/src/bounded-json-body.ts +0 -135
  337. package/template/packages/license-sdk/src/canonical-json.test.ts +0 -28
  338. package/template/packages/license-sdk/src/canonical-json.ts +0 -104
  339. package/template/packages/license-sdk/src/client.test.ts +0 -82
  340. package/template/packages/license-sdk/src/client.ts +0 -86
  341. package/template/packages/license-sdk/src/device.ts +0 -46
  342. package/template/packages/license-sdk/src/file-storage.test.ts +0 -80
  343. package/template/packages/license-sdk/src/file-storage.ts +0 -77
  344. package/template/packages/license-sdk/src/installation.test.ts +0 -241
  345. package/template/packages/license-sdk/src/installation.ts +0 -150
  346. package/template/packages/license-sdk/src/license-parser.ts +0 -56
  347. package/template/packages/license-sdk/src/offline-code-base32.ts +0 -57
  348. package/template/packages/license-sdk/src/offline-code-decompression.ts +0 -55
  349. package/template/packages/license-sdk/src/offline-code-limits.ts +0 -10
  350. package/template/packages/license-sdk/src/offline-code-wire.ts +0 -72
  351. package/template/packages/license-sdk/src/offline-code.test.ts +0 -172
  352. package/template/packages/license-sdk/src/offline-code.ts +0 -319
  353. package/template/packages/license-sdk/src/payload.test.ts +0 -72
  354. package/template/packages/license-sdk/src/payload.ts +0 -201
  355. package/template/packages/license-sdk/src/storage.ts +0 -64
  356. package/template/packages/license-sdk/src/test-fixtures.ts +0 -59
  357. package/template/packages/license-sdk/src/v1-test-vector.ts +0 -49
  358. package/template/packages/license-sdk/src/validation.test.ts +0 -62
  359. package/template/packages/license-sdk/src/validation.ts +0 -143
  360. package/template/packages/license-sdk/src/verification.test.ts +0 -97
  361. package/template/packages/license-sdk/src/verification.ts +0 -199
@@ -1,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
- }
@@ -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
- }