@allbluecn/web-app 0.19.4 → 0.20.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 (213) hide show
  1. package/LICENSE +661 -0
  2. package/package.json +35 -37
  3. package/public/fonts/Trap-font/Trap-Black.otf +0 -0
  4. package/public/fonts/Trap-font/Trap-Bold.otf +0 -0
  5. package/public/fonts/Trap-font/Trap-ExtraBold.otf +0 -0
  6. package/public/fonts/Trap-font/Trap-Light.otf +0 -0
  7. package/public/fonts/Trap-font/Trap-Medium.otf +0 -0
  8. package/public/fonts/Trap-font/Trap-Regular.otf +0 -0
  9. package/public/fonts/Trap-font/Trap-SemiBold.otf +0 -0
  10. package/public/fonts/buttersans-Rounded.otf +0 -0
  11. package/src/components/bug/__tests__/board-subgroups.test.tsx +1 -1
  12. package/src/components/bug/__tests__/board-view.test.tsx +1 -1
  13. package/src/components/bug/__tests__/comment-reactions.test.tsx +1 -1
  14. package/src/components/bug/__tests__/create-bug-dialog.test.tsx +2 -2
  15. package/src/components/bug/__tests__/filters-compat.test.tsx +1 -1
  16. package/src/components/bug/__tests__/fix-request-race.test.tsx +1 -1
  17. package/src/components/bug/__tests__/peek-modes.test.tsx +1 -1
  18. package/src/components/bug/__tests__/use-paged-bug-groups.test.tsx +1 -1
  19. package/src/components/bug/__tests__/view-type.test.tsx +1 -1
  20. package/src/components/bug/bug-list-view.tsx +2 -2
  21. package/src/components/bug/bug-toolbar.tsx +1 -1
  22. package/src/components/bug/create-bug-dialog.tsx +3 -3
  23. package/src/components/bug/detail/__tests__/activity-timeline.test.tsx +1 -1
  24. package/src/components/bug/detail/__tests__/bug-detail-collab.test.tsx +1 -1
  25. package/src/components/bug/detail/__tests__/repro-report-card.test.tsx +1 -1
  26. package/src/components/bug/detail/activity-tabs.tsx +1 -1
  27. package/src/components/bug/detail/activity-timeline.tsx +1 -1
  28. package/src/components/bug/detail/bug-detail-body.tsx +3 -3
  29. package/src/components/bug/detail/comment-input.tsx +1 -1
  30. package/src/components/bug/detail/comment-item.tsx +1 -1
  31. package/src/components/bug/detail/comment-reactions.tsx +1 -1
  32. package/src/components/bug/detail/comment-thread.tsx +1 -1
  33. package/src/components/bug/detail/props-section.tsx +2 -2
  34. package/src/components/bug/detail/repro-report-card.tsx +1 -1
  35. package/src/components/bug/detail/reproduce-steps-view.tsx +1 -1
  36. package/src/components/bug/display/use-paged-bug-groups.ts +2 -2
  37. package/src/components/bug/filters/field-configs.tsx +1 -1
  38. package/src/components/bug/inline-editors/assignee-editor.tsx +1 -1
  39. package/src/components/bug/peek-panel.tsx +2 -2
  40. package/src/components/bug/reproduce-steps-editor.tsx +1 -1
  41. package/src/components/bug/story-bugs-panel.tsx +2 -1
  42. package/src/components/bug/views/board-view.tsx +2 -2
  43. package/src/components/bug/views/bug-list/batch-toolbar.tsx +1 -1
  44. package/src/components/bug/views/bug-list/group.tsx +1 -1
  45. package/src/components/bug/views/bug-list/index.tsx +1 -1
  46. package/src/components/bug/views/bug-list/inline-bug-creator.tsx +1 -1
  47. package/src/components/bug/views/bug-list/row.tsx +2 -2
  48. package/src/components/bug/views/create-view-dialog.tsx +1 -1
  49. package/src/components/iteration/iteration-progress-bar.tsx +2 -2
  50. package/src/components/knowledge/public/legal-compliance-editor.tsx +1 -1
  51. package/src/components/layout/sidebar-03/nav-knowledge.tsx +1 -1
  52. package/src/components/layout/sidebar-03/nav-workspace.tsx +1 -1
  53. package/src/components/shared-kit/list-kit/kanban/__tests__/instance-key.test.ts +1 -1
  54. package/src/components/story/__tests__/constants.test.ts +1 -1
  55. package/src/components/story/__tests__/dependency-type-badge.test.ts +1 -1
  56. package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +1 -1
  57. package/src/components/story/__tests__/story-list-view-peek-sync.test.tsx +1 -1
  58. package/src/components/story/ai/story-ai-panel.tsx +1 -1
  59. package/src/components/story/ai/story-ai-preview-list.tsx +2 -2
  60. package/src/components/story/columns.tsx +2 -2
  61. package/src/components/story/create-story-dialog.tsx +4 -4
  62. package/src/components/story/detail/__tests__/activity-timeline.test.tsx +1 -1
  63. package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +1 -1
  64. package/src/components/story/detail/__tests__/story-collab.test.tsx +1 -1
  65. package/src/components/story/detail/activity-tabs.tsx +1 -1
  66. package/src/components/story/detail/activity-timeline.tsx +2 -2
  67. package/src/components/story/detail/comment-input.tsx +1 -1
  68. package/src/components/story/detail/comment-item.tsx +1 -1
  69. package/src/components/story/detail/comment-thread.tsx +1 -1
  70. package/src/components/story/detail/reaction-bar.tsx +1 -1
  71. package/src/components/story/detail/story-detail-body.tsx +4 -4
  72. package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +2 -2
  73. package/src/components/story/detail/story-detail-toolbar/children.tsx +1 -1
  74. package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +3 -3
  75. package/src/components/story/detail/story-detail-toolbar/index.tsx +8 -8
  76. package/src/components/story/detail/story-detail-toolbar/related-test-cases.tsx +2 -2
  77. package/src/components/story/detail/story-properties.tsx +2 -2
  78. package/src/components/story/display/__tests__/flatten-story-groups.test.ts +1 -1
  79. package/src/components/story/display/__tests__/group-fields.test.ts +1 -1
  80. package/src/components/story/display/__tests__/group-resolver.test.ts +2 -2
  81. package/src/components/story/display/__tests__/order-resolver.test.ts +1 -1
  82. package/src/components/story/display/__tests__/use-grouped-stories.test.ts +1 -1
  83. package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +1 -1
  84. package/src/components/story/display/flatten-story-groups.ts +1 -1
  85. package/src/components/story/display/group-fields.ts +2 -2
  86. package/src/components/story/display/group-resolver.tsx +2 -2
  87. package/src/components/story/display/order-resolver.ts +2 -2
  88. package/src/components/story/display/types.ts +1 -1
  89. package/src/components/story/display/use-grouped-stories.ts +1 -1
  90. package/src/components/story/display/use-paged-story-groups.ts +2 -2
  91. package/src/components/story/entity-overlay.ts +24 -0
  92. package/src/components/story/filters/field-configs.tsx +2 -2
  93. package/src/components/story/inline-editors/assignee-editor.tsx +1 -1
  94. package/src/components/story/inline-editors/dependency-type-select.tsx +1 -1
  95. package/src/components/story/inline-editors/parent-story-editor.tsx +2 -2
  96. package/src/components/story/inline-editors/priority-editor.tsx +2 -2
  97. package/src/components/story/inline-editors/state-editor.tsx +2 -2
  98. package/src/components/story/inline-editors/story-select-dialog.tsx +2 -2
  99. package/src/components/story/peek-panel.tsx +3 -3
  100. package/src/components/story/priority-icon.tsx +2 -2
  101. package/src/components/story/relations/__tests__/attachment-manage-dialog.test.tsx +1 -1
  102. package/src/components/story/relations/__tests__/design-select-dialog.test.tsx +1 -1
  103. package/src/components/story/relations/__tests__/knowledge-select-dialog.test.tsx +1 -1
  104. package/src/components/story/relations/__tests__/link-manage-dialog.test.tsx +1 -1
  105. package/src/components/story/relations/attachment-manage-dialog.tsx +4 -4
  106. package/src/components/story/relations/design-select-dialog.tsx +2 -2
  107. package/src/components/story/relations/knowledge-select-dialog.tsx +2 -2
  108. package/src/components/story/relations/link-manage-dialog.tsx +2 -2
  109. package/src/components/story/relations/story-ref-row.tsx +3 -3
  110. package/src/components/story/story-list-table.tsx +1 -1
  111. package/src/components/story/story-list-view.tsx +1 -1
  112. package/src/components/story/story-toolbar.tsx +1 -1
  113. package/src/components/story/views/__tests__/calendar-view.test.tsx +1 -1
  114. package/src/components/story/views/__tests__/kanban-drop-subgroup.test.ts +1 -1
  115. package/src/components/story/views/__tests__/kanban-peek-sync.test.tsx +2 -2
  116. package/src/components/story/views/__tests__/kanban-view-membership-sync.test.tsx +1 -1
  117. package/src/components/story/views/__tests__/timeline-view.test.tsx +1 -1
  118. package/src/components/story/views/calendar/__tests__/mappers.test.ts +1 -1
  119. package/src/components/story/views/calendar/mappers.ts +1 -1
  120. package/src/components/story/views/calendar-view.tsx +1 -1
  121. package/src/components/story/views/create-view-dialog.tsx +1 -1
  122. package/src/components/story/views/kanban-view.tsx +2 -2
  123. package/src/components/story/views/story-list/batch-preview-dialog.tsx +2 -2
  124. package/src/components/story/views/story-list/batch-toolbar.tsx +2 -2
  125. package/src/components/story/views/story-list/group.tsx +2 -2
  126. package/src/components/story/views/story-list/index.tsx +2 -2
  127. package/src/components/story/views/story-list/inline-story-creator.tsx +2 -2
  128. package/src/components/story/views/story-list/row.tsx +2 -2
  129. package/src/components/story/views/story-list/use-flat-rows.ts +1 -1
  130. package/src/components/story/views/timeline/__tests__/mappers.test.ts +2 -2
  131. package/src/components/story/views/timeline/mappers.ts +2 -2
  132. package/src/components/story/views/timeline/use-date-commit.ts +1 -1
  133. package/src/components/story/views/timeline-view.tsx +2 -2
  134. package/src/components/testcase/__tests__/case-row.test.tsx +1 -1
  135. package/src/components/testcase/__tests__/create-dialog-scope.test.tsx +1 -1
  136. package/src/components/testcase/__tests__/display-options.test.tsx +1 -1
  137. package/src/components/testcase/__tests__/export-entry.test.tsx +1 -1
  138. package/src/components/testcase/__tests__/list-view.test.tsx +1 -1
  139. package/src/components/testcase/__tests__/step-editor.test.tsx +1 -1
  140. package/src/components/testcase/__tests__/toolbar.test.tsx +1 -1
  141. package/src/components/testcase/create-testcase-dialog.tsx +1 -1
  142. package/src/components/testcase/detail/case-detail-body.tsx +1 -1
  143. package/src/components/testcase/detail/properties.tsx +1 -1
  144. package/src/components/testcase/display/display-dropdown.tsx +2 -2
  145. package/src/components/testcase/display/use-testcase-display-options.ts +1 -1
  146. package/src/components/testcase/filters/field-configs.tsx +1 -1
  147. package/src/components/testcase/inline-editors/assignee-editor.tsx +1 -1
  148. package/src/components/testcase/list/case-row.tsx +1 -1
  149. package/src/components/testcase/list/grouped-list.tsx +1 -1
  150. package/src/components/testcase/list-view.tsx +2 -2
  151. package/src/components/testcase/runs/convert-bug-button.tsx +2 -2
  152. package/src/components/testcase/testcase-toolbar.tsx +2 -2
  153. package/src/components/testcase/views/batch-toolbar.tsx +1 -1
  154. package/src/components/testcase/views/board-view.tsx +2 -2
  155. package/src/lib/changelog/sdk-versions.ts +2 -2
  156. package/src/lib/collections/knowledge/knowledge-bases-collection.ts +2 -1
  157. package/src/lib/collections/query-keys.ts +18 -0
  158. package/src/lib/collections/reference/users-brief-collection.ts +2 -1
  159. package/src/lib/collections/reference/workspaces-collection.ts +2 -1
  160. package/src/lib/story/__tests__/activity-grouper.test.ts +1 -1
  161. package/src/lib/story/activity-grouper.ts +1 -1
  162. package/src/paraglide/.gitignore +3 -0
  163. package/src/paraglide/.prettierignore +3 -0
  164. package/src/paraglide/README.md +162 -0
  165. package/src/paraglide/messages/_index.js +88192 -0
  166. package/src/paraglide/messages/en.js +28230 -0
  167. package/src/paraglide/messages/zh.js +28228 -0
  168. package/src/paraglide/messages.js +4 -0
  169. package/src/paraglide/registry.js +46 -0
  170. package/src/paraglide/runtime.js +2311 -0
  171. package/src/paraglide/server.js +294 -0
  172. package/src/routes/_nav/bugs/$bugId/route.lazy.tsx +2 -2
  173. package/src/routes/_nav/projects/$projectId/iterations/$iterationId/route.lazy.tsx +1 -1
  174. package/src/routes/_nav/route.tsx +1 -2
  175. package/src/routes/_nav/stories/$storyId/route.lazy.tsx +2 -2
  176. package/src/routes/_nav/stories/$storyId/route.tsx +1 -1
  177. package/src/routes/api/design/prototype/__tests__/route.test.ts +4 -1
  178. package/src/server/__tests__/task-executor.test.ts +7 -0
  179. package/src/server/serverFns/__tests__/legal-code-canonical.test.ts +169 -0
  180. package/src/server/serverFns/__tests__/story-tree.test.ts +0 -1
  181. package/src/server/serverFns/__tests__/update-bug-guard.test.ts +1 -1
  182. package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +1 -1
  183. package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +1 -1
  184. package/src/server/serverFns/bug/bug-activities.ts +2 -2
  185. package/src/server/serverFns/bug/bug-comments.ts +3 -3
  186. package/src/server/serverFns/bug/bug-crud/detail-select.ts +1 -1
  187. package/src/server/serverFns/bug/bug-crud/detail.ts +2 -2
  188. package/src/server/serverFns/bug/bug-crud.ts +1 -1
  189. package/src/server/serverFns/bug/bug-group-query.ts +1 -1
  190. package/src/server/serverFns/bug/bug-groups.ts +2 -2
  191. package/src/server/serverFns/bug/bug-story-utils.ts +1 -1
  192. package/src/server/serverFns/iteration/iteration-crud.ts +1 -1
  193. package/src/server/serverFns/knowledge/public/legal/create.ts +3 -0
  194. package/src/server/serverFns/knowledge/public/legal/update.ts +8 -8
  195. package/src/server/serverFns/story/link-preview.ts +3 -2
  196. package/src/server/serverFns/story/story-children.ts +1 -1
  197. package/src/server/serverFns/story/story-comments.ts +1 -1
  198. package/src/server/serverFns/story/story-crud/bulk.ts +1 -1
  199. package/src/server/serverFns/story/story-crud.ts +1 -1
  200. package/src/server/serverFns/story/story-dependencies.ts +1 -1
  201. package/src/server/serverFns/story/story-groups.ts +1 -1
  202. package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +1 -1
  203. package/src/server/serverFns/testcase/testcase-board.ts +1 -1
  204. package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
  205. package/src/server/serverFns/testcase/testcase-display-options.ts +1 -1
  206. package/src/server/serverFns/testcase/testcase-filter-views.ts +1 -1
  207. package/src/server/serverFns/testcase/types.ts +1 -1
  208. package/src/server/story/types.ts +2 -2
  209. package/src/shared/bug/__tests__/constants.test.ts +1 -1
  210. package/src/{components → shared}/bug/types.ts +4 -3
  211. package/src/{components → shared}/story/constants.ts +1 -1
  212. package/src/{components → shared}/story/types.ts +1 -7
  213. /package/src/{components/testcase/display → shared/testcase}/field-configs.ts +0 -0
