@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.
Files changed (186) hide show
  1. package/CHANGELOG.md +305 -0
  2. package/biome.json +5 -0
  3. package/jsr.json +8 -8
  4. package/package.json +63 -32
  5. package/src/__tests__/ai-temp-auth.test.ts +309 -0
  6. package/src/__tests__/api-proxy-guard.test.ts +1451 -0
  7. package/src/__tests__/app-url.test.ts +270 -0
  8. package/src/__tests__/avatar-url.test.ts +97 -0
  9. package/src/__tests__/color-helper.test.ts +179 -0
  10. package/src/__tests__/constants.test.ts +351 -0
  11. package/src/__tests__/crypto.test.ts +107 -0
  12. package/src/__tests__/date-helper.test.ts +408 -0
  13. package/src/__tests__/fixtures/task-description-full-featured.json +456 -0
  14. package/src/__tests__/format.test.ts +317 -0
  15. package/src/__tests__/html-sanitizer.test.ts +360 -0
  16. package/src/__tests__/interest-calculator.test.ts +336 -0
  17. package/src/__tests__/interest-detector.test.ts +222 -0
  18. package/src/__tests__/label-colors.test.ts +241 -0
  19. package/src/__tests__/name-helper.test.ts +158 -0
  20. package/src/__tests__/node-diff.test.ts +576 -0
  21. package/src/__tests__/notification-service.test.ts +210 -0
  22. package/src/__tests__/onboarding-helper.test.ts +331 -0
  23. package/src/__tests__/path-helper.test.ts +152 -0
  24. package/src/__tests__/permissions.test.tsx +81 -0
  25. package/src/__tests__/request-emoji-limit.test.ts +172 -0
  26. package/src/__tests__/search-helper.test.ts +51 -0
  27. package/src/__tests__/storage-display-name.test.ts +37 -0
  28. package/src/__tests__/storage-path.test.ts +238 -0
  29. package/src/__tests__/tag-utils.test.ts +205 -0
  30. package/src/__tests__/task-description-yjs-state.test.ts +581 -0
  31. package/src/__tests__/task-helper-board-api-routing.test.ts +94 -0
  32. package/src/__tests__/task-helper-create-task.test.ts +129 -0
  33. package/src/__tests__/task-helpers.test.ts +464 -0
  34. package/src/__tests__/task-overrides.test.ts +305 -0
  35. package/src/__tests__/task-reorder-cache.test.ts +74 -0
  36. package/src/__tests__/task-sort-keys.test.ts +36 -0
  37. package/src/__tests__/task-transformers.test.ts +62 -0
  38. package/src/__tests__/text-helper.test.ts +776 -0
  39. package/src/__tests__/time-helper.test.ts +70 -0
  40. package/src/__tests__/time-tracker-period.test.ts +55 -0
  41. package/src/__tests__/timezone.test.ts +117 -0
  42. package/src/__tests__/upstash-rest.test.ts +77 -0
  43. package/src/__tests__/uuid-helper.test.ts +133 -0
  44. package/src/__tests__/workspace-helper.test.ts +859 -0
  45. package/src/__tests__/workspace-limits.test.ts +255 -0
  46. package/src/__tests__/yjs-helper.test.ts +581 -0
  47. package/src/abuse-protection/__tests__/backend-rate-limit.test.ts +113 -0
  48. package/src/abuse-protection/__tests__/edge.test.ts +136 -0
  49. package/src/abuse-protection/__tests__/index.test.ts +562 -0
  50. package/src/abuse-protection/__tests__/reputation.test.ts +192 -0
  51. package/src/abuse-protection/backend-rate-limit.ts +44 -0
  52. package/src/abuse-protection/constants.ts +117 -0
  53. package/src/abuse-protection/edge.ts +223 -0
  54. package/src/abuse-protection/index.ts +1545 -0
  55. package/src/abuse-protection/reputation.ts +587 -0
  56. package/src/abuse-protection/types.ts +97 -0
  57. package/src/abuse-protection/user-agent.ts +124 -0
  58. package/src/abuse-protection/user-suspension.ts +231 -0
  59. package/src/ai-temp-auth.ts +315 -0
  60. package/src/api-proxy-guard.ts +965 -0
  61. package/src/app-url.ts +96 -0
  62. package/src/avatar-url.ts +64 -0
  63. package/src/break-duration.ts +84 -0
  64. package/src/calendar-auth-token.test.ts +37 -0
  65. package/src/calendar-auth-token.ts +19 -0
  66. package/src/calendar-sync-coordination.md +197 -0
  67. package/src/calendar-utils.test.ts +169 -0
  68. package/src/calendar-utils.ts +91 -0
  69. package/src/color-helper.ts +110 -0
  70. package/src/common/nextjs.tsx +99 -0
  71. package/src/common/scan.tsx +15 -0
  72. package/src/configs/reports.ts +160 -0
  73. package/src/constants.ts +85 -0
  74. package/src/crypto.ts +21 -0
  75. package/src/currencies.ts +97 -0
  76. package/src/date-helper.ts +313 -0
  77. package/src/editor/convert-to-task.ts +264 -0
  78. package/src/editor/index.ts +5 -0
  79. package/src/email/__tests__/client.test.ts +141 -0
  80. package/src/email/__tests__/validation.test.ts +46 -0
  81. package/src/email/client.ts +92 -0
  82. package/src/email/server.ts +128 -0
  83. package/src/email/validation.ts +11 -0
  84. package/src/encryption/__tests__/calendar-events.test.ts +411 -0
  85. package/src/encryption/__tests__/configuration.test.ts +114 -0
  86. package/src/encryption/__tests__/field-encryption.test.ts +232 -0
  87. package/src/encryption/__tests__/key-generation.test.ts +30 -0
  88. package/src/encryption/__tests__/performance-edge-cases.test.ts +187 -0
  89. package/src/encryption/__tests__/test-helpers.ts +22 -0
  90. package/src/encryption/__tests__/workspace-key-encryption.test.ts +129 -0
  91. package/src/encryption/encryption-service.ts +343 -0
  92. package/src/encryption/index.ts +25 -0
  93. package/src/encryption/types.ts +57 -0
  94. package/src/exchange-rates.ts +49 -0
  95. package/src/feature-flags/__tests__/feature-flags.test.ts +302 -0
  96. package/src/feature-flags/core.ts +322 -0
  97. package/src/feature-flags/data.ts +16 -0
  98. package/src/feature-flags/default.ts +18 -0
  99. package/src/feature-flags/index.ts +7 -0
  100. package/src/feature-flags/requestable-features.ts +79 -0
  101. package/src/feature-flags/types.ts +4 -0
  102. package/src/fetcher.ts +2 -0
  103. package/src/finance/index.ts +4 -0
  104. package/src/finance/interest-calculator.ts +456 -0
  105. package/src/finance/interest-detector.ts +141 -0
  106. package/src/finance/transform-invoice-results.ts +219 -0
  107. package/src/finance/wallet-permissions.test.ts +169 -0
  108. package/src/finance/wallet-permissions.ts +82 -0
  109. package/src/format.ts +120 -1
  110. package/src/generated/platform-build-metadata.ts +11 -0
  111. package/src/hooks/use-platform.ts +64 -0
  112. package/src/html-sanitizer.ts +155 -0
  113. package/src/internal-domains.ts +497 -0
  114. package/src/keyboard-preset.ts +109 -0
  115. package/src/label-colors.ts +213 -0
  116. package/src/launchable-apps.test.ts +126 -0
  117. package/src/launchable-apps.ts +490 -0
  118. package/src/name-helper.ts +269 -0
  119. package/src/next-config.test.ts +234 -0
  120. package/src/next-config.ts +203 -0
  121. package/src/node-diff.ts +375 -0
  122. package/src/notification-service.ts +379 -0
  123. package/src/nova/scores/__tests__/calculate.test.ts +254 -0
  124. package/src/nova/scores/calculate.ts +132 -0
  125. package/src/nova/submissions/check-permission.ts +132 -0
  126. package/src/onboarding-helper.ts +213 -0
  127. package/src/path-helper.ts +93 -0
  128. package/src/permissions.tsx +1170 -0
  129. package/src/plan-helpers.test.ts +188 -0
  130. package/src/plan-helpers.ts +80 -0
  131. package/src/platform-release.test.ts +74 -0
  132. package/src/platform-release.ts +155 -0
  133. package/src/portless.ts +124 -0
  134. package/src/priority-styles.ts +42 -0
  135. package/src/request-emoji-limit.ts +335 -0
  136. package/src/search-helper.ts +18 -0
  137. package/src/search.test.ts +89 -0
  138. package/src/search.ts +355 -0
  139. package/src/storage-display-name.ts +30 -0
  140. package/src/storage-path.ts +147 -0
  141. package/src/tag-utils.ts +159 -0
  142. package/src/task/reorder.ts +245 -0
  143. package/src/task/transformers.ts +149 -0
  144. package/src/task-date-timezone.ts +133 -0
  145. package/src/task-description-content.ts +240 -0
  146. package/src/task-helper/board.ts +193 -0
  147. package/src/task-helper/bulk-actions.ts +564 -0
  148. package/src/task-helper/personal-external-staging.ts +21 -0
  149. package/src/task-helper/recycle-bin.ts +202 -0
  150. package/src/task-helper/relationships.ts +346 -0
  151. package/src/task-helper/shared.ts +109 -0
  152. package/src/task-helper/sort-keys.ts +337 -0
  153. package/src/task-helper/task-hooks-basic.ts +342 -0
  154. package/src/task-helper/task-hooks-move.ts +264 -0
  155. package/src/task-helper/task-operations.ts +278 -0
  156. package/src/task-helper.ts +12 -0
  157. package/src/task-helpers.ts +241 -0
  158. package/src/task-list-status.ts +62 -0
  159. package/src/task-overrides.ts +82 -0
  160. package/src/task-snapshot.ts +374 -0
  161. package/src/text-diff.ts +81 -0
  162. package/src/text-helper.ts +537 -0
  163. package/src/time-helper.ts +63 -0
  164. package/src/time-tracker-period.ts +73 -0
  165. package/src/timeblock-helper.ts +418 -0
  166. package/src/timezone.ts +190 -0
  167. package/src/timezones.json +1271 -0
  168. package/src/upstash-rest.ts +56 -0
  169. package/src/user-helper.ts +296 -0
  170. package/src/uuid-helper.ts +11 -0
  171. package/src/workspace-handle.ts +10 -0
  172. package/src/workspace-helper.ts +1408 -0
  173. package/src/workspace-limits.ts +68 -0
  174. package/src/yjs-helper.ts +217 -0
  175. package/src/yjs-task-description.ts +81 -0
  176. package/tsconfig.json +3 -5
  177. package/tsconfig.typecheck.json +33 -0
  178. package/vitest.config.ts +36 -0
  179. package/dist/index.d.ts +0 -8
  180. package/dist/index.js +0 -2
  181. package/dist/index.js.map +0 -1
  182. package/dist/index.mjs +0 -2
  183. package/dist/index.mjs.map +0 -1
  184. package/eslint.config.mjs +0 -20
  185. package/rollup.config.js +0 -41
  186. package/src/index.ts +0 -1
