@zlooks.cn/blog-server 1.0.0

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 (265) hide show
  1. package/dist/blog/blog-application.d.ts +153 -0
  2. package/dist/blog/blog-application.d.ts.map +1 -0
  3. package/dist/blog/blog-application.js +115 -0
  4. package/dist/blog/blog-application.js.map +1 -0
  5. package/dist/blog/comment-client-metadata.d.ts +19 -0
  6. package/dist/blog/comment-client-metadata.d.ts.map +1 -0
  7. package/dist/blog/comment-client-metadata.js +78 -0
  8. package/dist/blog/comment-client-metadata.js.map +1 -0
  9. package/dist/blog/errors.d.ts +12 -0
  10. package/dist/blog/errors.d.ts.map +1 -0
  11. package/dist/blog/errors.js +21 -0
  12. package/dist/blog/errors.js.map +1 -0
  13. package/dist/blog/micro-user-directory.d.ts +11 -0
  14. package/dist/blog/micro-user-directory.d.ts.map +1 -0
  15. package/dist/blog/micro-user-directory.js +18 -0
  16. package/dist/blog/micro-user-directory.js.map +1 -0
  17. package/dist/blog/post-content-audit.d.ts +34 -0
  18. package/dist/blog/post-content-audit.d.ts.map +1 -0
  19. package/dist/blog/post-content-audit.js +190 -0
  20. package/dist/blog/post-content-audit.js.map +1 -0
  21. package/dist/blog/post-view-cache.d.ts +15 -0
  22. package/dist/blog/post-view-cache.d.ts.map +1 -0
  23. package/dist/blog/post-view-cache.js +23 -0
  24. package/dist/blog/post-view-cache.js.map +1 -0
  25. package/dist/blog/public-blog-micro.d.ts +291 -0
  26. package/dist/blog/public-blog-micro.d.ts.map +1 -0
  27. package/dist/blog/public-blog-micro.js +71 -0
  28. package/dist/blog/public-blog-micro.js.map +1 -0
  29. package/dist/blog/public-blog-service.d.ts +104 -0
  30. package/dist/blog/public-blog-service.d.ts.map +1 -0
  31. package/dist/blog/public-blog-service.js +341 -0
  32. package/dist/blog/public-blog-service.js.map +1 -0
  33. package/dist/browser-api/dispatch-stream.d.ts +5 -0
  34. package/dist/browser-api/dispatch-stream.d.ts.map +1 -0
  35. package/dist/browser-api/dispatch-stream.js +4 -0
  36. package/dist/browser-api/dispatch-stream.js.map +1 -0
  37. package/dist/browser-api/dispatcher.d.ts +15 -0
  38. package/dist/browser-api/dispatcher.d.ts.map +1 -0
  39. package/dist/browser-api/dispatcher.js +145 -0
  40. package/dist/browser-api/dispatcher.js.map +1 -0
  41. package/dist/browser-api/provider-publisher.d.ts +24 -0
  42. package/dist/browser-api/provider-publisher.d.ts.map +1 -0
  43. package/dist/browser-api/provider-publisher.js +107 -0
  44. package/dist/browser-api/provider-publisher.js.map +1 -0
  45. package/dist/command/index.d.ts +3 -0
  46. package/dist/command/index.d.ts.map +1 -0
  47. package/dist/command/index.js +24 -0
  48. package/dist/command/index.js.map +1 -0
  49. package/dist/config/schema.d.ts +25 -0
  50. package/dist/config/schema.d.ts.map +1 -0
  51. package/dist/config/schema.js +19 -0
  52. package/dist/config/schema.js.map +1 -0
  53. package/dist/contracts/identity.d.ts +2 -0
  54. package/dist/contracts/identity.d.ts.map +1 -0
  55. package/dist/contracts/identity.js +2 -0
  56. package/dist/contracts/identity.js.map +1 -0
  57. package/dist/contracts/mcp.d.ts +69 -0
  58. package/dist/contracts/mcp.d.ts.map +1 -0
  59. package/dist/contracts/mcp.js +41 -0
  60. package/dist/contracts/mcp.js.map +1 -0
  61. package/dist/contracts/public-api.d.ts +3 -0
  62. package/dist/contracts/public-api.d.ts.map +1 -0
  63. package/dist/contracts/public-api.js +2 -0
  64. package/dist/contracts/public-api.js.map +1 -0
  65. package/dist/mcp/blog-prompts.d.ts +135 -0
  66. package/dist/mcp/blog-prompts.d.ts.map +1 -0
  67. package/dist/mcp/blog-prompts.js +107 -0
  68. package/dist/mcp/blog-prompts.js.map +1 -0
  69. package/dist/mcp/blog-provider.d.ts +4 -0
  70. package/dist/mcp/blog-provider.d.ts.map +1 -0
  71. package/dist/mcp/blog-provider.js +27 -0
  72. package/dist/mcp/blog-provider.js.map +1 -0
  73. package/dist/mcp/blog-theme-authoring-prompt.d.ts +19 -0
  74. package/dist/mcp/blog-theme-authoring-prompt.d.ts.map +1 -0
  75. package/dist/mcp/blog-theme-authoring-prompt.js +171 -0
  76. package/dist/mcp/blog-theme-authoring-prompt.js.map +1 -0
  77. package/dist/mcp/category-tools.d.ts +45 -0
  78. package/dist/mcp/category-tools.d.ts.map +1 -0
  79. package/dist/mcp/category-tools.js +14 -0
  80. package/dist/mcp/category-tools.js.map +1 -0
  81. package/dist/mcp/comment-tools.d.ts +44 -0
  82. package/dist/mcp/comment-tools.d.ts.map +1 -0
  83. package/dist/mcp/comment-tools.js +15 -0
  84. package/dist/mcp/comment-tools.js.map +1 -0
  85. package/dist/mcp/friend-link-tools.d.ts +63 -0
  86. package/dist/mcp/friend-link-tools.d.ts.map +1 -0
  87. package/dist/mcp/friend-link-tools.js +16 -0
  88. package/dist/mcp/friend-link-tools.js.map +1 -0
  89. package/dist/mcp/page-tools.d.ts +67 -0
  90. package/dist/mcp/page-tools.d.ts.map +1 -0
  91. package/dist/mcp/page-tools.js +36 -0
  92. package/dist/mcp/page-tools.js.map +1 -0
  93. package/dist/mcp/post-tools.d.ts +114 -0
  94. package/dist/mcp/post-tools.d.ts.map +1 -0
  95. package/dist/mcp/post-tools.js +122 -0
  96. package/dist/mcp/post-tools.js.map +1 -0
  97. package/dist/mcp/tag-tools.d.ts +37 -0
  98. package/dist/mcp/tag-tools.d.ts.map +1 -0
  99. package/dist/mcp/tag-tools.js +14 -0
  100. package/dist/mcp/tag-tools.js.map +1 -0
  101. package/dist/mcp/theme-tools.d.ts +16 -0
  102. package/dist/mcp/theme-tools.d.ts.map +1 -0
  103. package/dist/mcp/theme-tools.js +71 -0
  104. package/dist/mcp/theme-tools.js.map +1 -0
  105. package/dist/mcp/tool-descriptions.d.ts +5 -0
  106. package/dist/mcp/tool-descriptions.d.ts.map +1 -0
  107. package/dist/mcp/tool-descriptions.js +51 -0
  108. package/dist/mcp/tool-descriptions.js.map +1 -0
  109. package/dist/mcp/tool-support.d.ts +242 -0
  110. package/dist/mcp/tool-support.d.ts.map +1 -0
  111. package/dist/mcp/tool-support.js +127 -0
  112. package/dist/mcp/tool-support.js.map +1 -0
  113. package/dist/messages/browser/dispatch.msg.d.ts +3 -0
  114. package/dist/messages/browser/dispatch.msg.d.ts.map +1 -0
  115. package/dist/messages/browser/dispatch.msg.js +9 -0
  116. package/dist/messages/browser/dispatch.msg.js.map +1 -0
  117. package/dist/messages/public/categories/list.msg.d.ts +3 -0
  118. package/dist/messages/public/categories/list.msg.d.ts.map +1 -0
  119. package/dist/messages/public/categories/list.msg.js +8 -0
  120. package/dist/messages/public/categories/list.msg.js.map +1 -0
  121. package/dist/messages/public/comments/list.msg.d.ts +3 -0
  122. package/dist/messages/public/comments/list.msg.d.ts.map +1 -0
  123. package/dist/messages/public/comments/list.msg.js +8 -0
  124. package/dist/messages/public/comments/list.msg.js.map +1 -0
  125. package/dist/messages/public/comments/recent.msg.d.ts +3 -0
  126. package/dist/messages/public/comments/recent.msg.d.ts.map +1 -0
  127. package/dist/messages/public/comments/recent.msg.js +8 -0
  128. package/dist/messages/public/comments/recent.msg.js.map +1 -0
  129. package/dist/messages/public/friend-links/list.msg.d.ts +3 -0
  130. package/dist/messages/public/friend-links/list.msg.d.ts.map +1 -0
  131. package/dist/messages/public/friend-links/list.msg.js +8 -0
  132. package/dist/messages/public/friend-links/list.msg.js.map +1 -0
  133. package/dist/messages/public/pages/list.msg.d.ts +3 -0
  134. package/dist/messages/public/pages/list.msg.d.ts.map +1 -0
  135. package/dist/messages/public/pages/list.msg.js +8 -0
  136. package/dist/messages/public/pages/list.msg.js.map +1 -0
  137. package/dist/messages/public/pages/read.msg.d.ts +3 -0
  138. package/dist/messages/public/pages/read.msg.d.ts.map +1 -0
  139. package/dist/messages/public/pages/read.msg.js +8 -0
  140. package/dist/messages/public/pages/read.msg.js.map +1 -0
  141. package/dist/messages/public/posts/archive.msg.d.ts +3 -0
  142. package/dist/messages/public/posts/archive.msg.d.ts.map +1 -0
  143. package/dist/messages/public/posts/archive.msg.js +8 -0
  144. package/dist/messages/public/posts/archive.msg.js.map +1 -0
  145. package/dist/messages/public/posts/connections.msg.d.ts +3 -0
  146. package/dist/messages/public/posts/connections.msg.d.ts.map +1 -0
  147. package/dist/messages/public/posts/connections.msg.js +8 -0
  148. package/dist/messages/public/posts/connections.msg.js.map +1 -0
  149. package/dist/messages/public/posts/list.msg.d.ts +3 -0
  150. package/dist/messages/public/posts/list.msg.d.ts.map +1 -0
  151. package/dist/messages/public/posts/list.msg.js +8 -0
  152. package/dist/messages/public/posts/list.msg.js.map +1 -0
  153. package/dist/messages/public/posts/open.msg.d.ts +3 -0
  154. package/dist/messages/public/posts/open.msg.d.ts.map +1 -0
  155. package/dist/messages/public/posts/open.msg.js +8 -0
  156. package/dist/messages/public/posts/open.msg.js.map +1 -0
  157. package/dist/messages/public/posts/read.msg.d.ts +3 -0
  158. package/dist/messages/public/posts/read.msg.d.ts.map +1 -0
  159. package/dist/messages/public/posts/read.msg.js +8 -0
  160. package/dist/messages/public/posts/read.msg.js.map +1 -0
  161. package/dist/messages/public/tags/list.msg.d.ts +3 -0
  162. package/dist/messages/public/tags/list.msg.d.ts.map +1 -0
  163. package/dist/messages/public/tags/list.msg.js +8 -0
  164. package/dist/messages/public/tags/list.msg.js.map +1 -0
  165. package/dist/migrations/index.d.ts +3 -0
  166. package/dist/migrations/index.d.ts.map +1 -0
  167. package/dist/migrations/index.js +2 -0
  168. package/dist/migrations/index.js.map +1 -0
  169. package/dist/persistence/category-store.d.ts +23 -0
  170. package/dist/persistence/category-store.d.ts.map +1 -0
  171. package/dist/persistence/category-store.js +86 -0
  172. package/dist/persistence/category-store.js.map +1 -0
  173. package/dist/persistence/comment-store.d.ts +41 -0
  174. package/dist/persistence/comment-store.d.ts.map +1 -0
  175. package/dist/persistence/comment-store.js +146 -0
  176. package/dist/persistence/comment-store.js.map +1 -0
  177. package/dist/persistence/entities.d.ts +103 -0
  178. package/dist/persistence/entities.d.ts.map +1 -0
  179. package/dist/persistence/entities.js +188 -0
  180. package/dist/persistence/entities.js.map +1 -0
  181. package/dist/persistence/friend-link-store.d.ts +26 -0
  182. package/dist/persistence/friend-link-store.d.ts.map +1 -0
  183. package/dist/persistence/friend-link-store.js +80 -0
  184. package/dist/persistence/friend-link-store.js.map +1 -0
  185. package/dist/persistence/page-store.d.ts +35 -0
  186. package/dist/persistence/page-store.d.ts.map +1 -0
  187. package/dist/persistence/page-store.js +125 -0
  188. package/dist/persistence/page-store.js.map +1 -0
  189. package/dist/persistence/post-engagement-store.d.ts +23 -0
  190. package/dist/persistence/post-engagement-store.d.ts.map +1 -0
  191. package/dist/persistence/post-engagement-store.js +85 -0
  192. package/dist/persistence/post-engagement-store.js.map +1 -0
  193. package/dist/persistence/post-store.d.ts +87 -0
  194. package/dist/persistence/post-store.d.ts.map +1 -0
  195. package/dist/persistence/post-store.js +465 -0
  196. package/dist/persistence/post-store.js.map +1 -0
  197. package/dist/persistence/tag-store.d.ts +21 -0
  198. package/dist/persistence/tag-store.d.ts.map +1 -0
  199. package/dist/persistence/tag-store.js +82 -0
  200. package/dist/persistence/tag-store.js.map +1 -0
  201. package/dist/services/application.service.d.ts +4 -0
  202. package/dist/services/application.service.d.ts.map +1 -0
  203. package/dist/services/application.service.js +22 -0
  204. package/dist/services/application.service.js.map +1 -0
  205. package/dist/services/blog-runtime.service.d.ts +10 -0
  206. package/dist/services/blog-runtime.service.d.ts.map +1 -0
  207. package/dist/services/blog-runtime.service.js +32 -0
  208. package/dist/services/blog-runtime.service.js.map +1 -0
  209. package/dist/services/blog.boot.d.ts +6 -0
  210. package/dist/services/blog.boot.d.ts.map +1 -0
  211. package/dist/services/blog.boot.js +13 -0
  212. package/dist/services/blog.boot.js.map +1 -0
  213. package/dist/services/browser-api-provider.service.d.ts +4 -0
  214. package/dist/services/browser-api-provider.service.d.ts.map +1 -0
  215. package/dist/services/browser-api-provider.service.js +25 -0
  216. package/dist/services/browser-api-provider.service.js.map +1 -0
  217. package/dist/services/config.service.d.ts +3 -0
  218. package/dist/services/config.service.d.ts.map +1 -0
  219. package/dist/services/config.service.js +22 -0
  220. package/dist/services/config.service.js.map +1 -0
  221. package/dist/services/database.service.d.ts +3 -0
  222. package/dist/services/database.service.d.ts.map +1 -0
  223. package/dist/services/database.service.js +16 -0
  224. package/dist/services/database.service.js.map +1 -0
  225. package/dist/services/logger.service.d.ts +4 -0
  226. package/dist/services/logger.service.d.ts.map +1 -0
  227. package/dist/services/logger.service.js +8 -0
  228. package/dist/services/logger.service.js.map +1 -0
  229. package/dist/services/mcp-provider.service.d.ts +3 -0
  230. package/dist/services/mcp-provider.service.d.ts.map +1 -0
  231. package/dist/services/mcp-provider.service.js +17 -0
  232. package/dist/services/mcp-provider.service.js.map +1 -0
  233. package/dist/services/redis.service.d.ts +4 -0
  234. package/dist/services/redis.service.d.ts.map +1 -0
  235. package/dist/services/redis.service.js +18 -0
  236. package/dist/services/redis.service.js.map +1 -0
  237. package/dist/services/theme-control.service.d.ts +4 -0
  238. package/dist/services/theme-control.service.d.ts.map +1 -0
  239. package/dist/services/theme-control.service.js +53 -0
  240. package/dist/services/theme-control.service.js.map +1 -0
  241. package/dist/themes/theme-catalog.d.ts +40 -0
  242. package/dist/themes/theme-catalog.d.ts.map +1 -0
  243. package/dist/themes/theme-catalog.js +164 -0
  244. package/dist/themes/theme-catalog.js.map +1 -0
  245. package/dist/themes/theme-control.d.ts +16 -0
  246. package/dist/themes/theme-control.d.ts.map +1 -0
  247. package/dist/themes/theme-control.js +57 -0
  248. package/dist/themes/theme-control.js.map +1 -0
  249. package/dist/themes/theme-discovery-runtime.d.ts +41 -0
  250. package/dist/themes/theme-discovery-runtime.d.ts.map +1 -0
  251. package/dist/themes/theme-discovery-runtime.js +109 -0
  252. package/dist/themes/theme-discovery-runtime.js.map +1 -0
  253. package/dist/themes/theme-generation-store.d.ts +14 -0
  254. package/dist/themes/theme-generation-store.d.ts.map +1 -0
  255. package/dist/themes/theme-generation-store.js +49 -0
  256. package/dist/themes/theme-generation-store.js.map +1 -0
  257. package/dist/themes/theme-selection-publisher.d.ts +28 -0
  258. package/dist/themes/theme-selection-publisher.d.ts.map +1 -0
  259. package/dist/themes/theme-selection-publisher.js +92 -0
  260. package/dist/themes/theme-selection-publisher.js.map +1 -0
  261. package/dist/themes/theme-selection-store.d.ts +22 -0
  262. package/dist/themes/theme-selection-store.d.ts.map +1 -0
  263. package/dist/themes/theme-selection-store.js +84 -0
  264. package/dist/themes/theme-selection-store.js.map +1 -0
  265. package/package.json +74 -0