@@ -0,0 +1,294 @@
1
+ /* eslint-disable */
2
+
3
+ import * as runtime from "./runtime.js";
4
+
5
+ /**
6
+ * Server middleware that handles locale-based routing and request processing.
7
+ *
8
+ * Configure `disableAsyncLocalStorage` when generating Paraglide with
9
+ * `paraglideVitePlugin()` or `compile()`, not when calling
10
+ * `paraglideMiddleware()`. Keep AsyncLocalStorage enabled by default and
11
+ * only disable it for runtimes that lack `AsyncLocalStorage` support and
12
+ * guarantee request isolation.
13
+ *
14
+ * This middleware performs several key functions:
15
+ *
16
+ * 1. Determines the locale for the incoming request using configured strategies
17
+ * 2. Handles URL localization and redirects (only for document requests)
18
+ * 3. Maintains locale state using AsyncLocalStorage to prevent request interference
19
+ *
20
+ * When URL strategy is used:
21
+ *
22
+ * - The locale is extracted from the URL for all request types
23
+ * - If URL doesn't match the determined locale, redirects to localized URL (only for document requests)
24
+ * - De-localizes URLs before passing to server (e.g., `/fr/about` → `/about`)
25
+ *
26
+ * @see https://paraglidejs.com/middleware
27
+ *
28
+ * @template T - The return type of the resolve function
29
+ *
30
+ * @param {Request} request - The incoming request object
31
+ * @param {(args: { request: Request, locale: import("./runtime.js").Locale }) => T | Promise<T>} resolve - Function to handle the request. The callback receives:
32
+ * - `request`: A modified request with a delocalized URL when the URL strategy is used (e.g., `/fr/about` → `/about`).
33
+ * If your framework handles URL localization itself (e.g., TanStack Router's `rewrite` option), use the original
34
+ * request instead to avoid redirect loops.
35
+ * - `locale`: The determined locale for this request.
36
+ * @param {{
37
+ * effectiveRequestUrl?: string | URL | ((request: Request) => string | URL),
38
+ * onRedirect?: (response: Response) => void
39
+ * }} [options] - Options to control middleware behavior. `effectiveRequestUrl` sets the effective request URL used for route matching, URL-based locale detection, redirects, and `getUrlOrigin()`.
40
+ * @returns {Promise<Response>}
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // Basic usage in metaframeworks like NextJS, SvelteKit, Astro, Nuxt, etc.
45
+ * export const handle = async ({ event, resolve }) => {
46
+ * return paraglideMiddleware(event.request, ({ request, locale }) => {
47
+ * // let the framework further resolve the request
48
+ * return resolve(request);
49
+ * });
50
+ * };
51
+ * ```
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // Usage in a framework like Express JS or Hono
56
+ * app.use(async (req, res, next) => {
57
+ * const result = await paraglideMiddleware(req, ({ request, locale }) => {
58
+ * // If a redirect happens this won't be called
59
+ * return next(request);
60
+ * });
61
+ * });
62
+ * ```
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Usage with frameworks that handle URL localization/delocalization themselves
67
+ * //
68
+ * // Some frameworks like TanStack Router handle URL localization and delocalization
69
+ * // themselves via their own rewrite APIs (e.g., `rewrite.input`/`rewrite.output`).
70
+ * //
71
+ * // When the framework handles this, the middleware's URL delocalization is not needed.
72
+ * // Using the modified `request` from the callback would cause a redirect loop because
73
+ * // both the middleware and the framework would attempt to delocalize the URL.
74
+ * //
75
+ * // Solution: Pass the original request to the handler instead of the modified one.
76
+ * // The middleware still handles locale detection, cookies, and AsyncLocalStorage context.
77
+ * //
78
+ * // ❌ WRONG - causes redirect loop when framework handles URL rewriting:
79
+ * // paraglideMiddleware(req, ({ request }) => handler.fetch(request))
80
+ * //
81
+ * // ✅ CORRECT - use original request when framework handles URL localization:
82
+ * // paraglideMiddleware(req, () => handler.fetch(req))
83
+ *
84
+ * * *
85
+ * export default {
86
+ * fetch(req: Request): Promise<Response> {
87
+ * // TanStack Router handles URL rewriting via deLocalizeUrl/localizeUrl
88
+ * // so we pass the original `req` instead of the modified `request`
89
+ * return paraglideMiddleware(req, () => handler.fetch(req))
90
+ * },
91
+ * }
92
+ * ```
93
+ */
94
+ export async function paraglideMiddleware(request, resolve, options) {
95
+ let requestAsyncLocalStorage = runtime.serverAsyncLocalStorage;
96
+ requestAsyncLocalStorage = runtime.getServerAsyncLocalStorage();
97
+ if (!runtime.disableAsyncLocalStorage && !requestAsyncLocalStorage) {
98
+ const { AsyncLocalStorage } = await import("async_hooks");
99
+ requestAsyncLocalStorage = runtime.getServerAsyncLocalStorage();
100
+ if (!requestAsyncLocalStorage) {
101
+ requestAsyncLocalStorage = new AsyncLocalStorage();
102
+ runtime.overwriteServerAsyncLocalStorage(requestAsyncLocalStorage);
103
+ }
104
+ }
105
+ if (!requestAsyncLocalStorage) {
106
+ requestAsyncLocalStorage = createMockAsyncLocalStorage();
107
+ runtime.overwriteServerAsyncLocalStorage(requestAsyncLocalStorage);
108
+ }
109
+ const url = resolveMiddlewareUrl(request, options?.effectiveRequestUrl);
110
+ const origin = url.origin;
111
+ if (runtime.isExcludedByRouteStrategy(url.href)) {
112
+ const locale = runtime.baseLocale;
113
+ const newRequest = cloneRequestWithFallback(request, url);
114
+ /** @type {Set<string>} */
115
+ const messageCalls = new Set();
116
+ return /** @type {Response} */ (await requestAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest })));
117
+ }
118
+ const strategy = runtime.getStrategyForUrl(url.href);
119
+ const decision = await runtime.shouldRedirect({
120
+ request,
121
+ effectiveRequestUrl: url,
122
+ });
123
+ const locale = decision.locale;
124
+ // if the client makes a request to a URL that doesn't match
125
+ // the localizedUrl, redirect the client to the localized URL
126
+ if (request.headers.get("Sec-Fetch-Dest") === "document" &&
127
+ decision.shouldRedirect &&
128
+ decision.redirectUrl) {
129
+ // Create headers object with Vary header if preferredLanguage strategy is used
130
+ /** @type {Record<string, string>} */
131
+ const headers = {};
132
+ if (strategy.includes("preferredLanguage")) {
133
+ headers["Vary"] = "Accept-Language";
134
+ }
135
+ const response = new Response(null, {
136
+ status: 307,
137
+ headers: {
138
+ Location: decision.redirectUrl.href,
139
+ ...headers,
140
+ },
141
+ });
142
+ options?.onRedirect?.(response);
143
+ return response;
144
+ }
145
+ // If the strategy includes "url", we need to de-localize the URL
146
+ // before passing it to the server middleware.
147
+ //
148
+ // The middleware is responsible for mapping a localized URL to the
149
+ // de-localized URL e.g. `/en/about` to `/about`. Otherwise,
150
+ // the server can't render the correct page.
151
+ let newRequest;
152
+ if (strategy.includes("url")) {
153
+ newRequest = cloneRequestWithFallback(request, runtime.deLocalizeUrl(url));
154
+ }
155
+ else {
156
+ newRequest = cloneRequestWithFallback(request, url);
157
+ }
158
+ // the message functions that have been called in this request
159
+ /** @type {Set<string>} */
160
+ const messageCalls = new Set();
161
+ const response = await requestAsyncLocalStorage?.run({ locale, origin, messageCalls }, () => resolve({ locale, request: newRequest }));
162
+ // Only modify HTML responses
163
+ if (runtime.experimentalMiddlewareLocaleSplitting &&
164
+ response.headers.get("Content-Type")?.includes("html")) {
165
+ const body = await response.text();
166
+ const messages = [];
167
+ // using .values() to avoid polyfilling in older projects. else the following error is thrown
168
+ // Type 'Set<string>' can only be iterated through when using the '--downlevelIteration' flag or with a '--target' of 'es2015' or higher.
169
+ for (const messageCall of Array.from(messageCalls)) {
170
+ const [id, locale] =
171
+ /** @type {[string, import("./runtime.js").Locale]} */ (messageCall.split(":"));
172
+ messages.push(`${id}: ${compiledBundles[id]?.[locale]}`);
173
+ }
174
+ // Prevent translated content from terminating the inline script tag.
175
+ const escapedMessages = messages
176
+ .join(",")
177
+ .replace(/<\/(script)/gi, "<\\/$1");
178
+ // Reuse the request's CSP nonce (if any) so the injected script is allowed under a strict CSP
179
+ const nonce = response.headers
180
+ .get("Content-Security-Policy")
181
+ ?.match(/'nonce-([\w+/=-]+)'/)?.[1];
182
+ const nonceAttr = nonce ? `nonce="${nonce}"` : "";
183
+ const script = `<script ${nonceAttr}>globalThis.__paraglide = globalThis.__paraglide ?? {}; globalThis.__paraglide.ssr = { ${escapedMessages} }</script>`;
184
+ // Insert the script before the closing head tag
185
+ const newBody = body.replace("</head>", `${script}</head>`);
186
+ // Create a new response with the modified body
187
+ // Clone all headers except Content-Length which will be set automatically
188
+ const newHeaders = new Headers(response.headers);
189
+ newHeaders.delete("Content-Length"); // Let the browser calculate the correct length
190
+ return new Response(newBody, {
191
+ status: response.status,
192
+ statusText: response.statusText,
193
+ headers: newHeaders,
194
+ });
195
+ }
196
+ return response;
197
+ }
198
+ /**
199
+ * @param {Request} request
200
+ * @param {string | URL | ((request: Request) => string | URL) | undefined} effectiveRequestUrl
201
+ * @returns {URL}
202
+ */
203
+ function resolveMiddlewareUrl(request, effectiveRequestUrl) {
204
+ if (typeof effectiveRequestUrl === "function") {
205
+ return new URL(effectiveRequestUrl(request), request.url);
206
+ }
207
+ if (typeof effectiveRequestUrl === "string" ||
208
+ effectiveRequestUrl instanceof URL) {
209
+ return new URL(effectiveRequestUrl, request.url);
210
+ }
211
+ return new URL(request.url);
212
+ }
213
+ /**
214
+ * Some metaframeworks (NextJS) require a new Request object.
215
+ * https://github.com/opral/inlang-paraglide-js/issues/411
216
+ *
217
+ * However, some frameworks (TanStack Start 1.143+) use custom Request
218
+ * implementations that cannot be cloned with `new Request(request)`.
219
+ * https://github.com/opral/paraglide-js/issues/573
220
+ *
221
+ * Effective request URL overrides behind proxies:
222
+ * https://github.com/opral/paraglide-js/issues/652
223
+ *
224
+ * @param {Request} request
225
+ * @param {string | URL} [url]
226
+ * @returns {Request}
227
+ */
228
+ function cloneRequestWithFallback(request, url = request.url) {
229
+ const targetUrl = typeof url === "string" ? url : url.href;
230
+ if (targetUrl === request.url) {
231
+ try {
232
+ // Clone first so building a new Request does not consume the original body stream.
233
+ return new Request(request.clone());
234
+ }
235
+ catch {
236
+ try {
237
+ return new Request(request);
238
+ }
239
+ catch {
240
+ return request;
241
+ }
242
+ }
243
+ }
244
+ try {
245
+ // Clone first so building a new Request does not consume the original body stream.
246
+ return new Request(targetUrl, request.clone());
247
+ }
248
+ catch {
249
+ try {
250
+ return new Request(targetUrl, request);
251
+ }
252
+ catch {
253
+ return request;
254
+ }
255
+ }
256
+ }
257
+ /**
258
+ * Creates a mock AsyncLocalStorage implementation for environments where
259
+ * native AsyncLocalStorage is not available or disabled.
260
+ *
261
+ * This mock implementation mimics the behavior of the native AsyncLocalStorage
262
+ * but doesn't require the async_hooks module. It's used as a fallback when
263
+ * the runtime does not expose AsyncLocalStorage or when it has been disabled.
264
+ *
265
+ * @returns {import("./runtime.js").ParaglideAsyncLocalStorage}
266
+ */
267
+ function createMockAsyncLocalStorage() {
268
+ /** @type {any} */
269
+ let currentStore = undefined;
270
+ return {
271
+ getStore() {
272
+ return currentStore;
273
+ },
274
+ async run(store, callback) {
275
+ currentStore = store;
276
+ try {
277
+ return await callback();
278
+ }
279
+ finally {
280
+ currentStore = undefined;
281
+ }
282
+ },
283
+ };
284
+ }
285
+ // Used in generated server.js when async local storage is disabled.
286
+ void createMockAsyncLocalStorage;
287
+ /**
288
+ * The compiled messages for the server middleware.
289
+ *
290
+ * Only populated if `enableMiddlewareOptimizations` is set to `true`.
291
+ *
292
+ * @type {Record<string, Record<import("./runtime.js").Locale, string>>}
293
+ */
294
+ const compiledBundles = {};
@@ -17,8 +17,8 @@
17
17
 