@@ -0,0 +1,302 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { FEATURE_GROUPS } from '../core';
3
+ import { FEATURE_FLAGS } from '../data';
4
+ import type { FeatureFlag, FeatureFlagMap } from '../types';
5
+
6
+ describe('FEATURE_FLAGS', () => {
7
+ describe('AI-related flags', () => {
8
+ it('should have ENABLE_AI flag', () => {
9
+ expect(FEATURE_FLAGS.ENABLE_AI).toBe('ENABLE_AI');
10
+ });
11
+
12
+ it('should have ENABLE_AI_ONLY flag', () => {
13
+ expect(FEATURE_FLAGS.ENABLE_AI_ONLY).toBe('ENABLE_AI_ONLY');
14
+ });
15
+
16
+ it('should have ENABLE_CHAT flag', () => {
17
+ expect(FEATURE_FLAGS.ENABLE_CHAT).toBe('ENABLE_CHAT');
18
+ });
19
+ });
20
+
21
+ describe('Education-related flags', () => {
22
+ it('should have ENABLE_EDUCATION flag', () => {
23
+ expect(FEATURE_FLAGS.ENABLE_EDUCATION).toBe('ENABLE_EDUCATION');
24
+ });
25
+
26
+ it('should have ENABLE_QUIZZES flag', () => {
27
+ expect(FEATURE_FLAGS.ENABLE_QUIZZES).toBe('ENABLE_QUIZZES');
28
+ });
29
+
30
+ it('should have ENABLE_CHALLENGES flag', () => {
31
+ expect(FEATURE_FLAGS.ENABLE_CHALLENGES).toBe('ENABLE_CHALLENGES');
32
+ });
33
+ });
34
+
35
+ describe('Document-related flags', () => {
36
+ it('should have ENABLE_DOCS flag', () => {
37
+ expect(FEATURE_FLAGS.ENABLE_DOCS).toBe('ENABLE_DOCS');
38
+ });
39
+
40
+ it('should have ENABLE_DRIVE flag', () => {
41
+ expect(FEATURE_FLAGS.ENABLE_DRIVE).toBe('ENABLE_DRIVE');
42
+ });
43
+
44
+ it('should have ENABLE_SLIDES flag', () => {
45
+ expect(FEATURE_FLAGS.ENABLE_SLIDES).toBe('ENABLE_SLIDES');
46
+ });
47
+ });
48
+
49
+ describe('Task-related flags', () => {
50
+ it('should have ENABLE_TASKS flag', () => {
51
+ expect(FEATURE_FLAGS.ENABLE_TASKS).toBe('ENABLE_TASKS');
52
+ });
53
+ });
54
+
55
+ describe('Workspace-related flags', () => {
56
+ it('should have DISABLE_INVITE flag', () => {
57
+ expect(FEATURE_FLAGS.DISABLE_INVITE).toBe('DISABLE_INVITE');
58
+ });
59
+
60
+ it('should have PREVENT_WORKSPACE_DELETION flag', () => {
61
+ expect(FEATURE_FLAGS.PREVENT_WORKSPACE_DELETION).toBe(
62
+ 'PREVENT_WORKSPACE_DELETION'
63
+ );
64
+ });
65
+
66
+ it('should have ENABLE_AVATAR flag', () => {
67
+ expect(FEATURE_FLAGS.ENABLE_AVATAR).toBe('ENABLE_AVATAR');
68
+ });
69
+
70
+ it('should have ENABLE_LOGO flag', () => {
71
+ expect(FEATURE_FLAGS.ENABLE_LOGO).toBe('ENABLE_LOGO');
72
+ });
73
+ });
74
+
75
+ describe('flag count and structure', () => {
76
+ it('should have exactly 14 feature flags', () => {
77
+ const flags = Object.keys(FEATURE_FLAGS);
78
+ expect(flags).toHaveLength(14);
79
+ });
80
+
81
+ it('should have all unique values', () => {
82
+ const values = Object.values(FEATURE_FLAGS);
83
+ const uniqueValues = new Set(values);
84
+ expect(uniqueValues.size).toBe(values.length);
85
+ });
86
+
87
+ it('should have keys matching values (self-referential)', () => {
88
+ for (const [key, value] of Object.entries(FEATURE_FLAGS)) {
89
+ expect(key).toBe(value);
90
+ }
91
+ });
92
+ });
93
+ });
94
+
95
+ describe('FEATURE_GROUPS', () => {
96
+ describe('AI_FEATURES group', () => {
97
+ it('should include ENABLE_AI, ENABLE_CHAT, ENABLE_TASKS', () => {
98
+ expect(FEATURE_GROUPS.AI_FEATURES).toContain(FEATURE_FLAGS.ENABLE_AI);
99
+ expect(FEATURE_GROUPS.AI_FEATURES).toContain(FEATURE_FLAGS.ENABLE_CHAT);
100
+ expect(FEATURE_GROUPS.AI_FEATURES).toContain(FEATURE_FLAGS.ENABLE_TASKS);
101
+ });
102
+
103
+ it('should have exactly 3 flags', () => {
104
+ expect(FEATURE_GROUPS.AI_FEATURES).toHaveLength(3);
105
+ });
106
+ });
107
+
108
+ describe('EDUCATION_FEATURES group', () => {
109
+ it('should include education-related flags', () => {
110
+ expect(FEATURE_GROUPS.EDUCATION_FEATURES).toContain(
111
+ FEATURE_FLAGS.ENABLE_EDUCATION
112
+ );
113
+ expect(FEATURE_GROUPS.EDUCATION_FEATURES).toContain(
114
+ FEATURE_FLAGS.ENABLE_QUIZZES
115
+ );
116
+ expect(FEATURE_GROUPS.EDUCATION_FEATURES).toContain(
117
+ FEATURE_FLAGS.ENABLE_CHALLENGES
118
+ );
119
+ expect(FEATURE_GROUPS.EDUCATION_FEATURES).toContain(
120
+ FEATURE_FLAGS.ENABLE_AI
121
+ );
122
+ });
123
+
124
+ it('should have exactly 4 flags', () => {
125
+ expect(FEATURE_GROUPS.EDUCATION_FEATURES).toHaveLength(4);
126
+ });
127
+ });
128
+
129
+ describe('DOCUMENT_FEATURES group', () => {
130
+ it('should include document-related flags', () => {
131
+ expect(FEATURE_GROUPS.DOCUMENT_FEATURES).toContain(
132
+ FEATURE_FLAGS.ENABLE_DOCS
133
+ );
134
+ expect(FEATURE_GROUPS.DOCUMENT_FEATURES).toContain(
135
+ FEATURE_FLAGS.ENABLE_DRIVE
136
+ );
137
+ expect(FEATURE_GROUPS.DOCUMENT_FEATURES).toContain(
138
+ FEATURE_FLAGS.ENABLE_SLIDES
139
+ );
140
+ });
141
+
142
+ it('should have exactly 3 flags', () => {
143
+ expect(FEATURE_GROUPS.DOCUMENT_FEATURES).toHaveLength(3);
144
+ });
145
+ });
146
+
147
+ describe('WORKSPACE_FEATURES group', () => {
148
+ it('should include workspace customization flags', () => {
149
+ expect(FEATURE_GROUPS.WORKSPACE_FEATURES).toContain(
150
+ FEATURE_FLAGS.ENABLE_AVATAR
151
+ );
152
+ expect(FEATURE_GROUPS.WORKSPACE_FEATURES).toContain(
153
+ FEATURE_FLAGS.ENABLE_LOGO
154
+ );
155
+ });
156
+
157
+ it('should have exactly 2 flags', () => {
158
+ expect(FEATURE_GROUPS.WORKSPACE_FEATURES).toHaveLength(2);
159
+ });
160
+ });
161
+
162
+ describe('group integrity', () => {
163
+ it('all groups should only contain valid feature flags', () => {
164
+ const allFlags = Object.values(FEATURE_FLAGS);
165
+
166
+ for (const [, group] of Object.entries(FEATURE_GROUPS)) {
167
+ for (const flag of group) {
168
+ expect(allFlags).toContain(flag);
169
+ }
170
+ }
171
+ });
172
+ });
173
+ });
174
+
175
+ describe('FeatureFlag type', () => {
176
+ it('should accept valid feature flag keys', () => {
177
+ // Type checking tests
178
+ const flag1: FeatureFlag = 'ENABLE_AI';
179
+ const flag2: FeatureFlag = 'ENABLE_EDUCATION';
180
+ const flag3: FeatureFlag = 'ENABLE_DOCS';
181
+
182
+ expect(flag1).toBe('ENABLE_AI');
183
+ expect(flag2).toBe('ENABLE_EDUCATION');
184
+ expect(flag3).toBe('ENABLE_DOCS');
185
+ });
186
+ });
187
+
188
+ describe('FeatureFlagMap type', () => {
189
+ it('should create valid feature flag map', () => {
190
+ const flagMap: FeatureFlagMap = {
191
+ ENABLE_AI: true,
192
+ ENABLE_EDUCATION: false,
193
+ ENABLE_QUIZZES: true,
194
+ ENABLE_CHALLENGES: false,
195
+ ENABLE_AI_ONLY: false,
196
+ ENABLE_CHAT: true,
197
+ ENABLE_TASKS: true,
198
+ ENABLE_DOCS: false,
199
+ ENABLE_DRIVE: false,
200
+ ENABLE_SLIDES: false,
201
+ DISABLE_INVITE: false,
202
+ PREVENT_WORKSPACE_DELETION: true,
203
+ ENABLE_AVATAR: true,
204
+ ENABLE_LOGO: true,
205
+ };
206
+
207
+ expect(flagMap.ENABLE_AI).toBe(true);
208
+ expect(flagMap.ENABLE_EDUCATION).toBe(false);
209
+ });
210
+
211
+ it('should enforce boolean values', () => {
212
+ const flagMap: Partial<FeatureFlagMap> = {
213
+ ENABLE_AI: true,
214
+ ENABLE_EDUCATION: false,
215
+ };
216
+
217
+ expect(typeof flagMap.ENABLE_AI).toBe('boolean');
218
+ expect(typeof flagMap.ENABLE_EDUCATION).toBe('boolean');
219
+ });
220
+ });
221
+
222
+ describe('Feature flag usage patterns', () => {
223
+ describe('checking multiple flags', () => {
224
+ it('should support checking if all flags are enabled', () => {
225
+ const flagMap: FeatureFlagMap = {
226
+ ENABLE_AI: true,
227
+ ENABLE_EDUCATION: true,
228
+ ENABLE_QUIZZES: true,
229
+ ENABLE_CHALLENGES: true,
230
+ ENABLE_AI_ONLY: false,
231
+ ENABLE_CHAT: true,
232
+ ENABLE_TASKS: true,
233
+ ENABLE_DOCS: true,
234
+ ENABLE_DRIVE: true,
235
+ ENABLE_SLIDES: true,
236
+ DISABLE_INVITE: false,
237
+ PREVENT_WORKSPACE_DELETION: false,
238
+ ENABLE_AVATAR: true,
239
+ ENABLE_LOGO: true,
240
+ };
241
+
242
+ const aiFeatures: FeatureFlag[] = [
243
+ 'ENABLE_AI',
244
+ 'ENABLE_CHAT',
245
+ 'ENABLE_TASKS',
246
+ ];
247
+ const allAIEnabled = aiFeatures.every((flag) => flagMap[flag]);
248
+ expect(allAIEnabled).toBe(true);
249
+ });
250
+
251
+ it('should support checking if any flag is enabled', () => {
252
+ const flagMap: FeatureFlagMap = {
253
+ ENABLE_AI: false,
254
+ ENABLE_EDUCATION: false,
255
+ ENABLE_QUIZZES: false,
256
+ ENABLE_CHALLENGES: true, // Only this is enabled
257
+ ENABLE_AI_ONLY: false,
258
+ ENABLE_CHAT: false,
259
+ ENABLE_TASKS: false,
260
+ ENABLE_DOCS: false,
261
+ ENABLE_DRIVE: false,
262
+ ENABLE_SLIDES: false,
263
+ DISABLE_INVITE: false,
264
+ PREVENT_WORKSPACE_DELETION: false,
265
+ ENABLE_AVATAR: false,
266
+ ENABLE_LOGO: false,
267
+ };
268
+
269
+ const educationFeatures: FeatureFlag[] = [
270
+ 'ENABLE_EDUCATION',
271
+ 'ENABLE_QUIZZES',
272
+ 'ENABLE_CHALLENGES',
273
+ ];
274
+ const anyEducationEnabled = educationFeatures.some(
275
+ (flag) => flagMap[flag]
276
+ );
277
+ expect(anyEducationEnabled).toBe(true);
278
+ });
279
+ });
280
+
281
+ describe('negative flags (DISABLE_*)', () => {
282
+ it('should handle DISABLE_INVITE flag logic', () => {
283
+ const flagMap: Partial<FeatureFlagMap> = {
284
+ DISABLE_INVITE: true,
285
+ };
286
+
287
+ // When DISABLE_INVITE is true, invites should be disabled
288
+ const invitesEnabled = !flagMap.DISABLE_INVITE;
289
+ expect(invitesEnabled).toBe(false);
290
+ });
291
+
292
+ it('should handle PREVENT_WORKSPACE_DELETION flag logic', () => {
293
+ const flagMap: Partial<FeatureFlagMap> = {
294
+ PREVENT_WORKSPACE_DELETION: true,
295
+ };
296
+
297
+ // When PREVENT_WORKSPACE_DELETION is true, deletion should be prevented
298
+ const canDelete = !flagMap.PREVENT_WORKSPACE_DELETION;
299
+ expect(canDelete).toBe(false);
300
+ });
301
+ });
302
+ });
@@ -0,0 +1,322 @@
1
+ import { verifySecret } from '../workspace-helper';
2
+ import { FEATURE_FLAGS } from './data';
3
+ import type { FeatureFlag, FeatureFlagMap } from './types';
4
+
5
+ export async function requireFeatureFlags(
6
+ wsId: string,
7
+ {
8
+ requiredFlags = [],
9
+ forceAdmin = false,
10
+ }: {
11
+ requiredFlags: FeatureFlag[];
12
+ forceAdmin?: boolean;
13
+ }
14
+ ): Promise<{ featureFlags: FeatureFlagMap; missingFlags: FeatureFlag[] }> {
15
+ const featureFlags = await getFeatureFlags(wsId, forceAdmin);
16
+ const missingFlags = requiredFlags.filter((flag) => !featureFlags[flag]);
17
+ return {
18
+ featureFlags,
19
+ missingFlags,
20
+ };
21
+ }
22
+
23
+ /**
24
+ * Get all feature flags for a workspace
25
+ * @param wsId - Workspace ID
26
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
27
+ * @returns Promise<FeatureFlags>
28
+ */
29
+ export async function getFeatureFlags(
30
+ wsId: string,
31
+ forceAdmin: boolean = true
32
+ ): Promise<FeatureFlagMap> {
33
+ const [
34
+ ENABLE_AI,
35
+ ENABLE_EDUCATION,
36
+ ENABLE_QUIZZES,
37
+ ENABLE_CHALLENGES,
38
+ ENABLE_AI_ONLY,
39
+ ENABLE_CHAT,
40
+ ENABLE_TASKS,
41
+ ENABLE_DOCS,
42
+ ENABLE_DRIVE,
43
+ ENABLE_SLIDES,
44
+ DISABLE_INVITE,
45
+ PREVENT_WORKSPACE_DELETION,
46
+ ENABLE_AVATAR,
47
+ ENABLE_LOGO,
48
+ ] = await Promise.all([
49
+ verifySecret({
50
+ forceAdmin,
51
+ wsId,
52
+ name: FEATURE_FLAGS.ENABLE_AI,
53
+ value: 'true',
54
+ }),
55
+ verifySecret({
56
+ forceAdmin,
57
+ wsId,
58
+ name: FEATURE_FLAGS.ENABLE_EDUCATION,
59
+ value: 'true',
60
+ }),
61
+ verifySecret({
62
+ forceAdmin,
63
+ wsId,
64
+ name: FEATURE_FLAGS.ENABLE_QUIZZES,
65
+ value: 'true',
66
+ }),
67
+ verifySecret({
68
+ forceAdmin,
69
+ wsId,
70
+ name: FEATURE_FLAGS.ENABLE_CHALLENGES,
71
+ value: 'true',
72
+ }),
73
+ verifySecret({
74
+ forceAdmin,
75
+ wsId,
76
+ name: FEATURE_FLAGS.ENABLE_AI_ONLY,
77
+ value: 'true',
78
+ }),
79
+ verifySecret({
80
+ forceAdmin,
81
+ wsId,
82
+ name: FEATURE_FLAGS.ENABLE_CHAT,
83
+ value: 'true',
84
+ }),
85
+ verifySecret({
86
+ forceAdmin,
87
+ wsId,
88
+ name: FEATURE_FLAGS.ENABLE_TASKS,
89
+ value: 'true',
90
+ }),
91
+ verifySecret({
92
+ forceAdmin,
93
+ wsId,
94
+ name: FEATURE_FLAGS.ENABLE_DOCS,
95
+ value: 'true',
96
+ }),
97
+ verifySecret({
98
+ forceAdmin,
99
+ wsId,
100
+ name: FEATURE_FLAGS.ENABLE_DRIVE,
101
+ value: 'true',
102
+ }),
103
+ verifySecret({
104
+ forceAdmin,
105
+ wsId,
106
+ name: FEATURE_FLAGS.ENABLE_SLIDES,
107
+ value: 'true',
108
+ }),
109
+ verifySecret({
110
+ forceAdmin,
111
+ wsId,
112
+ name: FEATURE_FLAGS.DISABLE_INVITE,
113
+ value: 'true',
114
+ }),
115
+ verifySecret({
116
+ forceAdmin,
117
+ wsId,
118
+ name: FEATURE_FLAGS.PREVENT_WORKSPACE_DELETION,
119
+ value: 'true',
120
+ }),
121
+ verifySecret({
122
+ forceAdmin,
123
+ wsId,
124
+ name: FEATURE_FLAGS.ENABLE_AVATAR,
125
+ value: 'true',
126
+ }),
127
+ verifySecret({
128
+ forceAdmin,
129
+ wsId,
130
+ name: FEATURE_FLAGS.ENABLE_LOGO,
131
+ value: 'true',
132
+ }),
133
+ ]);
134
+
135
+ return {
136
+ ENABLE_AI,
137
+ ENABLE_EDUCATION,
138
+ ENABLE_QUIZZES,
139
+ ENABLE_CHALLENGES,
140
+ ENABLE_AI_ONLY,
141
+ ENABLE_CHAT,
142
+ ENABLE_TASKS,
143
+ ENABLE_DOCS,
144
+ ENABLE_DRIVE,
145
+ ENABLE_SLIDES,
146
+ DISABLE_INVITE,
147
+ PREVENT_WORKSPACE_DELETION,
148
+ ENABLE_AVATAR,
149
+ ENABLE_LOGO,
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Get a specific feature flag
155
+ * @param wsId - Workspace ID
156
+ * @param flagName - Feature flag name
157
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
158
+ * @returns Promise<boolean>
159
+ */
160
+ export async function getFeatureFlag(
161
+ wsId: string,
162
+ flagName: FeatureFlag,
163
+ forceAdmin: boolean = true
164
+ ): Promise<boolean> {
165
+ return verifySecret({
166
+ forceAdmin,
167
+ wsId,
168
+ name: FEATURE_FLAGS[flagName],
169
+ value: 'true',
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Check if multiple feature flags are enabled
175
+ * @param wsId - Workspace ID
176
+ * @param flagNames - Array of feature flag names
177
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
178
+ * @returns Promise<boolean>
179
+ */
180
+ export async function areFeatureFlagsEnabled(
181
+ wsId: string,
182
+ flagNames: readonly FeatureFlag[],
183
+ forceAdmin: boolean = true
184
+ ): Promise<boolean> {
185
+ const checks = await Promise.all(
186
+ flagNames.map((flagName) =>
187
+ verifySecret({
188
+ forceAdmin,
189
+ wsId,
190
+ name: FEATURE_FLAGS[flagName],
191
+ value: 'true',
192
+ })
193
+ )
194
+ );
195
+ return checks.every(Boolean);
196
+ }
197
+
198
+ /**
199
+ * Check if any of the specified feature flags are enabled
200
+ * @param wsId - Workspace ID
201
+ * @param flagNames - Array of feature flag names
202
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
203
+ * @returns Promise<boolean>
204
+ */
205
+ export async function isAnyFeatureFlagEnabled(
206
+ wsId: string,
207
+ flagNames: readonly FeatureFlag[],
208
+ forceAdmin: boolean = true
209
+ ): Promise<boolean> {
210
+ const checks = await Promise.all(
211
+ flagNames.map((flagName) =>
212
+ verifySecret({
213
+ forceAdmin,
214
+ wsId,
215
+ name: FEATURE_FLAGS[flagName],
216
+ value: 'true',
217
+ })
218
+ )
219
+ );
220
+ return checks.some(Boolean);
221
+ }
222
+
223
+ // Common feature flag combinations
224
+ export const FEATURE_GROUPS = {
225
+ AI_FEATURES: [
226
+ FEATURE_FLAGS.ENABLE_AI,
227
+ FEATURE_FLAGS.ENABLE_CHAT,
228
+ FEATURE_FLAGS.ENABLE_TASKS,
229
+ ] as const,
230
+ EDUCATION_FEATURES: [
231
+ FEATURE_FLAGS.ENABLE_EDUCATION,
232
+ FEATURE_FLAGS.ENABLE_QUIZZES,
233
+ FEATURE_FLAGS.ENABLE_CHALLENGES,
234
+ FEATURE_FLAGS.ENABLE_AI,
235
+ ] as const,
236
+ DOCUMENT_FEATURES: [
237
+ FEATURE_FLAGS.ENABLE_DOCS,
238
+ FEATURE_FLAGS.ENABLE_DRIVE,
239
+ FEATURE_FLAGS.ENABLE_SLIDES,
240
+ ] as const,
241
+ WORKSPACE_FEATURES: [
242
+ FEATURE_FLAGS.ENABLE_AVATAR,
243
+ FEATURE_FLAGS.ENABLE_LOGO,
244
+ ] as const,
245
+ } as const;
246
+
247
+ /**
248
+ * Check if all AI features are enabled
249
+ * @param wsId - Workspace ID
250
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
251
+ * @returns Promise<boolean>
252
+ */
253
+ export async function areAIFeaturesEnabled(
254
+ wsId: string,
255
+ forceAdmin: boolean = true
256
+ ): Promise<boolean> {
257
+ return areFeatureFlagsEnabled(wsId, FEATURE_GROUPS.AI_FEATURES, forceAdmin);
258
+ }
259
+
260
+ /**
261
+ * Check if all education features are enabled
262
+ * @param wsId - Workspace ID
263
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
264
+ * @returns Promise<boolean>
265
+ */
266
+ export async function areEducationFeaturesEnabled(
267
+ wsId: string,
268
+ forceAdmin: boolean = true
269
+ ): Promise<boolean> {
270
+ return areFeatureFlagsEnabled(
271
+ wsId,
272
+ FEATURE_GROUPS.EDUCATION_FEATURES,
273
+ forceAdmin
274
+ );
275
+ }
276
+
277
+ /**
278
+ * Check if all document features are enabled
279
+ * @param wsId - Workspace ID
280
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
281
+ * @returns Promise<boolean>
282
+ */
283
+ export async function areDocumentFeaturesEnabled(
284
+ wsId: string,
285
+ forceAdmin: boolean = true
286
+ ): Promise<boolean> {
287
+ return areFeatureFlagsEnabled(
288
+ wsId,
289
+ FEATURE_GROUPS.DOCUMENT_FEATURES,
290
+ forceAdmin
291
+ );
292
+ }
293
+
294
+ /**
295
+ * Check if any AI features are enabled
296
+ * @param wsId - Workspace ID
297
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
298
+ * @returns Promise<boolean>
299
+ */
300
+ export async function isAnyAIFeatureEnabled(
301
+ wsId: string,
302
+ forceAdmin: boolean = true
303
+ ): Promise<boolean> {
304
+ return isAnyFeatureFlagEnabled(wsId, FEATURE_GROUPS.AI_FEATURES, forceAdmin);
305
+ }
306
+
307
+ /**
308
+ * Check if any education features are enabled
309
+ * @param wsId - Workspace ID
310
+ * @param forceAdmin - Whether to use admin client (default: true for feature flags)
311
+ * @returns Promise<boolean>
312
+ */
313
+ export async function isAnyEducationFeatureEnabled(
314
+ wsId: string,
315
+ forceAdmin: boolean = true
316
+ ): Promise<boolean> {
317
+ return isAnyFeatureFlagEnabled(
318
+ wsId,
319
+ FEATURE_GROUPS.EDUCATION_FEATURES,
320
+ forceAdmin
321
+ );
322
+ }
@@ -0,0 +1,16 @@
1
+ export const FEATURE_FLAGS = {
2
+ ENABLE_AI: 'ENABLE_AI',
3
+ ENABLE_EDUCATION: 'ENABLE_EDUCATION',
4
+ ENABLE_QUIZZES: 'ENABLE_QUIZZES',
5
+ ENABLE_CHALLENGES: 'ENABLE_CHALLENGES',
6
+ ENABLE_AI_ONLY: 'ENABLE_AI_ONLY',
7
+ ENABLE_CHAT: 'ENABLE_CHAT',
8
+ ENABLE_TASKS: 'ENABLE_TASKS',
9
+ ENABLE_DOCS: 'ENABLE_DOCS',
10
+ ENABLE_DRIVE: 'ENABLE_DRIVE',
11
+ ENABLE_SLIDES: 'ENABLE_SLIDES',
12
+ DISABLE_INVITE: 'DISABLE_INVITE',
13
+ PREVENT_WORKSPACE_DELETION: 'PREVENT_WORKSPACE_DELETION',
14
+ ENABLE_AVATAR: 'ENABLE_AVATAR',
15
+ ENABLE_LOGO: 'ENABLE_LOGO',
16
+ } as const;
@@ -0,0 +1,18 @@
1
+ import type { FeatureFlagMap } from './types';
2
+
3
+ export const DEFAULT_FEATURE_FLAGS: FeatureFlagMap = {
4
+ ENABLE_AI: false,
5
+ ENABLE_EDUCATION: false,
6
+ ENABLE_QUIZZES: false,
7
+ ENABLE_CHALLENGES: false,
8
+ ENABLE_AI_ONLY: false,
9
+ ENABLE_CHAT: false,
10
+ ENABLE_TASKS: false,
11
+ ENABLE_DOCS: false,
12
+ ENABLE_DRIVE: false,
13
+ ENABLE_SLIDES: false,
14
+ DISABLE_INVITE: false,
15
+ PREVENT_WORKSPACE_DELETION: false,
16
+ ENABLE_AVATAR: false,
17
+ ENABLE_LOGO: false,
18
+ };
@@ -0,0 +1,7 @@
1
+ // Core feature flags
2
+ export * from './core';
3
+ export * from './data';
4
+ export * from './default';
5
+ // Requestable features
6
+ export * from './requestable-features';
7
+ export * from './types';