@@ -0,0 +1,92 @@
1
+ import { blogThemeSelectionSchema, blogThemeSelectionTopic, } from '@zlooks.cn/blog-shared';
2
+ export class ThemeSelectionPublisher {
3
+ options;
4
+ #clock;
5
+ #publication;
6
+ #timer;
7
+ #refreshing;
8
+ #closing;
9
+ #closed = false;
10
+ #sequence = -1;
11
+ constructor(options) {
12
+ this.options = options;
13
+ if (!Number.isSafeInteger(options.leaseMs) || options.leaseMs < 500 || options.leaseMs > 30_000) {
14
+ throw new TypeError('Blog theme selection lease must be between 500 and 30000 milliseconds');
15
+ }
16
+ this.#clock = options.clock ?? (() => new Date());
17
+ }
18
+ async start() {
19
+ if (this.#closed)
20
+ throw new Error('Blog theme selection publisher is closed');
21
+ if (this.#publication)
22
+ return;
23
+ const selection = await this.#announcement();
24
+ this.#publication = await this.options.application.publish(blogThemeSelectionTopic(this.options.instanceId), selection);
25
+ this.#timer = setInterval(() => {
26
+ void this.refresh().catch((error) => this.options.onError?.(error));
27
+ }, Math.max(250, Math.floor(this.options.leaseMs / 2)));
28
+ this.#timer.unref();
29
+ }
30
+ refresh(requiredRevision = 0) {
31
+ if (this.#closed)
32
+ return Promise.reject(new Error('Blog theme selection publisher is closed'));
33
+ if (this.#refreshing) {
34
+ if (requiredRevision === 0)
35
+ return this.#refreshing;
36
+ const operation = this.#refreshing.then((current) => current.revision >= requiredRevision ? current : this.#refresh(), () => this.#refresh());
37
+ return this.#trackRefresh(operation);
38
+ }
39
+ return this.#beginRefresh();
40
+ }
41
+ #beginRefresh() {
42
+ return this.#trackRefresh(this.#refresh());
43
+ }
44
+ #trackRefresh(operation) {
45
+ this.#refreshing = operation;
46
+ void operation.finally(() => {
47
+ if (this.#refreshing === operation)
48
+ this.#refreshing = undefined;
49
+ }).catch(() => undefined);
50
+ return operation;
51
+ }
52
+ async #refresh() {
53
+ const selection = await this.#announcement();
54
+ const publication = this.#publication;
55
+ if (!publication)
56
+ throw new Error('Blog theme selection publisher is not started');
57
+ await publication.update(selection);
58
+ return selection;
59
+ }
60
+ async #announcement() {
61
+ const selection = await this.options.store.read();
62
+ const issuedAt = this.#clock();
63
+ this.#sequence = Math.max(this.#sequence + 1, issuedAt.getTime());
64
+ return blogThemeSelectionSchema.parse({
65
+ instanceId: this.options.instanceId,
66
+ ...selection,
67
+ lease: {
68
+ issuedAt: issuedAt.toISOString(),
69
+ expiresAt: new Date(issuedAt.getTime() + this.options.leaseMs).toISOString(),
70
+ sequence: this.#sequence,
71
+ },
72
+ });
73
+ }
74
+ close() {
75
+ if (this.#closing)
76
+ return this.#closing;
77
+ this.#closed = true;
78
+ const operation = this.#close();
79
+ this.#closing = operation;
80
+ return operation.finally(() => { if (this.#closing === operation)
81
+ this.#closing = undefined; });
82
+ }
83
+ async #close() {
84
+ if (this.#timer)
85
+ clearInterval(this.#timer);
86
+ this.#timer = undefined;
87
+ await this.#refreshing?.catch(() => undefined);
88
+ await this.#publication?.unpublish();
89
+ this.#publication = undefined;
90
+ }
91
+ }
92
+ //# sourceMappingURL=theme-selection-publisher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-selection-publisher.js","sourceRoot":"","sources":["../../src/themes/theme-selection-publisher.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod';
2
+ declare const selectionSchema: z.ZodObject<{
3
+ themeId: z.ZodReadonly<z.ZodString>;
4
+ revision: z.ZodReadonly<z.ZodNumber>;
5
+ updatedAt: z.ZodISODateTime;
6
+ updatedBy: z.ZodString;
7
+ }, z.core.$strict>;
8
+ export type ThemeSelectionState = Readonly<z.infer<typeof selectionSchema>>;
9
+ export interface ThemeSelectionRedis {
10
+ hgetall(key: string): Promise<Record<string, string>>;
11
+ eval(script: string, keyCount: number, ...args: readonly (string | number)[]): Promise<unknown>;
12
+ }
13
+ export declare class RedisThemeSelectionStore {
14
+ #private;
15
+ private readonly redis;
16
+ private readonly clock;
17
+ constructor(redis: ThemeSelectionRedis, clock?: () => Date);
18
+ read(): Promise<ThemeSelectionState>;
19
+ select(themeId: string, updatedBy: string, signal?: AbortSignal): Promise<ThemeSelectionState>;
20
+ }
21
+ export {};
22
+ //# sourceMappingURL=theme-selection-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-selection-store.d.ts","sourceRoot":"","sources":["../../src/themes/theme-selection-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,QAAA,MAAM,eAAe;;;;;kBAKnB,CAAC;AAOH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC,CAAC;AAE5E,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACjG;AA4BD,qBAAa,wBAAwB;;IAEjC,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFxB,YACmB,KAAK,EAAE,mBAAmB,EAC1B,KAAK,GAAE,MAAM,IAAuB,EACnD;IAES,IAAI,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAEhD;IAqBY,MAAM,CACjB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,CAAC,CAsB9B;CACF"}
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod';
2
+ import { blogThemeIdSchema, blogThemeSelectionRevisionSchema } from '@zlooks.cn/blog-shared';
3
+ const REDIS_KEY = 'theme:selection';
4
+ const selectionSchema = z.strictObject({
5
+ themeId: blogThemeIdSchema,
6
+ revision: blogThemeSelectionRevisionSchema,
7
+ updatedAt: z.iso.datetime({ offset: true }),
8
+ updatedBy: z.string().trim().min(1).max(256),
9
+ });
10
+ const persistedSelectionSchema = selectionSchema.extend({
11
+ revision: z.string().regex(/^(?:0|[1-9][0-9]*)$/)
12
+ .transform(Number)
13
+ .pipe(blogThemeSelectionRevisionSchema),
14
+ });
15
+ const MAX_CAS_ATTEMPTS = 8;
16
+ const casResultSchema = z.tuple([z.enum(['ok', 'conflict'])]);
17
+ const CAS_SCRIPT = `
18
+ local count = redis.call('HLEN', KEYS[1])
19
+ if ARGV[1] == '1' then
20
+ if count ~= 0 then return {'conflict'} end
21
+ else
22
+ if count ~= 4 then return {'conflict'} end
23
+ local current = redis.call('HMGET', KEYS[1], 'themeId', 'revision', 'updatedAt', 'updatedBy')
24
+ if current[1] ~= ARGV[2] or current[2] ~= ARGV[3]
25
+ or current[3] ~= ARGV[4] or current[4] ~= ARGV[5] then
26
+ return {'conflict'}
27
+ end
28
+ end
29
+ redis.call('HSET', KEYS[1],
30
+ 'themeId', ARGV[6], 'revision', ARGV[7], 'updatedAt', ARGV[8], 'updatedBy', ARGV[9])
31
+ return {'ok'}
32
+ `;
33
+ export class RedisThemeSelectionStore {
34
+ redis;
35
+ clock;
36
+ constructor(redis, clock = () => new Date()) {
37
+ this.redis = redis;
38
+ this.clock = clock;
39
+ }
40
+ async read() {
41
+ return (await this.#readSnapshot()).state;
42
+ }
43
+ async #readSnapshot() {
44
+ const persisted = await this.redis.hgetall(REDIS_KEY);
45
+ if (Object.keys(persisted).length === 0) {
46
+ const raw = {
47
+ themeId: 'default', revision: '0', updatedAt: new Date(0).toISOString(), updatedBy: 'bootstrap',
48
+ };
49
+ return Object.freeze({ empty: true, raw: Object.freeze(raw), state: Object.freeze(selectionSchema.parse({
50
+ themeId: 'default', revision: 0, updatedAt: new Date(0).toISOString(), updatedBy: 'bootstrap',
51
+ })) });
52
+ }
53
+ const parsed = persistedSelectionSchema.safeParse(persisted);
54
+ if (!parsed.success)
55
+ throw new Error('Blog theme selection in Redis is invalid', { cause: parsed.error });
56
+ const raw = {
57
+ themeId: persisted.themeId, revision: persisted.revision,
58
+ updatedAt: persisted.updatedAt, updatedBy: persisted.updatedBy,
59
+ };
60
+ return Object.freeze({ empty: false, raw: Object.freeze(raw), state: Object.freeze(parsed.data) });
61
+ }
62
+ async select(themeId, updatedBy, signal) {
63
+ const selectedThemeId = blogThemeIdSchema.parse(themeId);
64
+ const actor = z.string().trim().min(1).max(256).parse(updatedBy);
65
+ const updatedAt = this.clock().toISOString();
66
+ for (let attempt = 0; attempt < MAX_CAS_ATTEMPTS; attempt += 1) {
67
+ const current = await this.#readSnapshot();
68
+ signal?.throwIfAborted();
69
+ if (current.state.themeId === selectedThemeId)
70
+ return current.state;
71
+ if (current.state.revision >= Number.MAX_SAFE_INTEGER - 1) {
72
+ throw new Error('Blog theme selection revision is exhausted');
73
+ }
74
+ const next = Object.freeze(selectionSchema.parse({
75
+ themeId: selectedThemeId, revision: current.state.revision + 1, updatedAt, updatedBy: actor,
76
+ }));
77
+ const [result] = casResultSchema.parse(await this.redis.eval(CAS_SCRIPT, 1, REDIS_KEY, current.empty ? '1' : '0', current.raw.themeId, current.raw.revision, current.raw.updatedAt, current.raw.updatedBy, next.themeId, String(next.revision), next.updatedAt, next.updatedBy));
78
+ if (result === 'ok')
79
+ return next;
80
+ }
81
+ throw new Error('Blog theme selection changed concurrently; retry the operation');
82
+ }
83
+ }
84
+ //# sourceMappingURL=theme-selection-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme-selection-store.js","sourceRoot":"","sources":["../../src/themes/theme-selection-store.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@zlooks.cn/blog-server",
3
+ "version": "1.0.0",
4
+ "private": false,
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=24.0.0"
8
+ },
9
+ "bin": {
10
+ "zlooks-blog-server": "dist/command/index.js"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "zlooks": {
16
+ "kind": "service",
17
+ "description": "博客领域服务",
18
+ "dependsOn": [],
19
+ "commands": {
20
+ "db": [
21
+ "sync",
22
+ "migrate"
23
+ ]
24
+ }
25
+ },
26
+ "scripts": {
27
+ "build": "pnpm run clean && tsc -p tsconfig.json && fix-esm-import-path --preserve-import-type ./dist",
28
+ "clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
29
+ "dev": "hile start --dev --env-file ~/.zlooks.cn/.env",
30
+ "start": "hile start --env-file ~/.zlooks.cn/.env",
31
+ "test": "vitest run",
32
+ "typecheck": "tsc -p tsconfig.json --noEmit"
33
+ },
34
+ "dependencies": {
35
+ "@hile/context": "4.0.2",
36
+ "@hile/core": "4.0.1",
37
+ "@hile/ioredis": "4.0.1",
38
+ "@hile/logger": "4.0.1",
39
+ "@hile/mcp": "1.0.4",
40
+ "@hile/message-loader": "4.0.3",
41
+ "@hile/micro": "4.0.3",
42
+ "@hile/typeorm": "4.0.1",
43
+ "@zlooks.cn/blog-schema": "1.0.0",
44
+ "@zlooks.cn/blog-shared": "1.0.0",
45
+ "@zlooks.cn/browser-api": "1.0.0",
46
+ "@zlooks.cn/database-command": "1.0.0",
47
+ "@zlooks.cn/infrastructure": "1.0.0",
48
+ "@zlooks.cn/public-host-contract": "1.0.0",
49
+ "@zlooks.cn/registry-config": "1.0.0",
50
+ "@zlooks.cn/service-command": "1.0.0",
51
+ "@zlooks.cn/user-shared": "1.0.0",
52
+ "@zlooks.cn/utils": "1.0.0",
53
+ "detect-port": "2.1.0",
54
+ "ioredis": "5.11.1",
55
+ "ip2region-ts": "2.0.1",
56
+ "mdast-util-to-string": "4.0.0",
57
+ "pg": "8.23.0",
58
+ "remark-parse": "11.0.0",
59
+ "typeorm": "1.1.0",
60
+ "unified": "11.0.5",
61
+ "unist-util-visit": "5.1.0",
62
+ "zod": "4.4.3"
63
+ },
64
+ "devDependencies": {
65
+ "@hile/cli": "4.0.4",
66
+ "@types/mdast": "4.0.4",
67
+ "@types/node": "26.2.0",
68
+ "fix-esm-import-path": "1.10.3",
69
+ "pg-mem": "3.0.14",
70
+ "typescript": "7.0.2",
71
+ "vitest": "4.1.11"
72
+ },
73
+ "gitHead": "a3e825c2a62561f73f49a54720e286f08da63429"
74
+ }