@tuturuuu/utils 0.0.3 → 0.6.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.
- package/CHANGELOG.md +305 -0
- package/biome.json +5 -0
- package/jsr.json +8 -8
- package/package.json +63 -32
- package/src/__tests__/ai-temp-auth.test.ts +309 -0
- package/src/__tests__/api-proxy-guard.test.ts +1451 -0
- package/src/__tests__/app-url.test.ts +270 -0
- package/src/__tests__/avatar-url.test.ts +97 -0
- package/src/__tests__/color-helper.test.ts +179 -0
- package/src/__tests__/constants.test.ts +351 -0
- package/src/__tests__/crypto.test.ts +107 -0
- package/src/__tests__/date-helper.test.ts +408 -0
- package/src/__tests__/fixtures/task-description-full-featured.json +456 -0
- package/src/__tests__/format.test.ts +317 -0
- package/src/__tests__/html-sanitizer.test.ts +360 -0
- package/src/__tests__/interest-calculator.test.ts +336 -0
- package/src/__tests__/interest-detector.test.ts +222 -0
- package/src/__tests__/label-colors.test.ts +241 -0
- package/src/__tests__/name-helper.test.ts +158 -0
- package/src/__tests__/node-diff.test.ts +576 -0
- package/src/__tests__/notification-service.test.ts +210 -0
- package/src/__tests__/onboarding-helper.test.ts +331 -0
- package/src/__tests__/path-helper.test.ts +152 -0
- package/src/__tests__/permissions.test.tsx +81 -0
- package/src/__tests__/request-emoji-limit.test.ts +172 -0
- package/src/__tests__/search-helper.test.ts +51 -0
- package/src/__tests__/storage-display-name.test.ts +37 -0
- package/src/__tests__/storage-path.test.ts +238 -0
- package/src/__tests__/tag-utils.test.ts +205 -0
- package/src/__tests__/task-description-yjs-state.test.ts +581 -0
- package/src/__tests__/task-helper-board-api-routing.test.ts +94 -0
- package/src/__tests__/task-helper-create-task.test.ts +129 -0
- package/src/__tests__/task-helpers.test.ts +464 -0
- package/src/__tests__/task-overrides.test.ts +305 -0
- package/src/__tests__/task-reorder-cache.test.ts +74 -0
- package/src/__tests__/task-sort-keys.test.ts +36 -0
- package/src/__tests__/task-transformers.test.ts +62 -0
- package/src/__tests__/text-helper.test.ts +776 -0
- package/src/__tests__/time-helper.test.ts +70 -0
- package/src/__tests__/time-tracker-period.test.ts +55 -0
- package/src/__tests__/timezone.test.ts +117 -0
- package/src/__tests__/upstash-rest.test.ts +77 -0
- package/src/__tests__/uuid-helper.test.ts +133 -0
- package/src/__tests__/workspace-helper.test.ts +859 -0
- package/src/__tests__/workspace-limits.test.ts +255 -0
- package/src/__tests__/yjs-helper.test.ts +581 -0
- package/src/abuse-protection/__tests__/backend-rate-limit.test.ts +113 -0
- package/src/abuse-protection/__tests__/edge.test.ts +136 -0
- package/src/abuse-protection/__tests__/index.test.ts +562 -0
- package/src/abuse-protection/__tests__/reputation.test.ts +192 -0
- package/src/abuse-protection/backend-rate-limit.ts +44 -0
- package/src/abuse-protection/constants.ts +117 -0
- package/src/abuse-protection/edge.ts +223 -0
- package/src/abuse-protection/index.ts +1545 -0
- package/src/abuse-protection/reputation.ts +587 -0
- package/src/abuse-protection/types.ts +97 -0
- package/src/abuse-protection/user-agent.ts +124 -0
- package/src/abuse-protection/user-suspension.ts +231 -0
- package/src/ai-temp-auth.ts +315 -0
- package/src/api-proxy-guard.ts +965 -0
- package/src/app-url.ts +96 -0
- package/src/avatar-url.ts +64 -0
- package/src/break-duration.ts +84 -0
- package/src/calendar-auth-token.test.ts +37 -0
- package/src/calendar-auth-token.ts +19 -0
- package/src/calendar-sync-coordination.md +197 -0
- package/src/calendar-utils.test.ts +169 -0
- package/src/calendar-utils.ts +91 -0
- package/src/color-helper.ts +110 -0
- package/src/common/nextjs.tsx +99 -0
- package/src/common/scan.tsx +15 -0
- package/src/configs/reports.ts +160 -0
- package/src/constants.ts +85 -0
- package/src/crypto.ts +21 -0
- package/src/currencies.ts +97 -0
- package/src/date-helper.ts +313 -0
- package/src/editor/convert-to-task.ts +264 -0
- package/src/editor/index.ts +5 -0
- package/src/email/__tests__/client.test.ts +141 -0
- package/src/email/__tests__/validation.test.ts +46 -0
- package/src/email/client.ts +92 -0
- package/src/email/server.ts +128 -0
- package/src/email/validation.ts +11 -0
- package/src/encryption/__tests__/calendar-events.test.ts +411 -0
- package/src/encryption/__tests__/configuration.test.ts +114 -0
- package/src/encryption/__tests__/field-encryption.test.ts +232 -0
- package/src/encryption/__tests__/key-generation.test.ts +30 -0
- package/src/encryption/__tests__/performance-edge-cases.test.ts +187 -0
- package/src/encryption/__tests__/test-helpers.ts +22 -0
- package/src/encryption/__tests__/workspace-key-encryption.test.ts +129 -0
- package/src/encryption/encryption-service.ts +343 -0
- package/src/encryption/index.ts +25 -0
- package/src/encryption/types.ts +57 -0
- package/src/exchange-rates.ts +49 -0
- package/src/feature-flags/__tests__/feature-flags.test.ts +302 -0
- package/src/feature-flags/core.ts +322 -0
- package/src/feature-flags/data.ts +16 -0
- package/src/feature-flags/default.ts +18 -0
- package/src/feature-flags/index.ts +7 -0
- package/src/feature-flags/requestable-features.ts +79 -0
- package/src/feature-flags/types.ts +4 -0
- package/src/fetcher.ts +2 -0
- package/src/finance/index.ts +4 -0
- package/src/finance/interest-calculator.ts +456 -0
- package/src/finance/interest-detector.ts +141 -0
- package/src/finance/transform-invoice-results.ts +219 -0
- package/src/finance/wallet-permissions.test.ts +169 -0
- package/src/finance/wallet-permissions.ts +82 -0
- package/src/format.ts +120 -1
- package/src/generated/platform-build-metadata.ts +11 -0
- package/src/hooks/use-platform.ts +64 -0
- package/src/html-sanitizer.ts +155 -0
- package/src/internal-domains.ts +497 -0
- package/src/keyboard-preset.ts +109 -0
- package/src/label-colors.ts +213 -0
- package/src/launchable-apps.test.ts +126 -0
- package/src/launchable-apps.ts +490 -0
- package/src/name-helper.ts +269 -0
- package/src/next-config.test.ts +234 -0
- package/src/next-config.ts +203 -0
- package/src/node-diff.ts +375 -0
- package/src/notification-service.ts +379 -0
- package/src/nova/scores/__tests__/calculate.test.ts +254 -0
- package/src/nova/scores/calculate.ts +132 -0
- package/src/nova/submissions/check-permission.ts +132 -0
- package/src/onboarding-helper.ts +213 -0
- package/src/path-helper.ts +93 -0
- package/src/permissions.tsx +1170 -0
- package/src/plan-helpers.test.ts +188 -0
- package/src/plan-helpers.ts +80 -0
- package/src/platform-release.test.ts +74 -0
- package/src/platform-release.ts +155 -0
- package/src/portless.ts +124 -0
- package/src/priority-styles.ts +42 -0
- package/src/request-emoji-limit.ts +335 -0
- package/src/search-helper.ts +18 -0
- package/src/search.test.ts +89 -0
- package/src/search.ts +355 -0
- package/src/storage-display-name.ts +30 -0
- package/src/storage-path.ts +147 -0
- package/src/tag-utils.ts +159 -0
- package/src/task/reorder.ts +245 -0
- package/src/task/transformers.ts +149 -0
- package/src/task-date-timezone.ts +133 -0
- package/src/task-description-content.ts +240 -0
- package/src/task-helper/board.ts +193 -0
- package/src/task-helper/bulk-actions.ts +564 -0
- package/src/task-helper/personal-external-staging.ts +21 -0
- package/src/task-helper/recycle-bin.ts +202 -0
- package/src/task-helper/relationships.ts +346 -0
- package/src/task-helper/shared.ts +109 -0
- package/src/task-helper/sort-keys.ts +337 -0
- package/src/task-helper/task-hooks-basic.ts +342 -0
- package/src/task-helper/task-hooks-move.ts +264 -0
- package/src/task-helper/task-operations.ts +278 -0
- package/src/task-helper.ts +12 -0
- package/src/task-helpers.ts +241 -0
- package/src/task-list-status.ts +62 -0
- package/src/task-overrides.ts +82 -0
- package/src/task-snapshot.ts +374 -0
- package/src/text-diff.ts +81 -0
- package/src/text-helper.ts +537 -0
- package/src/time-helper.ts +63 -0
- package/src/time-tracker-period.ts +73 -0
- package/src/timeblock-helper.ts +418 -0
- package/src/timezone.ts +190 -0
- package/src/timezones.json +1271 -0
- package/src/upstash-rest.ts +56 -0
- package/src/user-helper.ts +296 -0
- package/src/uuid-helper.ts +11 -0
- package/src/workspace-handle.ts +10 -0
- package/src/workspace-helper.ts +1408 -0
- package/src/workspace-limits.ts +68 -0
- package/src/yjs-helper.ts +217 -0
- package/src/yjs-task-description.ts +81 -0
- package/tsconfig.json +3 -5
- package/tsconfig.typecheck.json +33 -0
- package/vitest.config.ts +36 -0
- package/dist/index.d.ts +0 -8
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -2
- package/dist/index.mjs.map +0 -1
- package/eslint.config.mjs +0 -20
- package/rollup.config.js +0 -41
- package/src/index.ts +0 -1
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { MAX_WORKSPACES_FOR_FREE_USERS } from '../constants';
|
|
3
|
+
import {
|
|
4
|
+
checkWorkspaceCreationLimit,
|
|
5
|
+
WORKSPACE_LIMIT_ERROR_CODE,
|
|
6
|
+
} from '../workspace-limits';
|
|
7
|
+
|
|
8
|
+
describe('checkWorkspaceCreationLimit', () => {
|
|
9
|
+
const mockUserId = 'user-123';
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('when user has a Tuturuuu email', () => {
|
|
16
|
+
it('should allow creation for @tuturuuu.com email', async () => {
|
|
17
|
+
const mockSupabase = createMockSupabase({ count: 100, error: null });
|
|
18
|
+
|
|
19
|
+
const result = await checkWorkspaceCreationLimit(
|
|
20
|
+
mockSupabase,
|
|
21
|
+
mockUserId,
|
|
22
|
+
'user@tuturuuu.com'
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(result.canCreate).toBe(true);
|
|
26
|
+
expect(result.errorCode).toBeUndefined();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should allow creation for @xwf.tuturuuu.com email', async () => {
|
|
30
|
+
const mockSupabase = createMockSupabase({ count: 100, error: null });
|
|
31
|
+
|
|
32
|
+
const result = await checkWorkspaceCreationLimit(
|
|
33
|
+
mockSupabase,
|
|
34
|
+
mockUserId,
|
|
35
|
+
'admin@xwf.tuturuuu.com'
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(result.canCreate).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should allow creation regardless of workspace count for internal emails', async () => {
|
|
42
|
+
const mockSupabase = createMockSupabase({ count: 1000, error: null });
|
|
43
|
+
|
|
44
|
+
const result = await checkWorkspaceCreationLimit(
|
|
45
|
+
mockSupabase,
|
|
46
|
+
mockUserId,
|
|
47
|
+
'test@tuturuuu.com'
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(result.canCreate).toBe(true);
|
|
51
|
+
expect(result.currentCount).toBeUndefined();
|
|
52
|
+
expect(result.limit).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('when user has a regular email', () => {
|
|
57
|
+
it('should allow creation when under limit', async () => {
|
|
58
|
+
const mockSupabase = createMockSupabase({ count: 5, error: null });
|
|
59
|
+
|
|
60
|
+
const result = await checkWorkspaceCreationLimit(
|
|
61
|
+
mockSupabase,
|
|
62
|
+
mockUserId,
|
|
63
|
+
'user@example.com'
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(result.canCreate).toBe(true);
|
|
67
|
+
expect(result.currentCount).toBe(5);
|
|
68
|
+
expect(result.limit).toBe(MAX_WORKSPACES_FOR_FREE_USERS);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should allow creation when at zero workspaces', async () => {
|
|
72
|
+
const mockSupabase = createMockSupabase({ count: 0, error: null });
|
|
73
|
+
|
|
74
|
+
const result = await checkWorkspaceCreationLimit(
|
|
75
|
+
mockSupabase,
|
|
76
|
+
mockUserId,
|
|
77
|
+
'newuser@gmail.com'
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
expect(result.canCreate).toBe(true);
|
|
81
|
+
expect(result.currentCount).toBe(0);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should allow creation when one below limit', async () => {
|
|
85
|
+
const mockSupabase = createMockSupabase({
|
|
86
|
+
count: MAX_WORKSPACES_FOR_FREE_USERS - 1,
|
|
87
|
+
error: null,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const result = await checkWorkspaceCreationLimit(
|
|
91
|
+
mockSupabase,
|
|
92
|
+
mockUserId,
|
|
93
|
+
'user@example.com'
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(result.canCreate).toBe(true);
|
|
97
|
+
expect(result.currentCount).toBe(MAX_WORKSPACES_FOR_FREE_USERS - 1);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should deny creation when at limit', async () => {
|
|
101
|
+
const mockSupabase = createMockSupabase({
|
|
102
|
+
count: MAX_WORKSPACES_FOR_FREE_USERS,
|
|
103
|
+
error: null,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const result = await checkWorkspaceCreationLimit(
|
|
107
|
+
mockSupabase,
|
|
108
|
+
mockUserId,
|
|
109
|
+
'user@example.com'
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
expect(result.canCreate).toBe(false);
|
|
113
|
+
expect(result.currentCount).toBe(MAX_WORKSPACES_FOR_FREE_USERS);
|
|
114
|
+
expect(result.limit).toBe(MAX_WORKSPACES_FOR_FREE_USERS);
|
|
115
|
+
expect(result.errorCode).toBe(WORKSPACE_LIMIT_ERROR_CODE);
|
|
116
|
+
expect(result.errorMessage).toContain(
|
|
117
|
+
`${MAX_WORKSPACES_FOR_FREE_USERS} workspaces`
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should deny creation when over limit', async () => {
|
|
122
|
+
const mockSupabase = createMockSupabase({
|
|
123
|
+
count: MAX_WORKSPACES_FOR_FREE_USERS + 5,
|
|
124
|
+
error: null,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = await checkWorkspaceCreationLimit(
|
|
128
|
+
mockSupabase,
|
|
129
|
+
mockUserId,
|
|
130
|
+
'user@example.com'
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(result.canCreate).toBe(false);
|
|
134
|
+
expect(result.errorCode).toBe(WORKSPACE_LIMIT_ERROR_CODE);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('when user email is null or undefined', () => {
|
|
139
|
+
it('should check workspace count when email is null', async () => {
|
|
140
|
+
const mockSupabase = createMockSupabase({ count: 5, error: null });
|
|
141
|
+
|
|
142
|
+
const result = await checkWorkspaceCreationLimit(
|
|
143
|
+
mockSupabase,
|
|
144
|
+
mockUserId,
|
|
145
|
+
null
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
expect(result.canCreate).toBe(true);
|
|
149
|
+
expect(result.currentCount).toBe(5);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should check workspace count when email is undefined', async () => {
|
|
153
|
+
const mockSupabase = createMockSupabase({ count: 5, error: null });
|
|
154
|
+
|
|
155
|
+
const result = await checkWorkspaceCreationLimit(
|
|
156
|
+
mockSupabase,
|
|
157
|
+
mockUserId,
|
|
158
|
+
undefined
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
expect(result.canCreate).toBe(true);
|
|
162
|
+
expect(result.currentCount).toBe(5);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should deny when at limit with null email', async () => {
|
|
166
|
+
const mockSupabase = createMockSupabase({
|
|
167
|
+
count: MAX_WORKSPACES_FOR_FREE_USERS,
|
|
168
|
+
error: null,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const result = await checkWorkspaceCreationLimit(
|
|
172
|
+
mockSupabase,
|
|
173
|
+
mockUserId,
|
|
174
|
+
null
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
expect(result.canCreate).toBe(false);
|
|
178
|
+
expect(result.errorCode).toBe(WORKSPACE_LIMIT_ERROR_CODE);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('when database query fails', () => {
|
|
183
|
+
it('should return error when count query fails', async () => {
|
|
184
|
+
const mockError = { message: 'Database error', code: 'PGRST500' };
|
|
185
|
+
const mockSupabase = createMockSupabase({
|
|
186
|
+
count: null,
|
|
187
|
+
error: mockError,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const consoleSpy = vi
|
|
191
|
+
.spyOn(console, 'error')
|
|
192
|
+
.mockImplementation(() => {});
|
|
193
|
+
|
|
194
|
+
const result = await checkWorkspaceCreationLimit(
|
|
195
|
+
mockSupabase,
|
|
196
|
+
mockUserId,
|
|
197
|
+
'user@example.com'
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
expect(result.canCreate).toBe(false);
|
|
201
|
+
expect(result.errorCode).toBe('WORKSPACE_COUNT_ERROR');
|
|
202
|
+
expect(result.errorMessage).toBe('Error checking workspace limit');
|
|
203
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
204
|
+
|
|
205
|
+
consoleSpy.mockRestore();
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe('edge cases', () => {
|
|
210
|
+
it('should handle count being null', async () => {
|
|
211
|
+
const mockSupabase = createMockSupabase({ count: null, error: null });
|
|
212
|
+
|
|
213
|
+
const result = await checkWorkspaceCreationLimit(
|
|
214
|
+
mockSupabase,
|
|
215
|
+
mockUserId,
|
|
216
|
+
'user@example.com'
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
expect(result.canCreate).toBe(true);
|
|
220
|
+
expect(result.currentCount).toBe(0);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should handle empty string email as non-Tuturuuu', async () => {
|
|
224
|
+
const mockSupabase = createMockSupabase({ count: 5, error: null });
|
|
225
|
+
|
|
226
|
+
const result = await checkWorkspaceCreationLimit(
|
|
227
|
+
mockSupabase,
|
|
228
|
+
mockUserId,
|
|
229
|
+
''
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect(result.canCreate).toBe(true);
|
|
233
|
+
expect(result.currentCount).toBe(5);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Helper function to create mock Supabase client
|
|
239
|
+
function createMockSupabase({
|
|
240
|
+
count,
|
|
241
|
+
error,
|
|
242
|
+
}: {
|
|
243
|
+
count: number | null;
|
|
244
|
+
error: { message: string; code?: string } | null;
|
|
245
|
+
}) {
|
|
246
|
+
return {
|
|
247
|
+
from: vi.fn().mockReturnValue({
|
|
248
|
+
select: vi.fn().mockReturnValue({
|
|
249
|
+
eq: vi.fn().mockReturnValue({
|
|
250
|
+
eq: vi.fn().mockResolvedValue({ count, error }),
|
|
251
|
+
}),
|
|
252
|
+
}),
|
|
253
|
+
}),
|
|
254
|
+
};
|
|
255
|
+
}
|