18
18
  import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
19
19
  import { useState } from "react";
20
- import type { BugDetail } from "@/components/bug/types";
21
- import type { UserBrief } from "@/components/story/types";
20
+ import type { BugDetail } from "@/shared/bug/types";
21
+ import type { UserBrief } from "@/shared/story/types";
22
22
  import { BugDetailBody } from "@/components/bug/detail/bug-detail-body";
23
23
  import { BugPanelHeader } from "@/components/bug/bug-panel-header";
24
24
 
@@ -32,7 +32,7 @@ import { listProjectsBriefFn } from "@/server/serverFns/project";
32
32
  import { toast } from "@/components/ui/toast";
33
33
  import { useQuery } from "@tanstack/react-query";
34
34
  import type { IterationSummary } from "@/server/serverFns/iteration/schemas";
35
- import type { StoryState } from "@/components/story/constants";
35
+ import type { StoryState } from "@/shared/story/constants";
36
36
  import type { AvatarConfig } from "@allbluecn/kernel";
37
37
  import { m } from "@/paraglide/messages.js";
38
38
  import { toastError } from "@/lib/toast-error";
@@ -23,8 +23,7 @@ import { listPinnedWorkspacesBriefFn } from '@/server/serverFns/workspace'
23
23
  import { listKnowledgeFn } from '@/server/serverFns/knowledge'
