@lobehub/edge-contract 0.0.0-stage → 0.2.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.
@@ -0,0 +1,269 @@
1
+ // Key = topic slug (the `q` query param), value = display name for the SEO page and Hero.
2
+ // Landing server-renders `q` only for these topics; the gateway drops any other `q` from the
3
+ // cache key. A topic missing here loses its server-rendered page.
4
+ export const SEO_TOPICS = {
5
+ agent: {
6
+ 'aliyun': 'Aliyun',
7
+ 'baichuan': 'Baichuan',
8
+ 'baidu': 'Baidu',
9
+ 'bilibili': 'Bilibili',
10
+ 'chatgpt': 'ChatGPT',
11
+ 'claude': 'Claude',
12
+ 'claude-code': 'Claude Code',
13
+ 'clawdbot': 'ClawdBot',
14
+ 'coding': 'Coding',
15
+ 'codx': 'Codx',
16
+ 'crewai': 'CrewAI',
17
+ 'cursor': 'Cursor',
18
+ 'deepseek': 'DeepSeek',
19
+ 'design': 'Design',
20
+ 'dingtalk': 'DingTalk',
21
+ 'discord': 'Discord',
22
+ 'doubao': 'Doubao',
23
+ 'douyin': 'Douyin',
24
+ 'education': 'Education',
25
+ 'excel': 'Microsoft Excel',
26
+ 'feishu': 'Feishu',
27
+ 'gemini': 'Gemini',
28
+ 'github': 'GitHub',
29
+ 'hunyuan': 'Hunyuan',
30
+ 'image': 'Image',
31
+ 'jd': 'JD.com',
32
+ 'kimi': 'Kimi',
33
+ 'kuaishou': 'Kuaishou',
34
+ 'langchain': 'LangChain',
35
+ 'line': 'Line',
36
+ 'manus': 'Manus',
37
+ 'marketing': 'Marketing',
38
+ 'memory': 'Memory',
39
+ 'moltbot': 'MoltBot',
40
+ 'notion': 'Notion',
41
+ 'office': 'Office',
42
+ 'ollama': 'Ollama',
43
+ 'openai': 'OpenAI',
44
+ 'openclaw': 'OpenClaw',
45
+ 'pdf': 'PDF',
46
+ 'pinduoduo': 'Pinduoduo',
47
+ 'programming': 'Programming',
48
+ 'qwen': 'Qwen',
49
+ 'rag': 'RAG',
50
+ 'reddit': 'Reddit',
51
+ 'research': 'Research',
52
+ 'slack': 'Slack',
53
+ 'taobao': 'Taobao',
54
+ 'telegram': 'Telegram',
55
+ 'tencent': 'Tencent',
56
+ 'tongyi': 'Tongyi',
57
+ 'toutiao': 'Toutiao',
58
+ 'translation': 'Translation',
59
+ 'vscode': 'VS Code',
60
+ 'wechat': 'WeChat',
61
+ 'wecom': 'WeCom',
62
+ 'weibo': 'Weibo',
63
+ 'wenxin': 'Wenxin',
64
+ 'writing': 'Writing',
65
+ 'xiaohongshu': 'Xiaohongshu',
66
+ 'youtube': 'YouTube',
67
+ 'zhipu': 'Zhipu',
68
+ '京东': '京东',
69
+ '今日头条': '今日头条',
70
+ '企业微信': '企业微信',
71
+ '公众号': '公众号',
72
+ '写作': '写作',
73
+ '哔哩哔哩': '哔哩哔哩',
74
+ '小红书': '小红书',
75
+ '微信': '微信',
76
+ '微博': '微博',
77
+ '快手': '快手',
78
+ '抖音': '抖音',
79
+ '拼多多': '拼多多',
80
+ '推特': '推特',
81
+ '淘宝': '淘宝',
82
+ '百度': '百度',
83
+ '营销': '营销',
84
+ '论文': '论文',
85
+ '豆包': '豆包',
86
+ '运营': '运营',
87
+ '钉钉': '钉钉',
88
+ '阿里云': '阿里云',
89
+ '飞书': '飞书',
90
+ },
91
+ mcp: {
92
+ 'aliyun': 'Aliyun',
93
+ 'aws': 'AWS',
94
+ 'azure': 'Azure',
95
+ 'baidu': 'Baidu',
96
+ 'bilibili': 'Bilibili',
97
+ 'bitbucket': 'Bitbucket',
98
+ 'blender': 'Blender',
99
+ 'brave': 'Brave',
100
+ 'chrome': 'Chrome',
101
+ 'confluence': 'Confluence',
102
+ 'deepseek': 'DeepSeek',
103
+ 'dingtalk': 'DingTalk',
104
+ 'discord': 'Discord',
105
+ 'excel': 'Microsoft Excel',
106
+ 'feishu': 'Feishu',
107
+ 'fhir': 'FHIR',
108
+ 'figma': 'Figma',
109
+ 'firebase': 'Firebase',
110
+ 'firecrawl': 'Firecrawl',
111
+ 'gemini': 'Gemini',
112
+ 'git': 'Git',
113
+ 'github': 'GitHub',
114
+ 'gitlab': 'GitLab',
115
+ 'gmail': 'Gmail',
116
+ 'godot': 'Godot',
117
+ 'google': 'Google',
118
+ 'google-calendar': 'Google Calendar',
119
+ 'grafana': 'Grafana',
120
+ 'home-assistant': 'Home Assistant',
121
+ 'huawei': 'Huawei',
122
+ 'image': 'Image',
123
+ 'jd': 'JD.com',
124
+ 'jetbrains': 'JetBrains',
125
+ 'jira': 'Jira',
126
+ 'kuaishou': 'Kuaishou',
127
+ 'kubernetes': 'Kubernetes',
128
+ 'line': 'Line',
129
+ 'linkedin': 'LinkedIn',
130
+ 'memory': 'Memory',
131
+ 'mongo': 'MongoDB',
132
+ 'mysql': 'MySQL',
133
+ 'neo4j': 'Neo4j',
134
+ 'notion': 'Notion',
135
+ 'obsidian': 'Obsidian',
136
+ 'ollama': 'Ollama',
137
+ 'openapi': 'OpenAPI',
138
+ 'outlook': 'Outlook',
139
+ 'pdf': 'PDF',
140
+ 'perplexity': 'Perplexity',
141
+ 'playwright': 'Playwright',
142
+ 'postgres': 'PostgreSQL',
143
+ 'python': 'Python',
144
+ 'qdrant': 'Qdrant',
145
+ 'rag': 'RAG',
146
+ 'reddit': 'Reddit',
147
+ 'salesforce': 'Salesforce',
148
+ 'sheets': 'Google Sheets',
149
+ 'shopify': 'Shopify',
150
+ 'slack': 'Slack',
151
+ 'spotify': 'Spotify',
152
+ 'sql': 'SQL',
153
+ 'sqlite': 'SQLite',
154
+ 'steam': 'Steam',
155
+ 'stripe': 'Stripe',
156
+ 'supabase': 'Supabase',
157
+ 'taobao': 'Taobao',
158
+ 'tavily': 'Tavily',
159
+ 'telegram': 'Telegram',
160
+ 'tencent': 'Tencent',
161
+ 'unity': 'Unity',
162
+ 'unity-editor': 'Unity Editor',
163
+ 'vercel': 'Vercel',
164
+ 'wechat': 'WeChat',
165
+ 'wecom': 'WeCom',
166
+ 'whatsapp': 'WhatsApp',
167
+ 'word': 'Microsoft Word',
168
+ 'x-twitter': 'X (Twitter)',
169
+ 'youtube': 'YouTube',
170
+ 'zapier': 'Zapier',
171
+ 'zhihu': 'Zhihu',
172
+ '京东': '京东',
173
+ '企业微信': '企业微信',
174
+ '哔哩哔哩': '哔哩哔哩',
175
+ '微信': '微信',
176
+ '快手': '快手',
177
+ '淘宝': '淘宝',
178
+ '百度': '百度',
179
+ '知乎': '知乎',
180
+ '钉钉': '钉钉',
181
+ '阿里云': '阿里云',
182
+ '飞书': '飞书',
183
+ },
184
+ skills: {
185
+ 'aliyun': 'Aliyun',
186
+ 'aws': 'AWS',
187
+ 'baidu': 'Baidu',
188
+ 'bilibili': 'Bilibili',
189
+ 'chatgpt': 'ChatGPT',
190
+ 'claude': 'Claude',
191
+ 'claude-code': 'Claude Code',
192
+ 'clawdbot': 'ClawdBot',
193
+ 'cline': 'Cline',
194
+ 'codex': 'Codex',
195
+ 'codx': 'Codx',
196
+ 'copilot': 'GitHub Copilot',
197
+ 'cursor': 'Cursor',
198
+ 'database': 'Database',
199
+ 'dingtalk': 'DingTalk',
200
+ 'discord': 'Discord',
201
+ 'docker': 'Docker',
202
+ 'excel': 'Microsoft Excel',
203
+ 'feishu': 'Feishu',
204
+ 'figma': 'Figma',
205
+ 'firebase': 'Firebase',
206
+ 'git': 'Git',
207
+ 'github': 'GitHub',
208
+ 'huawei': 'Huawei',
209
+ 'image': 'Image',
210
+ 'jd': 'JD.com',
211
+ 'kuaishou': 'Kuaishou',
212
+ 'kubernetes': 'Kubernetes',
213
+ 'line': 'Line',
214
+ 'manus': 'Manus',
215
+ 'memory': 'Memory',
216
+ 'moltbot': 'MoltBot',
217
+ 'mysql': 'MySQL',
218
+ 'notion': 'Notion',
219
+ 'ollama': 'Ollama',
220
+ 'openclaw': 'OpenClaw',
221
+ 'pdf': 'PDF',
222
+ 'perplexity': 'Perplexity',
223
+ 'pinduoduo': 'Pinduoduo',
224
+ 'playwright': 'Playwright',
225
+ 'postgres': 'PostgreSQL',
226
+ 'python': 'Python',
227
+ 'rag': 'RAG',
228
+ 'reddit': 'Reddit',
229
+ 'redis': 'Redis',
230
+ 'search': 'Search',
231
+ 'slack': 'Slack',
232
+ 'sql': 'SQL',
233
+ 'supabase': 'Supabase',
234
+ 'taobao': 'Taobao',
235
+ 'tavily': 'Tavily',
236
+ 'telegram': 'Telegram',
237
+ 'tencent': 'Tencent',
238
+ 'vscode': 'VS Code',
239
+ 'wechat': 'WeChat',
240
+ 'wecom': 'WeCom',
241
+ 'windsurf': 'Windsurf',
242
+ 'youtube': 'YouTube',
243
+ 'zhihu': 'Zhihu',
244
+ '公众号': '公众号',
245
+ '写作': '写作',
246
+ '哔哩哔哩': '哔哩哔哩',
247
+ '小红书': '小红书',
248
+ '微信': '微信',
249
+ '抖音': '抖音',
250
+ '推特': '推特',
251
+ '淘宝': '淘宝',
252
+ '百度': '百度',
253
+ '知乎': '知乎',
254
+ '营销': '营销',
255
+ '论文': '论文',
256
+ '运营': '运营',
257
+ '钉钉': '钉钉',
258
+ '阿里云': '阿里云',
259
+ '飞书': '飞书',
260
+ },
261
+ };
262
+ export const hasSeoTopic = (market, topic) => Object.hasOwn(SEO_TOPICS[market], topic);
263
+ // Free-text searches outside the sitemapped topics are never server-rendered:
264
+ // SSR renders the list without `q` and the browser fetches the results, so the
265
+ // HTML is identical to the URL without `q` and the CDN can share one cache entry
266
+ export const isClientOnlySearch = (market, q) => {
267
+ const topic = typeof q === 'string' ? q.trim() : '';
268
+ return Boolean(topic) && !hasSeoTopic(market, topic);
269
+ };
@@ -0,0 +1,257 @@
1
+ {
2
+ "attributionParams": [
3
+ "gad_source",
4
+ "mc_cid",
5
+ "gclid",
6
+ "gclsrc",
7
+ "dclid",
8
+ "gbraid",
9
+ "wbraid",
10
+ "fbclid",
11
+ "msclkid",
12
+ "twclid",
13
+ "li_fat_id",
14
+ "igshid",
15
+ "ttclid",
16
+ "rdt_cid",
17
+ "epik",
18
+ "qclid",
19
+ "sccid",
20
+ "irclid",
21
+ "_kx"
22
+ ],
23
+ "authority": "lobehub-cloud@19f89cf50 src/libs/analytics/acquisitionContext.ts and src/proxy.ts ATTRIBUTION_PARAMS",
24
+ "cases": [
25
+ {
26
+ "expected": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
27
+ "input": {
28
+ "context": {
29
+ "capturedAt": "2026-11-06T08:00:00.000Z",
30
+ "lhCid": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
31
+ },
32
+ "kind": "sign",
33
+ "secret": "fixture-secret-fixture-secret-00"
34
+ },
35
+ "name": "sign lower-case click id"
36
+ },
37
+ {
38
+ "expected": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
39
+ "input": {
40
+ "context": {
41
+ "capturedAt": "2026-11-06T08:00:00.000Z",
42
+ "lhCid": "3F2A9C1E-5B7D-4E8A-9C21-7D4E5F6A8B90"
43
+ },
44
+ "kind": "sign",
45
+ "secret": "fixture-secret-fixture-secret-00"
46
+ },
47
+ "name": "sign upper-case click id is lower-cased"
48
+ },
49
+ {
50
+ "expected": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
51
+ "input": {
52
+ "context": {
53
+ "capturedAt": "2026-11-06T08:00:00Z",
54
+ "lhCid": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
55
+ },
56
+ "kind": "sign",
57
+ "secret": "fixture-secret-fixture-secret-00"
58
+ },
59
+ "name": "sign normalizes the timestamp"
60
+ },
61
+ {
62
+ "expected": {
63
+ "capturedAt": "2026-11-06T08:00:00.000Z",
64
+ "lhCid": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
65
+ },
66
+ "input": {
67
+ "now": "2026-11-07T00:00:00.000Z",
68
+ "secret": "fixture-secret-fixture-secret-00",
69
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
70
+ "kind": "verify"
71
+ },
72
+ "name": "verify valid"
73
+ },
74
+ {
75
+ "expected": null,
76
+ "input": {
77
+ "now": "2026-11-07T00:00:00.000Z",
78
+ "secret": "another-secret",
79
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
80
+ "kind": "verify"
81
+ },
82
+ "name": "verify wrong secret"
83
+ },
84
+ {
85
+ "expected": null,
86
+ "input": {
87
+ "now": "2026-12-06T08:00:01.000Z",
88
+ "secret": "fixture-secret-fixture-secret-00",
89
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
90
+ "kind": "verify"
91
+ },
92
+ "name": "verify past 30 days"
93
+ },
94
+ {
95
+ "expected": {
96
+ "capturedAt": "2026-11-06T08:00:00.000Z",
97
+ "lhCid": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
98
+ },
99
+ "input": {
100
+ "now": "2026-12-06T08:00:00.000Z",
101
+ "secret": "fixture-secret-fixture-secret-00",
102
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
103
+ "kind": "verify"
104
+ },
105
+ "name": "verify at 30 days"
106
+ },
107
+ {
108
+ "expected": null,
109
+ "input": {
110
+ "now": "2026-11-06T07:54:59.000Z",
111
+ "secret": "fixture-secret-fixture-secret-00",
112
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
113
+ "kind": "verify"
114
+ },
115
+ "name": "verify captured in the future"
116
+ },
117
+ {
118
+ "expected": {
119
+ "capturedAt": "2026-11-06T08:00:00.000Z",
120
+ "lhCid": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
121
+ },
122
+ "input": {
123
+ "now": "2026-11-06T07:55:01.000Z",
124
+ "secret": "fixture-secret-fixture-secret-00",
125
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
126
+ "kind": "verify"
127
+ },
128
+ "name": "verify within future skew"
129
+ },
130
+ {
131
+ "expected": null,
132
+ "input": {
133
+ "now": "2026-11-07T00:00:00.000Z",
134
+ "secret": "fixture-secret-fixture-secret-00",
135
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxxx.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148",
136
+ "kind": "verify"
137
+ },
138
+ "name": "verify tampered payload"
139
+ },
140
+ {
141
+ "expected": null,
142
+ "input": {
143
+ "now": "2026-11-07T00:00:00.000Z",
144
+ "secret": "fixture-secret-fixture-secret-00",
145
+ "token": "eyJjYXB0dXJlZEF0IjoiMjAyNi0xMS0wNlQwODowMDowMC4wMDBaIiwibGhDaWQiOiIzZjJhOWMxZS01YjdkLTRlOGEtOWMyMS03ZDRlNWY2YThiOTAiLCJ2ZXJzaW9uIjoxfQ.8T7LWAoeGsHxFp2U7WWylIDGXI19NFdSPtEtkhlb148.x",
146
+ "kind": "verify"
147
+ },
148
+ "name": "verify three parts"
149
+ },
150
+ {
151
+ "expected": null,
152
+ "input": {
153
+ "now": "2026-11-07T00:00:00.000Z",
154
+ "secret": "fixture-secret-fixture-secret-00",
155
+ "token": "a+b.c/d",
156
+ "kind": "verify"
157
+ },
158
+ "name": "verify not base64url"
159
+ },
160
+ {
161
+ "expected": null,
162
+ "input": {
163
+ "now": "2026-11-07T00:00:00.000Z",
164
+ "secret": "fixture-secret-fixture-secret-00",
165
+ "token": "",
166
+ "kind": "verify"
167
+ },
168
+ "name": "verify empty"
169
+ },
170
+ {
171
+ "expected": null,
172
+ "input": {
173
+ "now": "2026-11-07T00:00:00.000Z",
174
+ "secret": "fixture-secret-fixture-secret-00",
175
+ "token": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
176
+ "kind": "verify"
177
+ },
178
+ "name": "verify too long"
179
+ },
180
+ {
181
+ "expected": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90",
182
+ "input": {
183
+ "kind": "capture",
184
+ "url": "https://lobehub.com/tasks?lh_cid=3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
185
+ },
186
+ "name": "capture valid click id"
187
+ },
188
+ {
189
+ "expected": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90",
190
+ "input": {
191
+ "kind": "capture",
192
+ "url": "https://lobehub.com/tasks?lh_cid=3F2A9C1E-5B7D-4E8A-9C21-7D4E5F6A8B90"
193
+ },
194
+ "name": "capture upper-case click id"
195
+ },
196
+ {
197
+ "expected": null,
198
+ "input": {
199
+ "kind": "capture",
200
+ "url": "https://lobehub.com/tasks?lh_cid=3f2a9c1e-5b7d-1e8a-9c21-7d4e5f6a8b90"
201
+ },
202
+ "name": "capture not a v4 uuid"
203
+ },
204
+ {
205
+ "expected": null,
206
+ "input": {
207
+ "kind": "capture",
208
+ "url": "https://lobehub.com/tasks?lh_cid=user-private-value"
209
+ },
210
+ "name": "capture free text"
211
+ },
212
+ {
213
+ "expected": null,
214
+ "input": {
215
+ "kind": "capture",
216
+ "url": "https://lobehub.com/tasks"
217
+ },
218
+ "name": "capture missing"
219
+ },
220
+ {
221
+ "expected": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90",
222
+ "input": {
223
+ "kind": "capture",
224
+ "url": "https://lobehub.com/signin?callbackUrl=https%3A%2F%2Flobehub.com%2Ftasks%3Flh_cid%3D3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
225
+ },
226
+ "name": "capture from the sign-in callback"
227
+ },
228
+ {
229
+ "expected": "3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90",
230
+ "input": {
231
+ "kind": "capture",
232
+ "url": "https://lobehub.com/signup?callbackUrl=https%3A%2F%2Flobehub.com%2Ftasks%3Flh_cid%3D3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
233
+ },
234
+ "name": "capture from the sign-up callback"
235
+ },
236
+ {
237
+ "expected": null,
238
+ "input": {
239
+ "kind": "capture",
240
+ "url": "https://lobehub.com/signin?callbackUrl=https%3A%2F%2Fevil.example%2F%3Flh_cid%3D3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
241
+ },
242
+ "name": "capture ignores a cross-origin callback"
243
+ },
244
+ {
245
+ "expected": null,
246
+ "input": {
247
+ "kind": "capture",
248
+ "url": "https://lobehub.com/tasks?callbackUrl=https%3A%2F%2Flobehub.com%2Fx%3Flh_cid%3D3f2a9c1e-5b7d-4e8a-9c21-7d4e5f6a8b90"
249
+ },
250
+ "name": "capture ignores callbacks off the auth pages"
251
+ }
252
+ ],
253
+ "cookie": {
254
+ "maxAgeSeconds": 2592000,
255
+ "name": "lh_acq"
256
+ }
257
+ }
@@ -0,0 +1,141 @@
1
+ {
2
+ "authority": "lobehub-cloud@19f89cf50 packages/const/src/workspace.ts parseLastWorkspaceCookie",
3
+ "cases": [
4
+ {
5
+ "expected": "my-team",
6
+ "input": {
7
+ "raw": "user_1:my-team",
8
+ "userId": "user_1"
9
+ },
10
+ "name": "valid"
11
+ },
12
+ {
13
+ "expected": null,
14
+ "input": {
15
+ "raw": "user_1:my-team",
16
+ "userId": "user_2"
17
+ },
18
+ "name": "another user"
19
+ },
20
+ {
21
+ "expected": null,
22
+ "input": {
23
+ "raw": "my-team",
24
+ "userId": "user_1"
25
+ },
26
+ "name": "no separator"
27
+ },
28
+ {
29
+ "expected": null,
30
+ "input": {
31
+ "raw": ":my-team",
32
+ "userId": "user_1"
33
+ },
34
+ "name": "empty user part"
35
+ },
36
+ {
37
+ "expected": null,
38
+ "input": {
39
+ "raw": "user_1:",
40
+ "userId": "user_1"
41
+ },
42
+ "name": "empty slug"
43
+ },
44
+ {
45
+ "expected": null,
46
+ "input": {
47
+ "raw": "user_1:ab",
48
+ "userId": "user_1"
49
+ },
50
+ "name": "slug too short"
51
+ },
52
+ {
53
+ "expected": "abc",
54
+ "input": {
55
+ "raw": "user_1:abc",
56
+ "userId": "user_1"
57
+ },
58
+ "name": "slug at min length"
59
+ },
60
+ {
61
+ "expected": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
62
+ "input": {
63
+ "raw": "user_1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
64
+ "userId": "user_1"
65
+ },
66
+ "name": "slug at max length"
67
+ },
68
+ {
69
+ "expected": null,
70
+ "input": {
71
+ "raw": "user_1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
72
+ "userId": "user_1"
73
+ },
74
+ "name": "slug too long"
75
+ },
76
+ {
77
+ "expected": null,
78
+ "input": {
79
+ "raw": "user_1:My-Team",
80
+ "userId": "user_1"
81
+ },
82
+ "name": "upper-case slug"
83
+ },
84
+ {
85
+ "expected": null,
86
+ "input": {
87
+ "raw": "user_1:my-team-",
88
+ "userId": "user_1"
89
+ },
90
+ "name": "trailing dash"
91
+ },
92
+ {
93
+ "expected": null,
94
+ "input": {
95
+ "raw": "user_1:-my-team",
96
+ "userId": "user_1"
97
+ },
98
+ "name": "leading dash"
99
+ },
100
+ {
101
+ "expected": null,
102
+ "input": {
103
+ "raw": "user_1:../admin",
104
+ "userId": "user_1"
105
+ },
106
+ "name": "path traversal"
107
+ },
108
+ {
109
+ "expected": null,
110
+ "input": {
111
+ "raw": "user_1:my:team",
112
+ "userId": "user_1"
113
+ },
114
+ "name": "second colon"
115
+ },
116
+ {
117
+ "expected": null,
118
+ "input": {
119
+ "raw": "user_1:my-team\r\nx",
120
+ "userId": "user_1"
121
+ },
122
+ "name": "CRLF smuggling"
123
+ },
124
+ {
125
+ "expected": null,
126
+ "input": {
127
+ "raw": null,
128
+ "userId": "user_1"
129
+ },
130
+ "name": "missing cookie"
131
+ },
132
+ {
133
+ "expected": null,
134
+ "input": {
135
+ "raw": "user_1:my-team",
136
+ "userId": null
137
+ },
138
+ "name": "missing user"
139
+ }
140
+ ]
141
+ }