@tuturuuu/utils 0.28.0 → 0.29.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,283 @@
1
+ import type { ComparisonFeature } from './commercial-comparison-types';
2
+ export const features1: ComparisonFeature[] = [
3
+ {
4
+ id: 'meet_4',
5
+ app: 'meet',
6
+ category: 'work',
7
+ values: ['preview', 'preview', 'preview', 'preview'],
8
+ detail: 'standard',
9
+ },
10
+ {
11
+ id: 'contacts_0',
12
+ app: 'contacts',
13
+ category: 'operations',
14
+ values: ['included', 'included', 'included', 'included'],
15
+ detail: 'standard',
16
+ },
17
+ {
18
+ id: 'contacts_1',
19
+ app: 'contacts',
20
+ category: 'operations',
21
+ values: ['included', 'included', 'included', 'included'],
22
+ detail: 'standard',
23
+ },
24
+ {
25
+ id: 'contacts_2',
26
+ app: 'contacts',
27
+ category: 'operations',
28
+ values: ['included', 'included', 'included', 'included'],
29
+ detail: 'standard',
30
+ },
31
+ {
32
+ id: 'contacts_3',
33
+ app: 'contacts',
34
+ category: 'operations',
35
+ values: ['included', 'included', 'included', 'included'],
36
+ detail: 'standard',
37
+ },
38
+ {
39
+ id: 'contacts_4',
40
+ app: 'contacts',
41
+ category: 'operations',
42
+ values: ['included', 'included', 'included', 'included'],
43
+ detail: 'standard',
44
+ },
45
+ {
46
+ id: 'contacts_5',
47
+ app: 'contacts',
48
+ category: 'operations',
49
+ values: ['included', 'included', 'included', 'included'],
50
+ detail: 'standard',
51
+ },
52
+ {
53
+ id: 'contacts_6',
54
+ app: 'contacts',
55
+ category: 'operations',
56
+ values: ['included', 'included', 'included', 'included'],
57
+ detail: 'standard',
58
+ },
59
+ {
60
+ id: 'finance_0',
61
+ app: 'finance',
62
+ category: 'operations',
63
+ values: ['included', 'included', 'included', 'included'],
64
+ detail: 'storage',
65
+ },
66
+ {
67
+ id: 'finance_1',
68
+ app: 'finance',
69
+ category: 'operations',
70
+ values: ['included', 'included', 'included', 'included'],
71
+ detail: 'storage',
72
+ },
73
+ {
74
+ id: 'finance_2',
75
+ app: 'finance',
76
+ category: 'operations',
77
+ values: ['included', 'included', 'included', 'included'],
78
+ detail: 'storage',
79
+ },
80
+ {
81
+ id: 'finance_3',
82
+ app: 'finance',
83
+ category: 'operations',
84
+ values: ['included', 'included', 'included', 'included'],
85
+ detail: 'storage',
86
+ },
87
+ {
88
+ id: 'finance_4',
89
+ app: 'finance',
90
+ category: 'operations',
91
+ values: ['included', 'included', 'included', 'included'],
92
+ detail: 'storage',
93
+ },
94
+ {
95
+ id: 'finance_5',
96
+ app: 'finance',
97
+ category: 'operations',
98
+ values: ['included', 'included', 'included', 'included'],
99
+ detail: 'storage',
100
+ },
101
+ {
102
+ id: 'finance_6',
103
+ app: 'finance',
104
+ category: 'operations',
105
+ values: ['included', 'included', 'included', 'included'],
106
+ detail: 'storage',
107
+ },
108
+ {
109
+ id: 'inventory_0',
110
+ app: 'inventory',
111
+ category: 'operations',
112
+ values: ['included', 'included', 'included', 'included'],
113
+ detail: 'standard',
114
+ },
115
+ {
116
+ id: 'inventory_1',
117
+ app: 'inventory',
118
+ category: 'operations',
119
+ values: ['included', 'included', 'included', 'included'],
120
+ detail: 'standard',
121
+ },
122
+ {
123
+ id: 'forms_0',
124
+ app: 'forms',
125
+ category: 'operations',
126
+ values: ['included', 'included', 'included', 'included'],
127
+ detail: 'storage',
128
+ },
129
+ {
130
+ id: 'forms_1',
131
+ app: 'forms',
132
+ category: 'operations',
133
+ values: ['included', 'included', 'included', 'included'],
134
+ detail: 'storage',
135
+ },
136
+ {
137
+ id: 'forms_2',
138
+ app: 'forms',
139
+ category: 'operations',
140
+ values: ['included', 'included', 'included', 'included'],
141
+ detail: 'storage',
142
+ },
143
+ {
144
+ id: 'forms_3',
145
+ app: 'forms',
146
+ category: 'operations',
147
+ values: ['included', 'included', 'included', 'included'],
148
+ detail: 'storage',
149
+ },
150
+ {
151
+ id: 'cms_0',
152
+ app: 'cms',
153
+ category: 'operations',
154
+ values: ['included', 'included', 'included', 'included'],
155
+ detail: 'storage',
156
+ },
157
+ {
158
+ id: 'cms_1',
159
+ app: 'cms',
160
+ category: 'operations',
161
+ values: ['included', 'included', 'included', 'included'],
162
+ detail: 'storage',
163
+ },
164
+ {
165
+ id: 'cms_2',
166
+ app: 'cms',
167
+ category: 'operations',
168
+ values: ['included', 'included', 'included', 'included'],
169
+ detail: 'storage',
170
+ },
171
+ {
172
+ id: 'cms_3',
173
+ app: 'cms',
174
+ category: 'operations',
175
+ values: ['included', 'included', 'included', 'included'],
176
+ detail: 'storage',
177
+ },
178
+ {
179
+ id: 'cms_4',
180
+ app: 'cms',
181
+ category: 'operations',
182
+ values: ['included', 'included', 'included', 'included'],
183
+ detail: 'storage',
184
+ },
185
+ {
186
+ id: 'cms_5',
187
+ app: 'cms',
188
+ category: 'operations',
189
+ values: ['included', 'included', 'included', 'included'],
190
+ detail: 'storage',
191
+ },
192
+ {
193
+ id: 'cms_6',
194
+ app: 'cms',
195
+ category: 'operations',
196
+ values: ['included', 'included', 'included', 'included'],
197
+ detail: 'storage',
198
+ },
199
+ {
200
+ id: 'mail_0',
201
+ app: 'mail',
202
+ category: 'operations',
203
+ values: ['preview', 'preview', 'preview', 'preview'],
204
+ detail: 'standard',
205
+ },
206
+ {
207
+ id: 'mail_1',
208
+ app: 'mail',
209
+ category: 'operations',
210
+ values: ['preview', 'preview', 'preview', 'preview'],
211
+ detail: 'standard',
212
+ },
213
+ {
214
+ id: 'mail_2',
215
+ app: 'mail',
216
+ category: 'operations',
217
+ values: ['preview', 'preview', 'preview', 'preview'],
218
+ detail: 'standard',
219
+ },
220
+ {
221
+ id: 'ai_0',
222
+ app: 'ai',
223
+ category: 'ai',
224
+ values: ['included', 'included', 'included', 'included'],
225
+ detail: 'ai',
226
+ },
227
+ {
228
+ id: 'ai_1',
229
+ app: 'ai',
230
+ category: 'ai',
231
+ values: ['included', 'included', 'included', 'included'],
232
+ detail: 'ai',
233
+ },
234
+ {
235
+ id: 'ai_2',
236
+ app: 'ai',
237
+ category: 'ai',
238
+ values: ['included', 'included', 'included', 'included'],
239
+ detail: 'ai',
240
+ },
241
+ {
242
+ id: 'ai_3',
243
+ app: 'ai',
244
+ category: 'ai',
245
+ values: ['included', 'included', 'included', 'included'],
246
+ detail: 'ai',
247
+ },
248
+ {
249
+ id: 'ai_4',
250
+ app: 'ai',
251
+ category: 'ai',
252
+ values: ['included', 'included', 'included', 'included'],
253
+ detail: 'ai',
254
+ },
255
+ {
256
+ id: 'ai_5',
257
+ app: 'ai',
258
+ category: 'ai',
259
+ values: ['included', 'included', 'included', 'included'],
260
+ detail: 'ai',
261
+ },
262
+ {
263
+ id: 'mind_0',
264
+ app: 'mind',
265
+ category: 'ai',
266
+ values: ['preview', 'preview', 'preview', 'preview'],
267
+ detail: 'ai',
268
+ },
269
+ {
270
+ id: 'hive_0',
271
+ app: 'hive',
272
+ category: 'ai',
273
+ values: ['preview', 'preview', 'preview', 'preview'],
274
+ detail: 'ai',
275
+ },
276
+ {
277
+ id: 'hive_1',
278
+ app: 'hive',
279
+ category: 'ai',
280
+ values: ['preview', 'preview', 'preview', 'preview'],
281
+ detail: 'ai',
282
+ },
283
+ ];
@@ -0,0 +1,262 @@
1
+ import type { ComparisonFeature } from './commercial-comparison-types';
2
+ export const features2: ComparisonFeature[] = [
3
+ {
4
+ id: 'lettin_0',
5
+ app: 'lettin',
6
+ category: 'learning',
7
+ values: ['preview', 'preview', 'preview', 'preview'],
8
+ detail: 'standard',
9
+ },
10
+ {
11
+ id: 'colab_0',
12
+ app: 'colab',
13
+ category: 'learning',
14
+ values: ['included', 'included', 'included', 'included'],
15
+ detail: 'ai',
16
+ },
17
+ {
18
+ id: 'colab_1',
19
+ app: 'colab',
20
+ category: 'learning',
21
+ values: ['included', 'included', 'included', 'included'],
22
+ detail: 'ai',
23
+ },
24
+ {
25
+ id: 'colab_2',
26
+ app: 'colab',
27
+ category: 'learning',
28
+ values: ['included', 'included', 'included', 'included'],
29
+ detail: 'ai',
30
+ },
31
+ {
32
+ id: 'colab_3',
33
+ app: 'colab',
34
+ category: 'learning',
35
+ values: ['included', 'included', 'included', 'included'],
36
+ detail: 'ai',
37
+ },
38
+ {
39
+ id: 'learn_0',
40
+ app: 'learn',
41
+ category: 'learning',
42
+ values: ['preview', 'preview', 'preview', 'preview'],
43
+ detail: 'standard',
44
+ },
45
+ {
46
+ id: 'teach_0',
47
+ app: 'teach',
48
+ category: 'learning',
49
+ values: ['preview', 'preview', 'preview', 'preview'],
50
+ detail: 'standard',
51
+ },
52
+ {
53
+ id: 'rewise_0',
54
+ app: 'rewise',
55
+ category: 'learning',
56
+ values: ['preview', 'preview', 'preview', 'preview'],
57
+ detail: 'standard',
58
+ },
59
+ {
60
+ id: 'nova_0',
61
+ app: 'nova',
62
+ category: 'learning',
63
+ values: ['preview', 'preview', 'preview', 'preview'],
64
+ detail: 'standard',
65
+ },
66
+ {
67
+ id: 'git_0',
68
+ app: 'git',
69
+ category: 'developer',
70
+ values: ['included', 'included', 'included', 'included'],
71
+ detail: 'standard',
72
+ },
73
+ {
74
+ id: 'git_1',
75
+ app: 'git',
76
+ category: 'developer',
77
+ values: ['included', 'included', 'included', 'included'],
78
+ detail: 'standard',
79
+ },
80
+ {
81
+ id: 'git_2',
82
+ app: 'git',
83
+ category: 'developer',
84
+ values: ['included', 'included', 'included', 'included'],
85
+ detail: 'standard',
86
+ },
87
+ {
88
+ id: 'external_0',
89
+ app: 'external',
90
+ category: 'developer',
91
+ values: ['included', 'included', 'included', 'included'],
92
+ detail: 'standard',
93
+ },
94
+ {
95
+ id: 'external_1',
96
+ app: 'external',
97
+ category: 'developer',
98
+ values: ['included', 'included', 'included', 'included'],
99
+ detail: 'standard',
100
+ },
101
+ {
102
+ id: 'mobile_0',
103
+ app: 'mobile',
104
+ category: 'work',
105
+ values: ['included', 'included', 'included', 'included'],
106
+ detail: 'standard',
107
+ },
108
+ {
109
+ id: 'mobile_1',
110
+ app: 'mobile',
111
+ category: 'work',
112
+ values: ['included', 'included', 'included', 'included'],
113
+ detail: 'standard',
114
+ },
115
+ {
116
+ id: 'pay_0',
117
+ app: 'pay',
118
+ category: 'operations',
119
+ values: ['included', 'included', 'included', 'included'],
120
+ detail: 'standard',
121
+ },
122
+ {
123
+ id: 'pay_1',
124
+ app: 'pay',
125
+ category: 'operations',
126
+ values: ['included', 'included', 'included', 'included'],
127
+ detail: 'standard',
128
+ },
129
+ {
130
+ id: 'pay_2',
131
+ app: 'pay',
132
+ category: 'operations',
133
+ values: ['included', 'included', 'included', 'included'],
134
+ detail: 'standard',
135
+ },
136
+ {
137
+ id: 'apps_0',
138
+ app: 'apps',
139
+ category: 'work',
140
+ values: ['included', 'included', 'included', 'included'],
141
+ detail: 'standard',
142
+ },
143
+ {
144
+ id: 'tools_0',
145
+ app: 'tools',
146
+ category: 'work',
147
+ values: ['included', 'included', 'included', 'included'],
148
+ detail: 'standard',
149
+ },
150
+ {
151
+ id: 'shortener_0',
152
+ app: 'shortener',
153
+ category: 'work',
154
+ values: ['included', 'included', 'included', 'included'],
155
+ detail: 'standard',
156
+ },
157
+ {
158
+ id: 'storefront_0',
159
+ app: 'storefront',
160
+ category: 'operations',
161
+ values: ['preview', 'preview', 'preview', 'preview'],
162
+ detail: 'standard',
163
+ },
164
+ {
165
+ id: 'playground_0',
166
+ app: 'playground',
167
+ category: 'developer',
168
+ values: ['preview', 'preview', 'preview', 'preview'],
169
+ detail: 'standard',
170
+ },
171
+ {
172
+ id: 'discord_0',
173
+ app: 'discord',
174
+ category: 'developer',
175
+ values: ['preview', 'preview', 'preview', 'preview'],
176
+ detail: 'standard',
177
+ },
178
+ {
179
+ id: 'docs_0',
180
+ app: 'docs',
181
+ category: 'developer',
182
+ values: ['included', 'included', 'included', 'included'],
183
+ detail: 'standard',
184
+ },
185
+ {
186
+ id: 'backend_0',
187
+ app: 'backend',
188
+ category: 'internal',
189
+ values: ['internal', 'internal', 'internal', 'internal'],
190
+ detail: 'standard',
191
+ },
192
+ {
193
+ id: 'database_0',
194
+ app: 'database',
195
+ category: 'internal',
196
+ values: ['internal', 'internal', 'internal', 'internal'],
197
+ detail: 'standard',
198
+ },
199
+ {
200
+ id: 'redis_0',
201
+ app: 'redis',
202
+ category: 'internal',
203
+ values: ['internal', 'internal', 'internal', 'internal'],
204
+ detail: 'standard',
205
+ },
206
+ {
207
+ id: 'supermemory_0',
208
+ app: 'supermemory',
209
+ category: 'internal',
210
+ values: ['internal', 'internal', 'internal', 'internal'],
211
+ detail: 'standard',
212
+ },
213
+ {
214
+ id: 'storage-unzip-proxy_0',
215
+ app: 'storage-unzip-proxy',
216
+ category: 'internal',
217
+ values: ['internal', 'internal', 'internal', 'internal'],
218
+ detail: 'standard',
219
+ },
220
+ {
221
+ id: 'chat-realtime_0',
222
+ app: 'chat-realtime',
223
+ category: 'internal',
224
+ values: ['internal', 'internal', 'internal', 'internal'],
225
+ detail: 'standard',
226
+ },
227
+ {
228
+ id: 'coordination_0',
229
+ app: 'coordination',
230
+ category: 'internal',
231
+ values: ['internal', 'internal', 'internal', 'internal'],
232
+ detail: 'standard',
233
+ },
234
+ {
235
+ id: 'meet-realtime_0',
236
+ app: 'meet-realtime',
237
+ category: 'internal',
238
+ values: ['internal', 'internal', 'internal', 'internal'],
239
+ detail: 'standard',
240
+ },
241
+ {
242
+ id: 'hive-realtime_0',
243
+ app: 'hive-realtime',
244
+ category: 'internal',
245
+ values: ['internal', 'internal', 'internal', 'internal'],
246
+ detail: 'standard',
247
+ },
248
+ {
249
+ id: 'tanstack-web_0',
250
+ app: 'tanstack-web',
251
+ category: 'internal',
252
+ values: ['internal', 'internal', 'internal', 'internal'],
253
+ detail: 'standard',
254
+ },
255
+ {
256
+ id: 'infrastructure_0',
257
+ app: 'infrastructure',
258
+ category: 'internal',
259
+ values: ['internal', 'internal', 'internal', 'internal'],
260
+ detail: 'standard',
261
+ },
262
+ ];
@@ -0,0 +1,20 @@
1
+ import type { FEATURE_TIERS } from './feature-tier-policy';
2
+ /** Source-backed feature inventory. Preview is availability, never an upgrade promise.
3
+ * Navigation and feature-tiers.ts own capabilities; capacity rows identify pending rollout.
4
+ * Internal services share the host workspace budget and are not separately sold. */
5
+ export type ComparisonTier = 'free' | 'plus' | 'pro' | 'enterprise';
6
+ export const COMPARISON_TIERS: ComparisonTier[] = [
7
+ 'free',
8
+ 'plus',
9
+ 'pro',
10
+ 'enterprise',
11
+ ];
12
+ export interface ComparisonFeature {
13
+ id: string;
14
+ app: string;
15
+ category: string;
16
+ values: [string, string, string, string];
17
+ detail: string;
18
+ pending?: boolean;
19
+ gate?: keyof typeof FEATURE_TIERS;
20
+ }
@@ -0,0 +1,32 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ import { COMPARISON_FEATURES } from './commercial-comparison';
5
+
6
+ describe('commercial inventory coverage', () => {
7
+ it('represents every app directory and avoids duplicate feature identifiers', () => {
8
+ const apps = readdirSync(resolve(__dirname, '../../../apps'), {
9
+ withFileTypes: true,
10
+ })
11
+ .filter((item) => item.isDirectory() && !item.name.startsWith('.'))
12
+ .map((item) => (item.name === 'web' ? 'platform' : item.name));
13
+ const represented = new Set(COMPARISON_FEATURES.map((row) => row.app));
14
+ expect(apps.filter((app) => !represented.has(app))).toEqual([]);
15
+ expect(new Set(COMPARISON_FEATURES.map((row) => row.id)).size).toBe(
16
+ COMPARISON_FEATURES.length
17
+ );
18
+ });
19
+ it('does not sell privileged internal services or promise public Meet access', () => {
20
+ for (const row of COMPARISON_FEATURES.filter(
21
+ (row) => row.category === 'internal'
22
+ ))
23
+ expect(row.values).toEqual([
24
+ 'internal',
25
+ 'internal',
26
+ 'internal',
27
+ 'internal',
28
+ ]);
29
+ for (const row of COMPARISON_FEATURES.filter((row) => row.app === 'meet'))
30
+ expect(row.values).toEqual(['preview', 'preview', 'preview', 'preview']);
31
+ });
32
+ });
@@ -0,0 +1,96 @@
1
+ import type { ComparisonFeature } from './commercial-comparison-types';
2
+ import { FEATURE_TIERS } from './feature-tier-policy';
3
+
4
+ export * from './commercial-comparison-types';
5
+
6
+ import { features0 } from './commercial-comparison-0';
7
+ import { features1 } from './commercial-comparison-1';
8
+ import { features2 } from './commercial-comparison-2';
9
+
10
+ const gateOrder = ['FREE', 'PLUS', 'PRO', 'ENTERPRISE'];
11
+ const advanced: ComparisonFeature[] = [
12
+ {
13
+ id: 'prioritySupport',
14
+ app: 'platform',
15
+ category: 'operations',
16
+ detail: 'support',
17
+ values: ['excluded', 'excluded', 'included', 'contract'],
18
+ },
19
+ {
20
+ id: 'customIntegrations',
21
+ app: 'platform',
22
+ category: 'operations',
23
+ detail: 'support',
24
+ values: ['excluded', 'excluded', 'preview', 'contract'],
25
+ },
26
+ {
27
+ id: 'dedicatedSupport',
28
+ app: 'platform',
29
+ category: 'operations',
30
+ detail: 'support',
31
+ values: ['excluded', 'excluded', 'excluded', 'contract'],
32
+ },
33
+ {
34
+ id: 'sla',
35
+ app: 'platform',
36
+ category: 'operations',
37
+ detail: 'support',
38
+ values: ['excluded', 'excluded', 'excluded', 'contract'],
39
+ },
40
+ {
41
+ id: 'selfHosting',
42
+ app: 'platform',
43
+ category: 'operations',
44
+ detail: 'support',
45
+ values: ['excluded', 'excluded', 'excluded', 'contract'],
46
+ },
47
+ {
48
+ id: 'aiLab',
49
+ app: 'platform',
50
+ category: 'work',
51
+ detail: 'standard',
52
+ values: ['included', 'included', 'included', 'included'],
53
+ gate: 'ai_lab',
54
+ },
55
+ {
56
+ id: 'voiceAssistant',
57
+ app: 'platform',
58
+ category: 'work',
59
+ detail: 'standard',
60
+ values: ['included', 'included', 'included', 'included'],
61
+ gate: 'voice_assistant',
62
+ },
63
+ {
64
+ id: 'mira',
65
+ app: 'platform',
66
+ category: 'work',
67
+ detail: 'standard',
68
+ values: ['included', 'included', 'included', 'included'],
69
+ gate: 'mira',
70
+ },
71
+ {
72
+ id: 'workforce',
73
+ app: 'platform',
74
+ category: 'work',
75
+ detail: 'standard',
76
+ values: ['included', 'included', 'included', 'included'],
77
+ gate: 'workforce',
78
+ },
79
+ ];
80
+ export const COMPARISON_FEATURES = [
81
+ ...features0,
82
+ ...features1,
83
+ ...features2,
84
+ ...advanced,
85
+ ].map((row) => {
86
+ if (!row.gate) return row;
87
+ const required = FEATURE_TIERS[row.gate];
88
+ return {
89
+ ...row,
90
+ values: gateOrder.map((tier) =>
91
+ gateOrder.indexOf(tier) >= gateOrder.indexOf(required)
92
+ ? 'included'
93
+ : 'excluded'
94
+ ) as ComparisonFeature['values'],
95
+ };
96
+ });