24
24
  import { ensureVerifyReminderFn } from '@/server/serverFns/auth/verify-reminder'
25
25
  import { getSidebarStateFn } from '@/server/serverFns/sidebar-state'
26
- import { WORKSPACES_QUERY_KEY } from '@/lib/collections/reference/workspaces-collection'
27
- import { KNOWLEDGE_BASES_QUERY_KEY } from '@/lib/collections/knowledge/knowledge-bases-collection'
26
+ import { WORKSPACES_QUERY_KEY, KNOWLEDGE_BASES_QUERY_KEY } from '@/lib/collections/query-keys'
28
27
  import { GlobalErrorBoundary } from '@/components/error/global-error-boundary'
29
28
  import { NavNotFound } from './-components/nav-not-found'
30
29
 
@@ -17,9 +17,9 @@
17
17
 
18
18
  import { createLazyFileRoute, useNavigate, useRouter } from "@tanstack/react-router";
19
19
  import { useCallback, useState, type UIEvent } from "react";
20
- import type { StoryDetail, UserBrief, CommentWithReplies, ActivityItem, RelationsData } from "@/components/story/types";
20
+ import type { StoryDetail, UserBrief, CommentWithReplies, ActivityItem, RelationsData } from "@/shared/story/types";
21
21
  import type { IterationSummary } from "@/server/serverFns/iteration/schemas";
