@tuturuuu/utils 0.27.1 → 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.
- package/CHANGELOG.md +47 -0
- package/biome.json +1 -1
- package/package.json +18 -17
- package/src/__tests__/api-proxy-guard.test.ts +20 -27
- package/src/__tests__/app-url.test.ts +2 -0
- package/src/__tests__/workspace-principal-resolution.test.ts +34 -0
- package/src/ai-temp-auth-constants.ts +2 -0
- package/src/ai-temp-auth.ts +2 -1
- package/src/api-proxy-guard.ts +0 -15
- package/src/calendar-utils.ts +10 -1
- package/src/commercial-comparison-0.ts +294 -0
- package/src/commercial-comparison-1.ts +283 -0
- package/src/commercial-comparison-2.ts +262 -0
- package/src/commercial-comparison-types.ts +20 -0
- package/src/commercial-comparison.test.ts +32 -0
- package/src/commercial-comparison.ts +96 -0
- package/src/coordination-protocol.ts +61 -0
- package/src/coordination.test.ts +84 -0
- package/src/coordination.ts +57 -0
- package/src/feature-tier-policy.ts +26 -0
- package/src/internal-domains.ts +6 -0
- package/src/keyboard-shortcuts.ts +40 -0
- package/src/launchable-apps.ts +25 -0
- package/src/meet-creation-policy.test.ts +32 -0
- package/src/meet-creation-policy.ts +12 -0
- package/src/meet-duration.ts +71 -0
- package/src/meet-hosting.test.ts +58 -0
- package/src/meet-hosting.ts +19 -0
- package/src/meeting-invitations.test.ts +102 -0
- package/src/meeting-invitations.ts +81 -0
- package/src/onboarding.ts +2 -0
- package/src/platform-release.test.ts +1 -1
- package/src/platform-release.ts +1 -1
- package/src/portless.ts +2 -0
- package/src/task-helper/task-sort.test.ts +22 -3
- package/src/task-helper/task-sort.ts +33 -3
- package/src/workspace-helper.ts +3 -3
- package/src/workspace-limits.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,52 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.29.0](https://github.com/tutur3u/platform/compare/utils-v0.28.0...utils-v0.29.0) (2026-09-20)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **infrastructure:** coordinate authenticator and meeting leases on Cloudflare ([2582bf2](https://github.com/tutur3u/platform/commit/2582bf2f72670e1f2696de80820eddd118c27d29))
|
|
9
|
+
* **infrastructure:** use Cloudflare for authenticator and meeting coordination ([#5411](https://github.com/tutur3u/platform/issues/5411)) ([a154602](https://github.com/tutur3u/platform/commit/a15460282959831fe617494126d9955b772957aa))
|
|
10
|
+
* **lettin:** add Cloudflare worldbuilding satellite ([0c8cc22](https://github.com/tutur3u/platform/commit/0c8cc22168477440e326bdec08427daf9721d9bf)) ([#5351](https://github.com/tutur3u/platform/issues/5351)) ([2631103](https://github.com/tutur3u/platform/commit/263110318bb9bab6d26a1bdd539fc52a40b25bec))
|
|
11
|
+
* **meet:** publish invite previews and extend paid call limits ([7cf3d6b](https://github.com/tutur3u/platform/commit/7cf3d6b97b0f353e83e894dcc8c83e0d2b6d7a3b))
|
|
12
|
+
* **meet:** turn notes into reviewed calendar and task follow-ups ([#5359](https://github.com/tutur3u/platform/issues/5359)) ([3914a41](https://github.com/tutur3u/platform/commit/3914a41828e6e837add7f55a3f05a5b42e31f98e))
|
|
13
|
+
* **meet:** unlock hosting for verified paid accounts ([00a0168](https://github.com/tutur3u/platform/commit/00a016891845b43ba0297730937fa68d6e465de8))
|
|
14
|
+
* **meet:** unlock paid hosting, longer calls and public previews ([#5364](https://github.com/tutur3u/platform/issues/5364)) ([a6687ff](https://github.com/tutur3u/platform/commit/a6687ffccda096f2ab9da850bf0ddfe8e04c3cf2))
|
|
15
|
+
* **pricing:** add full app comparison and evolving pitch deck ([c967e8d](https://github.com/tutur3u/platform/commit/c967e8d2db75fc65159c7dcb31ed1b27673ae924))
|
|
16
|
+
* **pricing:** compare every app and add an evolving pitch deck ([#5335](https://github.com/tutur3u/platform/issues/5335)) ([962ed75](https://github.com/tutur3u/platform/commit/962ed75e086eb85a06a878720a5d30b747e7ba82))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* **billing:** fail closed on unknown usage and subscription capacity ([ccfcba4](https://github.com/tutur3u/platform/commit/ccfcba46f236981b1d4e59e1c389bee8472b2ca8)) ([#5333](https://github.com/tutur3u/platform/issues/5333)) ([a308a89](https://github.com/tutur3u/platform/commit/a308a8996f17a81c76c8fb0d498a14d8dbf289c3))
|
|
22
|
+
* **deps:** align React types and published consumer ranges ([3612cd8](https://github.com/tutur3u/platform/commit/3612cd81723fb5a6982152688227ed21bc4095fd))
|
|
23
|
+
* **deps:** preserve React peer compatibility and clarify audit ([777b9ed](https://github.com/tutur3u/platform/commit/777b9ed7f42095bb9361e6bed94d989310c222de))
|
|
24
|
+
* **meet:** fail closed and release rejected follow-up receipts ([c13a295](https://github.com/tutur3u/platform/commit/c13a295c1fabcf8605fb05b2d6a5042db3213426))
|
|
25
|
+
* **payment:** protect webhook ingress and restore order cron ([e059fc7](https://github.com/tutur3u/platform/commit/e059fc74f4eff9e938823b1bd02b42093adc632e))
|
|
26
|
+
* **payment:** restore catalog webhook delivery and guard publication ([#5342](https://github.com/tutur3u/platform/issues/5342)) ([b5f61ec](https://github.com/tutur3u/platform/commit/b5f61ec25617b09245c117e47081544edafa454a))
|
|
27
|
+
* **pricing:** reconcile live catalog displays and tier comparisons ([3aa65c2](https://github.com/tutur3u/platform/commit/3aa65c2221d0d099ff5bd50de0c90bb4237d1bbd))
|
|
28
|
+
* **tasks:** align filtered counts and local task visibility ([913388b](https://github.com/tutur3u/platform/commit/913388b78767f927685df695398da030ad73bf82))
|
|
29
|
+
* **tasks:** show unprioritized tasks first across paginated views ([1a5dffc](https://github.com/tutur3u/platform/commit/1a5dffccf45fc1a9c20353d19d843d3c66c22379))
|
|
30
|
+
* **tasks:** surface unprioritized tasks before paginating ([#5315](https://github.com/tutur3u/platform/issues/5315)) ([496627b](https://github.com/tutur3u/platform/commit/496627b85688b0030199310ad21bd21cf2a92f72))
|
|
31
|
+
* **utils:** declare server-only runtime dependency ([172674d](https://github.com/tutur3u/platform/commit/172674d9168092acf1270fb09050a59d9efcbed6))
|
|
32
|
+
|
|
33
|
+
## [0.28.0](https://github.com/tutur3u/platform/compare/utils-v0.27.1...utils-v0.28.0) (2026-09-07)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* **calendar:** add installable Tasks and Calendar with bounded caching ([#5231](https://github.com/tutur3u/platform/issues/5231)) ([7078824](https://github.com/tutur3u/platform/commit/70788244874d811da3e1ce3a9af2c9389739a8c3))
|
|
39
|
+
* **colab:** launch multiplayer prompt workshops on Cloudflare ([7a4a3f9](https://github.com/tutur3u/platform/commit/7a4a3f9b4a12a0d7439cdb296398a21c0cd8939f))
|
|
40
|
+
* **meet:** host on Cloudflare and restrict meeting creation ([#5223](https://github.com/tutur3u/platform/issues/5223)) ([dff093b](https://github.com/tutur3u/platform/commit/dff093b4b6ade986ffd54f3c756d08589e47ad8e))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Bug Fixes
|
|
44
|
+
|
|
45
|
+
* **calendar:** harden PWA lifecycle and cache retention ([14565c0](https://github.com/tutur3u/platform/commit/14565c0106e74b947e152fd8bb787de7f70bc54d))
|
|
46
|
+
* **calendar:** recover failed syncs and redesign calendar views ([7d22948](https://github.com/tutur3u/platform/commit/7d22948095a29d236f698d42dde362fc38311dd0))
|
|
47
|
+
* **calendar:** recover failed syncs and redesign calendar views ([#5230](https://github.com/tutur3u/platform/issues/5230)) ([e52605d](https://github.com/tutur3u/platform/commit/e52605d671f921b1564746afcdedf2478deee78e))
|
|
48
|
+
* **meet:** restrict meeting hosts and restore reliable call media ([2a8a533](https://github.com/tutur3u/platform/commit/2a8a53355181fc8d429653506f7c845c02cf7ae9))
|
|
49
|
+
|
|
3
50
|
## [0.27.1](https://github.com/tutur3u/platform/compare/utils-v0.27.0...utils-v0.27.1) (2026-09-04)
|
|
4
51
|
|
|
5
52
|
|
package/biome.json
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuturuuu/utils",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.29.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tutur3u/platform",
|
|
@@ -20,38 +20,39 @@
|
|
|
20
20
|
"@noble/ciphers": "^2.4.0",
|
|
21
21
|
"@noble/hashes": "^2.4.0",
|
|
22
22
|
"@tanstack/react-query": "^5.102.8",
|
|
23
|
-
"@tiptap/core": "3.31.
|
|
24
|
-
"@tiptap/react": "3.31.
|
|
25
|
-
"@tuturuuu/google": "0.1.
|
|
26
|
-
"@tuturuuu/icons": "0.1.
|
|
27
|
-
"@tuturuuu/internal-api": "0.
|
|
28
|
-
"@tuturuuu/supabase": "0.5.
|
|
23
|
+
"@tiptap/core": "3.31.3",
|
|
24
|
+
"@tiptap/react": "3.31.3",
|
|
25
|
+
"@tuturuuu/google": "0.1.1",
|
|
26
|
+
"@tuturuuu/icons": "0.1.1",
|
|
27
|
+
"@tuturuuu/internal-api": "0.39.0",
|
|
28
|
+
"@tuturuuu/supabase": "0.5.2",
|
|
29
29
|
"@upstash/ratelimit": "^2.0.8",
|
|
30
|
-
"@upstash/redis": "^1.38.
|
|
31
|
-
"cn": "^0.
|
|
30
|
+
"@upstash/redis": "^1.38.4",
|
|
31
|
+
"cn": "^0.3.0",
|
|
32
32
|
"date-fns": "^4.4.0",
|
|
33
33
|
"dayjs": "^1.11.23",
|
|
34
34
|
"diff": "^9.0.0",
|
|
35
|
-
"effect": "^3.22.
|
|
35
|
+
"effect": "^3.22.2",
|
|
36
36
|
"moment": "^2.30.1",
|
|
37
|
-
"next": "^16.3.
|
|
37
|
+
"next": "^16.3.5",
|
|
38
38
|
"prosemirror-model": "^1.25.11",
|
|
39
39
|
"react": "^19.2.8",
|
|
40
40
|
"react-dom": "^19.2.8",
|
|
41
|
+
"server-only": "^0.0.1",
|
|
41
42
|
"uuid": "^14.0.2",
|
|
42
43
|
"y-prosemirror": "^1.3.7",
|
|
43
44
|
"yjs": "^13.6.32",
|
|
44
|
-
"zod": "^4.
|
|
45
|
+
"zod": "^4.6.2"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
|
-
"@tuturuuu/types": "0.
|
|
48
|
+
"@tuturuuu/types": "0.31.0",
|
|
48
49
|
"@tuturuuu/typescript-config": "0.1.2",
|
|
49
50
|
"@types/diff": "^8.0.0",
|
|
50
|
-
"@types/node": "^26.
|
|
51
|
-
"@types/react": "
|
|
51
|
+
"@types/node": "^26.5.1",
|
|
52
|
+
"@types/react": "~19.2.18",
|
|
52
53
|
"@vitejs/plugin-react": "^6.1.1",
|
|
53
54
|
"typescript": "7.0.2",
|
|
54
|
-
"vite": "^8.
|
|
55
|
+
"vite": "^8.3.0",
|
|
55
56
|
"vitest": "^5.0.0"
|
|
56
57
|
},
|
|
57
58
|
"exports": {
|
|
@@ -76,5 +77,5 @@
|
|
|
76
77
|
"./encryption": "./src/encryption/index.ts",
|
|
77
78
|
"./finance": "./src/finance/index.ts"
|
|
78
79
|
},
|
|
79
|
-
"packageManager": "bun@1.4.
|
|
80
|
+
"packageManager": "bun@1.4.1"
|
|
80
81
|
}
|
|
@@ -2231,45 +2231,38 @@ describe('guardApiProxyRequest', () => {
|
|
|
2231
2231
|
expect(blockedResponse?.headers.get('X-RateLimit-Policy')).toBe('cron');
|
|
2232
2232
|
});
|
|
2233
2233
|
|
|
2234
|
-
it('
|
|
2234
|
+
it('rate limits webhook requests even with forged signature headers', async () => {
|
|
2235
2235
|
vi.stubEnv('NODE_ENV', 'production');
|
|
2236
2236
|
vi.stubEnv('POLAR_WEBHOOK_SECRET', 'polar-secret');
|
|
2237
2237
|
vi.stubEnv('UPSTASH_REDIS_REST_URL', 'https://redis.test');
|
|
2238
2238
|
vi.stubEnv('UPSTASH_REDIS_REST_TOKEN', 'token');
|
|
2239
2239
|
mocks.redis.mockReturnValue({});
|
|
2240
|
-
|
|
2241
|
-
const { guardApiProxyRequest, clearApiProxyGuardLimiterCache } =
|
|
2242
|
-
await import('../api-proxy-guard.js');
|
|
2243
|
-
clearApiProxyGuardLimiterCache();
|
|
2244
|
-
|
|
2245
|
-
const allowedResponse = await guardApiProxyRequest(
|
|
2246
|
-
makeRequest('/api/payment/webhooks', 'POST', {
|
|
2247
|
-
'webhook-id': 'id',
|
|
2248
|
-
'webhook-signature': 'sig',
|
|
2249
|
-
'webhook-timestamp': 'ts',
|
|
2250
|
-
}),
|
|
2251
|
-
{ prefixBase: 'proxy:test:api' }
|
|
2252
|
-
);
|
|
2253
|
-
|
|
2254
|
-
expect(allowedResponse).toBeNull();
|
|
2255
|
-
|
|
2256
2240
|
mocks.extractIp.mockReturnValue('1.2.3.4');
|
|
2257
2241
|
mocks.isBlocked.mockResolvedValue(null);
|
|
2258
|
-
mocks.limit.
|
|
2242
|
+
mocks.limit.mockResolvedValue({
|
|
2259
2243
|
success: false,
|
|
2260
|
-
limit:
|
|
2244
|
+
limit: 30,
|
|
2261
2245
|
remaining: 0,
|
|
2262
2246
|
reset: Date.now() + 15_000,
|
|
2263
2247
|
});
|
|
2264
|
-
|
|
2265
|
-
const
|
|
2266
|
-
|
|
2248
|
+
const { guardApiProxyRequest } = await import('../api-proxy-guard.js');
|
|
2249
|
+
const attempts: Record<string, string>[] = [
|
|
2250
|
+
{ 'webhook-id': 'id' },
|
|
2251
|
+
{
|
|
2267
2252
|
'webhook-id': 'id',
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2253
|
+
'webhook-signature': 'sig',
|
|
2254
|
+
'webhook-timestamp': 'ts',
|
|
2255
|
+
},
|
|
2256
|
+
];
|
|
2257
|
+
for (const headers of attempts) {
|
|
2258
|
+
const response = await guardApiProxyRequest(
|
|
2259
|
+
makeRequest('/api/payment/webhooks', 'POST', headers),
|
|
2260
|
+
{ prefixBase: 'proxy:test:api' }
|
|
2261
|
+
);
|
|
2262
|
+
expect(response?.status).toBe(429);
|
|
2263
|
+
expect(response?.headers.get('X-RateLimit-Policy')).toBe('default');
|
|
2264
|
+
}
|
|
2265
|
+
expect(mocks.limit).toHaveBeenCalledTimes(2);
|
|
2273
2266
|
});
|
|
2274
2267
|
|
|
2275
2268
|
it('delegates to emoji validation after rate limiting passes', async () => {
|
|
@@ -292,6 +292,7 @@ describe('Portless app origin registry', () => {
|
|
|
292
292
|
'https://calendar.tuturuuu.localhost',
|
|
293
293
|
'https://chat.tuturuuu.localhost',
|
|
294
294
|
'https://cms.tuturuuu.localhost',
|
|
295
|
+
'https://colab.tuturuuu.localhost',
|
|
295
296
|
'https://contacts.tuturuuu.localhost',
|
|
296
297
|
'https://docs.tuturuuu.localhost',
|
|
297
298
|
'https://drive.tuturuuu.localhost',
|
|
@@ -304,6 +305,7 @@ describe('Portless app origin registry', () => {
|
|
|
304
305
|
'https://infra.tuturuuu.localhost',
|
|
305
306
|
'https://inventory.tuturuuu.localhost',
|
|
306
307
|
'https://learn.tuturuuu.localhost',
|
|
308
|
+
'https://lettin.tuturuuu.localhost',
|
|
307
309
|
'https://mail.tuturuuu.localhost',
|
|
308
310
|
'https://meet.tuturuuu.localhost',
|
|
309
311
|
'https://mind.tuturuuu.localhost',
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
vi.mock('@tuturuuu/supabase/next/server', () => ({
|
|
4
|
+
createAdminClient: vi.fn(),
|
|
5
|
+
createClient: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
resolveWorkspaceIdForPrincipal,
|
|
10
|
+
WorkspaceNotFoundError,
|
|
11
|
+
WorkspaceResolutionError,
|
|
12
|
+
} from '../workspace-helper';
|
|
13
|
+
|
|
14
|
+
it.each([
|
|
15
|
+
{ error: null, Expected: WorkspaceNotFoundError },
|
|
16
|
+
{ error: { message: 'unavailable' }, Expected: WorkspaceResolutionError },
|
|
17
|
+
])(
|
|
18
|
+
'distinguishes a missing personal workspace from a database lookup failure (%s)',
|
|
19
|
+
async ({ error, Expected }) => {
|
|
20
|
+
const query = {
|
|
21
|
+
select: vi.fn().mockReturnThis(),
|
|
22
|
+
eq: vi.fn().mockReturnThis(),
|
|
23
|
+
maybeSingle: vi.fn().mockResolvedValue({ data: null, error }),
|
|
24
|
+
};
|
|
25
|
+
const db = { from: () => query };
|
|
26
|
+
await expect(
|
|
27
|
+
resolveWorkspaceIdForPrincipal({
|
|
28
|
+
authorizationClient: db as never,
|
|
29
|
+
principal: { id: 'actor', email: null },
|
|
30
|
+
wsId: 'personal',
|
|
31
|
+
})
|
|
32
|
+
).rejects.toBeInstanceOf(Expected);
|
|
33
|
+
}
|
|
34
|
+
);
|
package/src/ai-temp-auth.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createHash, randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
+
import { AI_TEMP_AUTH_HEADER } from './ai-temp-auth-constants';
|
|
2
3
|
import { getUpstashRestRedisClient } from './upstash-rest';
|
|
3
4
|
|
|
4
|
-
export
|
|
5
|
+
export { AI_TEMP_AUTH_HEADER } from './ai-temp-auth-constants';
|
|
5
6
|
export const AI_TEMP_AUTH_TTL_SECONDS = 60;
|
|
6
7
|
export const AI_CREDIT_SNAPSHOT_TTL_SECONDS = 15;
|
|
7
8
|
|
package/src/api-proxy-guard.ts
CHANGED
|
@@ -628,14 +628,6 @@ function hasHeaderToken(
|
|
|
628
628
|
return secrets.some((secret) => !!secret && headerValue === secret);
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
-
function hasPolarWebhookSignatureHeaders(headers: Headers) {
|
|
632
|
-
return (
|
|
633
|
-
!!headers.get('webhook-id') &&
|
|
634
|
-
!!headers.get('webhook-timestamp') &&
|
|
635
|
-
!!headers.get('webhook-signature')
|
|
636
|
-
);
|
|
637
|
-
}
|
|
638
|
-
|
|
639
631
|
const DEFAULT_TRUSTED_BYPASS_RULES: TrustedProxyBypassRule[] = [
|
|
640
632
|
{
|
|
641
633
|
matches: (pathname, headers) =>
|
|
@@ -654,13 +646,6 @@ const DEFAULT_TRUSTED_BYPASS_RULES: TrustedProxyBypassRule[] = [
|
|
|
654
646
|
process.env.VERCEL_CRON_SECRET,
|
|
655
647
|
])),
|
|
656
648
|
},
|
|
657
|
-
{
|
|
658
|
-
matches: (pathname, headers) =>
|
|
659
|
-
(pathname === '/api/payment/webhooks' ||
|
|
660
|
-
pathname.startsWith('/api/payment/webhooks/')) &&
|
|
661
|
-
!!process.env.POLAR_WEBHOOK_SECRET &&
|
|
662
|
-
hasPolarWebhookSignatureHeaders(headers),
|
|
663
|
-
},
|
|
664
649
|
{
|
|
665
650
|
matches: (pathname, headers) =>
|
|
666
651
|
(pathname === '/api/v1/webhooks' ||
|
package/src/calendar-utils.ts
CHANGED
|
@@ -15,7 +15,16 @@ export function isAllDayEvent(
|
|
|
15
15
|
const durationMs = end.diff(start, 'millisecond');
|
|
16
16
|
const isMultipleOf24Hours = durationMs % (24 * 60 * 60 * 1000) === 0;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
const spansLocalMidnights =
|
|
19
|
+
start.startOf('day').valueOf() === start.valueOf() &&
|
|
20
|
+
end.startOf('day').valueOf() === end.valueOf();
|
|
21
|
+
const wallDuration =
|
|
22
|
+
durationMs + (end.utcOffset() - start.utcOffset()) * 60_000;
|
|
23
|
+
return (
|
|
24
|
+
durationMs > 0 &&
|
|
25
|
+
(isMultipleOf24Hours ||
|
|
26
|
+
(spansLocalMidnights && wallDuration % (24 * 60 * 60 * 1000) === 0))
|
|
27
|
+
);
|
|
19
28
|
}
|
|
20
29
|
|
|
21
30
|
// Helper function to convert Google Calendar all-day events to proper timezone
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { ComparisonFeature } from './commercial-comparison-types';
|
|
2
|
+
export const features0: ComparisonFeature[] = [
|
|
3
|
+
{
|
|
4
|
+
id: 'attachments',
|
|
5
|
+
app: 'capacity',
|
|
6
|
+
category: 'capacity',
|
|
7
|
+
values: ['10', '10', '10', '10'],
|
|
8
|
+
detail: 'attachment',
|
|
9
|
+
pending: false,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
id: 'publishers',
|
|
13
|
+
app: 'capacity',
|
|
14
|
+
category: 'capacity',
|
|
15
|
+
values: ['8 / 96', '8 / 96', '8 / 96', '8 / 96'],
|
|
16
|
+
detail: 'meetSafety',
|
|
17
|
+
pending: false,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 'duration',
|
|
21
|
+
app: 'capacity',
|
|
22
|
+
category: 'capacity',
|
|
23
|
+
values: ['2 h', '2 h', '2 h', '2 h'],
|
|
24
|
+
detail: 'meetSafety',
|
|
25
|
+
pending: false,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: 'workspaces',
|
|
29
|
+
app: 'capacity',
|
|
30
|
+
category: 'capacity',
|
|
31
|
+
values: ['10', '10', '10', '10'],
|
|
32
|
+
detail: 'account',
|
|
33
|
+
pending: false,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'board',
|
|
37
|
+
app: 'capacity',
|
|
38
|
+
category: 'capacity',
|
|
39
|
+
values: ['10', '50', '50', 'contract'],
|
|
40
|
+
detail: 'board',
|
|
41
|
+
pending: false,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'responses',
|
|
45
|
+
app: 'capacity',
|
|
46
|
+
category: 'capacity',
|
|
47
|
+
values: ['100', '1,000', '5,000', 'contract'],
|
|
48
|
+
detail: 'monthlyAllowance',
|
|
49
|
+
pending: true,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
id: 'automation',
|
|
53
|
+
app: 'capacity',
|
|
54
|
+
category: 'capacity',
|
|
55
|
+
values: ['100', '1,000', '5,000', 'contract'],
|
|
56
|
+
detail: 'monthlyAllowance',
|
|
57
|
+
pending: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'aiCredits',
|
|
61
|
+
app: 'capacity',
|
|
62
|
+
category: 'capacity',
|
|
63
|
+
values: ['1,000', '10,000', '30,000', 'contract'],
|
|
64
|
+
detail: 'aiAllowance',
|
|
65
|
+
pending: true,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: 'storage',
|
|
69
|
+
app: 'capacity',
|
|
70
|
+
category: 'capacity',
|
|
71
|
+
values: ['1 GiB', '20 GiB', '100 GiB', 'contract'],
|
|
72
|
+
detail: 'storageAllowance',
|
|
73
|
+
pending: true,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'guests',
|
|
77
|
+
app: 'capacity',
|
|
78
|
+
category: 'capacity',
|
|
79
|
+
values: ['10', '50', '250', 'contract'],
|
|
80
|
+
detail: 'guests',
|
|
81
|
+
pending: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'members',
|
|
85
|
+
app: 'capacity',
|
|
86
|
+
category: 'capacity',
|
|
87
|
+
values: ['5', 'seats', 'seats', 'contract'],
|
|
88
|
+
detail: 'members',
|
|
89
|
+
pending: true,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'platform_0',
|
|
93
|
+
app: 'platform',
|
|
94
|
+
category: 'work',
|
|
95
|
+
values: ['included', 'included', 'included', 'included'],
|
|
96
|
+
detail: 'standard',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'platform_1',
|
|
100
|
+
app: 'platform',
|
|
101
|
+
category: 'work',
|
|
102
|
+
values: ['included', 'included', 'included', 'included'],
|
|
103
|
+
detail: 'standard',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'platform_2',
|
|
107
|
+
app: 'platform',
|
|
108
|
+
category: 'work',
|
|
109
|
+
values: ['included', 'included', 'included', 'included'],
|
|
110
|
+
detail: 'standard',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'platform_3',
|
|
114
|
+
app: 'platform',
|
|
115
|
+
category: 'work',
|
|
116
|
+
values: ['included', 'included', 'included', 'included'],
|
|
117
|
+
detail: 'standard',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'platform_4',
|
|
121
|
+
app: 'platform',
|
|
122
|
+
category: 'work',
|
|
123
|
+
values: ['included', 'included', 'included', 'included'],
|
|
124
|
+
detail: 'standard',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'platform_5',
|
|
128
|
+
app: 'platform',
|
|
129
|
+
category: 'work',
|
|
130
|
+
values: ['included', 'included', 'included', 'included'],
|
|
131
|
+
detail: 'standard',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'tasks_0',
|
|
135
|
+
app: 'tasks',
|
|
136
|
+
category: 'work',
|
|
137
|
+
values: ['included', 'included', 'included', 'included'],
|
|
138
|
+
detail: 'standard',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: 'tasks_1',
|
|
142
|
+
app: 'tasks',
|
|
143
|
+
category: 'work',
|
|
144
|
+
values: ['included', 'included', 'included', 'included'],
|
|
145
|
+
detail: 'standard',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'tasks_2',
|
|
149
|
+
app: 'tasks',
|
|
150
|
+
category: 'work',
|
|
151
|
+
values: ['included', 'included', 'included', 'included'],
|
|
152
|
+
detail: 'standard',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'tasks_3',
|
|
156
|
+
app: 'tasks',
|
|
157
|
+
category: 'work',
|
|
158
|
+
values: ['included', 'included', 'included', 'included'],
|
|
159
|
+
detail: 'standard',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'tasks_4',
|
|
163
|
+
app: 'tasks',
|
|
164
|
+
category: 'work',
|
|
165
|
+
values: ['included', 'included', 'included', 'included'],
|
|
166
|
+
detail: 'standard',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'calendar_0',
|
|
170
|
+
app: 'calendar',
|
|
171
|
+
category: 'work',
|
|
172
|
+
values: ['included', 'included', 'included', 'included'],
|
|
173
|
+
detail: 'standard',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'calendar_1',
|
|
177
|
+
app: 'calendar',
|
|
178
|
+
category: 'work',
|
|
179
|
+
values: ['included', 'included', 'included', 'included'],
|
|
180
|
+
detail: 'standard',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'calendar_2',
|
|
184
|
+
app: 'calendar',
|
|
185
|
+
category: 'work',
|
|
186
|
+
values: ['included', 'included', 'included', 'included'],
|
|
187
|
+
detail: 'standard',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: 'track_0',
|
|
191
|
+
app: 'track',
|
|
192
|
+
category: 'work',
|
|
193
|
+
values: ['included', 'included', 'included', 'included'],
|
|
194
|
+
detail: 'standard',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'track_1',
|
|
198
|
+
app: 'track',
|
|
199
|
+
category: 'work',
|
|
200
|
+
values: ['included', 'included', 'included', 'included'],
|
|
201
|
+
detail: 'standard',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'track_2',
|
|
205
|
+
app: 'track',
|
|
206
|
+
category: 'work',
|
|
207
|
+
values: ['included', 'included', 'included', 'included'],
|
|
208
|
+
detail: 'standard',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'track_3',
|
|
212
|
+
app: 'track',
|
|
213
|
+
category: 'work',
|
|
214
|
+
values: ['included', 'included', 'included', 'included'],
|
|
215
|
+
detail: 'standard',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'drive_0',
|
|
219
|
+
app: 'drive',
|
|
220
|
+
category: 'work',
|
|
221
|
+
values: ['included', 'included', 'included', 'included'],
|
|
222
|
+
detail: 'storage',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'drive_1',
|
|
226
|
+
app: 'drive',
|
|
227
|
+
category: 'work',
|
|
228
|
+
values: ['included', 'included', 'included', 'included'],
|
|
229
|
+
detail: 'storage',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: 'drive_2',
|
|
233
|
+
app: 'drive',
|
|
234
|
+
category: 'work',
|
|
235
|
+
values: ['included', 'included', 'included', 'included'],
|
|
236
|
+
detail: 'storage',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'drive_3',
|
|
240
|
+
app: 'drive',
|
|
241
|
+
category: 'work',
|
|
242
|
+
values: ['included', 'included', 'included', 'included'],
|
|
243
|
+
detail: 'storage',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: 'chat_0',
|
|
247
|
+
app: 'chat',
|
|
248
|
+
category: 'work',
|
|
249
|
+
values: ['excluded', 'included', 'included', 'included'],
|
|
250
|
+
detail: 'storage',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'chat_1',
|
|
254
|
+
app: 'chat',
|
|
255
|
+
category: 'work',
|
|
256
|
+
values: ['excluded', 'included', 'included', 'included'],
|
|
257
|
+
detail: 'storage',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: 'chat_2',
|
|
261
|
+
app: 'chat',
|
|
262
|
+
category: 'work',
|
|
263
|
+
values: ['excluded', 'included', 'included', 'included'],
|
|
264
|
+
detail: 'storage',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
id: 'meet_0',
|
|
268
|
+
app: 'meet',
|
|
269
|
+
category: 'work',
|
|
270
|
+
values: ['preview', 'preview', 'preview', 'preview'],
|
|
271
|
+
detail: 'standard',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: 'meet_1',
|
|
275
|
+
app: 'meet',
|
|
276
|
+
category: 'work',
|
|
277
|
+
values: ['preview', 'preview', 'preview', 'preview'],
|
|
278
|
+
detail: 'standard',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'meet_2',
|
|
282
|
+
app: 'meet',
|
|
283
|
+
category: 'work',
|
|
284
|
+
values: ['preview', 'preview', 'preview', 'preview'],
|
|
285
|
+
detail: 'standard',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 'meet_3',
|
|
289
|
+
app: 'meet',
|
|
290
|
+
category: 'work',
|
|
291
|
+
values: ['preview', 'preview', 'preview', 'preview'],
|
|
292
|
+
detail: 'standard',
|
|
293
|
+
},
|
|
294
|
+
];
|