22
- import type { StoryState } from "@/components/story/constants";
22
+ import type { StoryState } from "@/shared/story/constants";
23
23
  import { StoryDetailBody } from "@/components/story/detail/story-detail-body";
24
24
  import { StoryPanelHeader } from "@/components/story/story-panel-header";
25
25
 
@@ -35,7 +35,7 @@ import { listProjectsBriefFn } from "@/server/serverFns/project";
35
35
  import { listIterationsFn } from "@/server/serverFns/iteration";
36
36
  import { getCollabSnapshotFn } from "@/server/serverFns/collab-snapshot";
37
37
  import { isCollabEnabled } from "@/lib/collab/env";
38
- import type { RelationsData } from "@/components/story/types";
38
+ import type { RelationsData } from "@/shared/story/types";
39
39
  import { StoryDetailSkeleton } from "../-components/story-detail-skeleton";
40
40
 
41
41
  // 「关联需求」选择源拉取上限:超过该数的需求会被静默截断(组件端无搜索补拉),
@@ -450,6 +450,9 @@ describe('/api/design/prototype 流式生成', () => {
450
450
  expect(eventsOf(events, 'canvas/nodes')).toHaveLength(1)
451
451
  const dones = eventsOf(events, 'canvas/done')
452
452
  expect(dones).toHaveLength(1)
453
- expect((dones[0].value as { message: string }).message.length).toBeGreaterThan(0)
453
+ // 文案协议化(2026-10-28):服务端只回 status,完成文案由客户端 m.canvas_ai_doneFallback()
454
+ // 渲染;无 LLM 总结时 message 缺省(不再由服务端塞中文)
455
+ expect((dones[0].value as { status: string }).status).toBe('completed')
456
+ expect((dones[0].value as { message?: string }).message).toBeUndefined()
454
457
  })
455
458
  })
@@ -20,6 +20,10 @@ vi.mock('@allbluecn/database', () => ({
20
20
  testCase: { findUnique: vi.fn() },
21
21
  },
22
22
  }))
23
+ // 源条目属主校验(2026-10-28 审计 A4x-1):测试默认按「是项目成员」放行,
24
+ // 需要验证拒绝路径时在用例内改 mock
25
+ vi.mock('@/server/lib/project-membership', () => ({ isProjectMember: vi.fn(async () => true) }))
26
+
23
27
  vi.mock('@/server/ai-task/context-builder', async (importOriginal) => {
24
28
  const actual = await importOriginal<typeof import('@/server/ai-task/context-builder')>()
25
29
  return { ...actual, buildSourceContextBlock: vi.fn(() => '## 需求上下文(mock)') }
@@ -257,6 +261,7 @@ describe('executeAiTaskRun', () => {
257
261
 
258
262
  it('源型 story:查源表并注入 buildSourceContextBlock 输出到 systemPrompts', async () => {
259
263
  storyFindUnique.mockResolvedValue({
264
+ projectId: 'p1',
260
265
  identifier: 'ALB-S12',
261
266
  name: '登录页重构',
262
267
  description: null,
@@ -272,6 +277,7 @@ describe('executeAiTaskRun', () => {
272
277
  expect(storyFindUnique).toHaveBeenCalledWith({
273
278
  where: { id: 'story-1' },
274
279
  select: expect.objectContaining({
280
+ projectId: true,
275
281
  identifier: true,
276
282
  name: true,
277
283
  description: true,
@@ -308,6 +314,7 @@ describe('executeAiTaskRun', () => {
308
314
 
309
315
  it('源型分发:bug / testcase 各查对应表', async () => {
310
316
  bugFindUnique.mockResolvedValue({
317
+ projectId: 'p1',
311
318
  identifier: 'ALB-B3',
312
319
  title: '首页白屏',
313
320
  severity: 'critical',
@@ -0,0 +1,169 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // AllBlue - 理想之海
3
+ // Copyright (C) 2026 AllBlue Contributors
4
+ //
5
+ // This program is free software: you can redistribute it and/or modify
6
+ // it under the terms of the GNU Affero General Public License as published by
7
+ // the Free Software Foundation, either version 3 of the License, or
8
+ // (at your option) any later version.
9
+ //
10
+ // This program is distributed in the hope that it will be useful,
11
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ // GNU Affero General Public License for more details.
14
+ //
15
+ // You should have received a copy of the GNU Affero General Public License
16
+ // along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+
18
+ // 审计 S7(2026-10-28)LegalRegulation.code 持久化回归:
19
+ // - update:canonicalId 仅在显式传 code 时重算(compliance 同款裁定),
20
+ // 仅改 title 一律不漂移(有 code 行身份=code;无 code 存量行保持创建时 key);
21
+ // - code 落库 + 空串归一 null;
22
+ // - create:code 落库 + canonicalId = code || title。
23
+
24
+ import { describe, it, expect, vi, beforeEach } from 'vitest'
25
+
26
+ vi.mock('@tanstack/react-start', () => ({
27
+ createServerFn: (_config: unknown) => {
28
+ const api = {
29
+ _validator: undefined as unknown,
30
+ validator(schema: unknown) {
31
+ this._validator = schema
32
+ return this
33
+ },
34
+ handler(fn: (args: { data: unknown }) => Promise<unknown>) {
35
+ const wrapped = async (args: unknown) => {
36
+ let data = (args as { data?: unknown } | null | undefined)?.data
37
+ if (this._validator && data !== undefined) {
38
+ const v = this._validator as { parse?: (d: unknown) => unknown }
39
+ data = v.parse ? v.parse(data) : data
40
+ }
41
+ return fn({ data })
42
+ }
43
+ return wrapped
44
+ },
45
+ }
46
+ return api
47
+ },
48
+ }))
49
+
50
+ vi.mock('@tanstack/react-start/server', () => ({
51
+ getRequest: vi.fn(),
52
+ }))
53
+
54
+ const m = vi.hoisted(() => ({
55
+ legalRegulation: {
56
+ findUnique: vi.fn(),
57
+ create: vi.fn(),
58
+ update: vi.fn(),
59
+ },
60
+ }))
61
+
62
+ vi.mock('@allbluecn/database/get-prisma', () => ({ getPrisma: async () => m }))
63
+ vi.mock('@allbluecn/database', () => ({ Prisma: {} }))
64
+
65
+ vi.mock('@/server/auth-helpers', () => ({ requireAuth: vi.fn() }))
66
+
67
+ import { requireAuth } from '@/server/auth-helpers'
68
+ import * as updateMod from '../knowledge/public/legal/update'
69
+ import * as createMod from '../knowledge/public/legal/create'
70
+
71
+ const mockRequireAuth = vi.mocked(requireAuth)
72
+
73
+ describe('updateLegalFn:code 持久化 + canonicalId 重算口径(S7)', () => {
74
+ beforeEach(() => {
75
+ vi.clearAllMocks()
76
+ mockRequireAuth.mockResolvedValue({ user: { id: 'u1', role: 'USER' } } as never)
77
+ m.legalRegulation.update.mockResolvedValue({ id: 'row-1' })
78
+ })
79
+
80
+ it('仅改 title(有 code 行)→ canonicalId 保持 code 维度不漂移,code 不重写', async () => {
81
+ m.legalRegulation.findUnique.mockResolvedValue({
82
+ id: 'row-1', userId: 'u1', title: '旧标题', code: 'GB 4943', canonicalId: 'GB 4943',
83
+ })
84
+ await updateMod.updateLegalFn({ data: { id: 'row-1', title: '新标题' } })
85
+ expect(m.legalRegulation.update).toHaveBeenCalledWith(
86
+ expect.objectContaining({
87
+ data: expect.not.objectContaining({ canonicalId: expect.anything(), code: expect.anything() }),
88
+ }),
89
+ )
90
+ })
91
+
92
+ it('仅改 title(无 code 存量行)→ canonicalId 保持创建时 key 不失联', async () => {
93
+ m.legalRegulation.findUnique.mockResolvedValue({
94
+ id: 'row-1', userId: 'u1', title: '旧标题', code: null, canonicalId: '旧标题',
95
+ })
96
+ await updateMod.updateLegalFn({ data: { id: 'row-1', title: '新标题' } })
97
+ expect(m.legalRegulation.update).toHaveBeenCalledWith(
98
+ expect.objectContaining({
99
+ data: expect.not.objectContaining({ canonicalId: expect.anything() }),
100
+ }),
101
+ )
102
+ })
103
+
104
+ it('显式传 code → code 落库且 canonicalId 以 code 重算', async () => {
105
+ m.legalRegulation.findUnique.mockResolvedValue({
106
+ id: 'row-1', userId: 'u1', title: '旧标题', code: null, canonicalId: '旧标题',
107
+ })
108
+ await updateMod.updateLegalFn({ data: { id: 'row-1', code: 'GB 4943' } })
109
+ expect(m.legalRegulation.update).toHaveBeenCalledWith(
110
+ expect.objectContaining({
111
+ data: expect.objectContaining({ code: 'GB 4943', canonicalId: 'GB 4943' }),
112
+ }),
113
+ )
114
+ })
115
+
116
+ it('code 传空串 → 归一为 null 落库,canonicalId 回落 title 维度', async () => {
117
+ m.legalRegulation.findUnique.mockResolvedValue({
118
+ id: 'row-1', userId: 'u1', title: '旧标题', code: 'GB 4943', canonicalId: 'GB 4943',
119
+ })
120
+ await updateMod.updateLegalFn({ data: { id: 'row-1', code: '' } })
121
+ expect(m.legalRegulation.update).toHaveBeenCalledWith(
122
+ expect.objectContaining({
123
+ data: expect.objectContaining({ code: null, canonicalId: '旧标题' }),
124
+ }),
125
+ )
126
+ })
127
+
128
+ it('code + title 同传 → code 维度优先', async () => {
129
+ m.legalRegulation.findUnique.mockResolvedValue({
130
+ id: 'row-1', userId: 'u1', title: '旧标题', code: null, canonicalId: '旧标题',
131
+ })
132
+ await updateMod.updateLegalFn({ data: { id: 'row-1', title: '新标题', code: 'GB 4943-2024' } })
133
+ expect(m.legalRegulation.update).toHaveBeenCalledWith(
134
+ expect.objectContaining({
135
+ data: expect.objectContaining({ code: 'GB 4943-2024', canonicalId: 'GB 4943-2024' }),
136
+ }),
137
+ )
138
+ })
139
+ })
140
+
141
+ describe('createLegalFn:code 落库(S7)', () => {
142
+ beforeEach(() => {
143
+ vi.clearAllMocks()
144
+ mockRequireAuth.mockResolvedValue({ user: { id: 'u1', role: 'USER' } } as never)
145
+ m.legalRegulation.create.mockResolvedValue({ id: 'new-1' })
146
+ })
147
+
148
+ it('传 code → 落库且 canonicalId = code', async () => {
149
+ await createMod.createLegalFn({
150
+ data: { title: '某标准', code: 'GB 4943', jurisdiction: 'CN_NATIONAL', category: '安全', tags: [] },
151
+ })
152
+ expect(m.legalRegulation.create).toHaveBeenCalledWith(
153
+ expect.objectContaining({
154
+ data: expect.objectContaining({ code: 'GB 4943', canonicalId: 'GB 4943' }),
155
+ }),
156
+ )
157
+ })
158
+
159
+ it('不传 code → code 落 null,canonicalId = title', async () => {
160
+ await createMod.createLegalFn({
161
+ data: { title: '某办法', jurisdiction: 'CN_LOCAL', category: '管理', tags: [] },
162
+ })
163
+ expect(m.legalRegulation.create).toHaveBeenCalledWith(
164
+ expect.objectContaining({
165
+ data: expect.objectContaining({ code: null, canonicalId: '某办法' }),
166
+ }),
167
+ )
168
+ })
169
+ })
@@ -82,7 +82,6 @@ import {
82
82
  addChildFn,
83
83
  removeChildFn,
84
84
  } from "../story/story-tree";
85
- import { STORY_LIST_SELECT, castStoryUsers } from "../../story/types";
86
85
 
87
86
  const USER = "u-current";
88
87
  const PARENT = "st-parent";
@@ -92,7 +92,7 @@ vi.mock("@/server/collab/write-guard", () => ({
92
92
  }));
93
93
 
94
94
  import { updateBugFn } from "../bug/bug-crud";
95
- import { BUG_LIST_SELECT } from "@/components/bug/types";
95
+ import { BUG_LIST_SELECT } from "@/shared/bug/types";
96
96
 
97
97
  const U = "u-1";
98
98
  const P = "pr-1";
@@ -93,7 +93,7 @@ import {
93
93
  listCommentsFn,
94
94
  updateCommentFn,
95
95
  } from "../bug-comments";
96
- import { BUG_USER_BRIEF_SELECT } from "@/components/bug/types";
96
+ import { BUG_USER_BRIEF_SELECT } from "@/shared/bug/types";
97
97
 
98
98
  const USER = "u-current";
99
99
  const OTHER = "u-other";
@@ -108,7 +108,7 @@ import {
108
108
  DEFAULT_BUG_DISPLAY_OPTIONS,
109
109
  } from "@/shared/bug/display-types";
110
110
  import { BUG_STATES } from "@/shared/bug/constants";
111
- import { BUG_LIST_SELECT } from "@/components/bug/types";
111
+ import { BUG_LIST_SELECT } from "@/shared/bug/types";
112
112
  import type { BugDisplayOptions } from "@/shared/bug/display-types";
113
113
 
114
114
  const DATE = new Date("2026-01-01T00:00:00Z");
@@ -21,8 +21,8 @@ import { getRequest } from "@tanstack/react-start/server";
21
21
  import { z } from "zod";
22
22
  import { requireAuth } from "@/server/auth-helpers";
23
23
  import type { AvatarConfig } from "@allbluecn/kernel";
24
- import { BUG_USER_BRIEF_SELECT } from "@/components/bug/types";
25
- import type { BugActivityItem } from "@/components/bug/types";
24
+ import { BUG_USER_BRIEF_SELECT } from "@/shared/bug/types";
25
+ import type { BugActivityItem } from "@/shared/bug/types";
26
26
  import { assertBugProjectMember } from "@/server/bug/bug-permissions";
27
27
 
28
28
  // BugActivity.userId 可空(SDK/agent 产生),user 关系 onDelete: SetNull
@@ -21,9 +21,9 @@ import { getRequest } from "@tanstack/react-start/server";
21
21
  import { z } from "zod";
22
22
  import { requireAuth } from "@/server/auth-helpers";
23
23
  import type { AvatarConfig } from "@allbluecn/kernel";
24
- import { BUG_USER_BRIEF_SELECT } from "@/components/bug/types";
25
- import type { BugCommentWithReplies } from "@/components/bug/types";
26
- import type { ReactionGroup } from "@/components/story/types";
24
+ import { BUG_USER_BRIEF_SELECT } from "@/shared/bug/types";
25
+ import type { BugCommentWithReplies } from "@/shared/bug/types";
26
+ import type { ReactionGroup } from "@/shared/story/types";
27
27
  import { assertBugProjectMember } from "@/server/bug/bug-permissions";
28
28
  import { aggregateReactions } from "@/server/story/types";
29
29
 
@@ -21,7 +21,7 @@
21
21
  // 任务 2 裁定:详情扩展字段在 serverFns/bug 侧扩展,不改 components/bug/types.ts
22
22
  // 的 BUG_LIST_SELECT——列表查询不多拉大字段。
23
23
  import type { Prisma } from "@allbluecn/database";
24
- import { BUG_LIST_SELECT } from "@/components/bug/types";
24
+ import { BUG_LIST_SELECT } from "@/shared/bug/types";
25
25
 
26
26
  export const BUG_DETAIL_SELECT = {
27
27
  ...BUG_LIST_SELECT,
@@ -20,8 +20,8 @@ import { createServerFn } from "@tanstack/react-start";
20
20
  import { getRequest } from "@tanstack/react-start/server";
21
21
  import { z } from "zod";
22
22
  import { requireAuth } from "@/server/auth-helpers";
23
- import { castBugUsers } from "@/components/bug/types";
24
- import type { BugDetail, ReproduceSteps } from "@/components/bug/types";
23
+ import { castBugUsers } from "@/shared/bug/types";
24
+ import type { BugDetail, ReproduceSteps } from "@/shared/bug/types";
25
25
  import { assertBugProjectMember } from "@/server/bug/bug-permissions";
26
26
  import { BUG_DETAIL_SELECT, extractScreenshotBlobId } from "./detail-select";
27
27