@workjournal/shared 0.2.0 → 0.12.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/dist/__tests__/propose-workspace-name.test.d.ts +2 -0
- package/dist/__tests__/propose-workspace-name.test.d.ts.map +1 -0
- package/dist/__tests__/propose-workspace-name.test.js +69 -0
- package/dist/__tests__/propose-workspace-name.test.js.map +1 -0
- package/dist/__tests__/slug.test.d.ts +2 -0
- package/dist/__tests__/slug.test.d.ts.map +1 -0
- package/dist/__tests__/slug.test.js +73 -0
- package/dist/__tests__/slug.test.js.map +1 -0
- package/dist/constants.d.ts +48 -17
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +58 -8
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/propose-workspace-name.d.ts +15 -0
- package/dist/propose-workspace-name.d.ts.map +1 -0
- package/dist/propose-workspace-name.js +34 -0
- package/dist/propose-workspace-name.js.map +1 -0
- package/dist/slug.d.ts +25 -0
- package/dist/slug.d.ts.map +1 -0
- package/dist/slug.js +61 -0
- package/dist/slug.js.map +1 -0
- package/dist/tiers.d.ts +15 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +10 -0
- package/dist/tiers.js.map +1 -0
- package/dist/types.d.ts +63 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propose-workspace-name.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/propose-workspace-name.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { proposeWorkspaceNameFromEmail } from '../propose-workspace-name.js';
|
|
3
|
+
import { MAX_NAME_LENGTH } from '../slug.js';
|
|
4
|
+
describe('proposeWorkspaceNameFromEmail', () => {
|
|
5
|
+
it('turns a plain email into dash-separated form', () => {
|
|
6
|
+
// julius-at-example-com is 21 chars (over MAX_NAME_LENGTH=20), so it
|
|
7
|
+
// trims at the last hyphen in the back half → julius-at-example.
|
|
8
|
+
expect(proposeWorkspaceNameFromEmail('julius@example.com')).toBe('julius-at-example');
|
|
9
|
+
});
|
|
10
|
+
it('turns a short email into the full dash-separated form', () => {
|
|
11
|
+
// Fits within MAX_NAME_LENGTH=20: j-at-e-com = 11 chars.
|
|
12
|
+
expect(proposeWorkspaceNameFromEmail('j@e.com')).toBe('j-at-e-com');
|
|
13
|
+
});
|
|
14
|
+
it('lowercases mixed-case input', () => {
|
|
15
|
+
expect(proposeWorkspaceNameFromEmail('J@E.COM')).toBe('j-at-e-com');
|
|
16
|
+
});
|
|
17
|
+
it('trims surrounding whitespace', () => {
|
|
18
|
+
expect(proposeWorkspaceNameFromEmail(' j@e.com ')).toBe('j-at-e-com');
|
|
19
|
+
});
|
|
20
|
+
it('falls back to my-workspace for empty input', () => {
|
|
21
|
+
expect(proposeWorkspaceNameFromEmail('')).toBe('my-workspace');
|
|
22
|
+
});
|
|
23
|
+
it('falls back to my-workspace for null', () => {
|
|
24
|
+
expect(proposeWorkspaceNameFromEmail(null)).toBe('my-workspace');
|
|
25
|
+
});
|
|
26
|
+
it('falls back to my-workspace for undefined', () => {
|
|
27
|
+
expect(proposeWorkspaceNameFromEmail(undefined)).toBe('my-workspace');
|
|
28
|
+
});
|
|
29
|
+
it('falls back to my-workspace for whitespace-only input', () => {
|
|
30
|
+
expect(proposeWorkspaceNameFromEmail(' ')).toBe('my-workspace');
|
|
31
|
+
});
|
|
32
|
+
it('falls back to my-workspace for input with only special chars', () => {
|
|
33
|
+
// No @, no alphanumerics → nothing survives the slug transform.
|
|
34
|
+
expect(proposeWorkspaceNameFromEmail('!!! ### $$$')).toBe('my-workspace');
|
|
35
|
+
});
|
|
36
|
+
it('falls back to my-workspace for pure emoji input', () => {
|
|
37
|
+
expect(proposeWorkspaceNameFromEmail('🚀🎉')).toBe('my-workspace');
|
|
38
|
+
});
|
|
39
|
+
it('truncates a long email to at most MAX_NAME_LENGTH', () => {
|
|
40
|
+
const result = proposeWorkspaceNameFromEmail('verylongusername@somelongdomain.com');
|
|
41
|
+
expect(result.length).toBeLessThanOrEqual(MAX_NAME_LENGTH);
|
|
42
|
+
});
|
|
43
|
+
it('prefers a hyphen boundary when truncating a long email', () => {
|
|
44
|
+
// verylongusername-at-somelongdomain-com is 39 chars, way past 20.
|
|
45
|
+
// We expect a trim at a hyphen boundary that lives in the back half
|
|
46
|
+
// of the 20-char cap (i.e. index > 10), never a mid-word chop.
|
|
47
|
+
const result = proposeWorkspaceNameFromEmail('verylongusername@somelongdomain.com');
|
|
48
|
+
expect(result.length).toBeLessThanOrEqual(MAX_NAME_LENGTH);
|
|
49
|
+
expect(result.endsWith('-')).toBe(false);
|
|
50
|
+
expect(result.startsWith('verylongusername-at-')).toBe(false);
|
|
51
|
+
// The cap is 20; after hyphen-boundary trim the result must still start
|
|
52
|
+
// with the user portion so it remains recognisable.
|
|
53
|
+
expect(result.startsWith('verylongusername')).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
it('collapses runs of non-alphanumerics to a single dash', () => {
|
|
56
|
+
// first.last@example.com → first-last-at-example-com (25 chars, over
|
|
57
|
+
// cap), capped to first-last-at-exampl, then trimmed at hyphen-13.
|
|
58
|
+
expect(proposeWorkspaceNameFromEmail('first.last@example.com')).toBe('first-last-at');
|
|
59
|
+
});
|
|
60
|
+
it('strips leading and trailing dashes after transform', () => {
|
|
61
|
+
expect(proposeWorkspaceNameFromEmail('@example.com')).toBe('at-example-com');
|
|
62
|
+
});
|
|
63
|
+
it('ASCII-normalises diacritics via shared slugify', () => {
|
|
64
|
+
// `ó` is decomposed by NFKD into `o` + combining mark; slugify strips
|
|
65
|
+
// the combining mark, yielding `jose-at-example-com` (19 chars, fits).
|
|
66
|
+
expect(proposeWorkspaceNameFromEmail('jóse@example.com')).toBe('jose-at-example-com');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=propose-workspace-name.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propose-workspace-name.test.js","sourceRoot":"","sources":["../../src/__tests__/propose-workspace-name.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/slug.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { isReservedSlug, MAX_SLUG_LENGTH, RESERVED_SLUGS, slugify } from '../slug.js';
|
|
3
|
+
describe('slugify', () => {
|
|
4
|
+
it('lowercases and hyphenates basic input', () => {
|
|
5
|
+
expect(slugify('Hello World')).toBe('hello-world');
|
|
6
|
+
});
|
|
7
|
+
it('collapses runs of non-alphanumerics', () => {
|
|
8
|
+
expect(slugify('foo---bar baz')).toBe('foo-bar-baz');
|
|
9
|
+
});
|
|
10
|
+
it('strips leading and trailing hyphens', () => {
|
|
11
|
+
expect(slugify(' ---leading and trailing--- ')).toBe('leading-and-trailing');
|
|
12
|
+
});
|
|
13
|
+
it('strips diacritics via NFKD normalisation', () => {
|
|
14
|
+
expect(slugify('Café Résumé')).toBe('cafe-resume');
|
|
15
|
+
});
|
|
16
|
+
it('handles already-slug input unchanged', () => {
|
|
17
|
+
expect(slugify('already-slug-99')).toBe('already-slug-99');
|
|
18
|
+
});
|
|
19
|
+
it('returns empty string for all-symbols input', () => {
|
|
20
|
+
expect(slugify('!!! @@@ ###')).toBe('');
|
|
21
|
+
});
|
|
22
|
+
it('returns empty string for pure CJK input (no transliteration)', () => {
|
|
23
|
+
expect(slugify('日本語')).toBe('');
|
|
24
|
+
});
|
|
25
|
+
it('returns empty string for pure emoji', () => {
|
|
26
|
+
expect(slugify('🚀🎉')).toBe('');
|
|
27
|
+
});
|
|
28
|
+
it('caps at MAX_SLUG_LENGTH', () => {
|
|
29
|
+
const input = 'a'.repeat(50);
|
|
30
|
+
expect(slugify(input).length).toBeLessThanOrEqual(MAX_SLUG_LENGTH);
|
|
31
|
+
});
|
|
32
|
+
it('prefers a hyphen boundary when capping', () => {
|
|
33
|
+
const input = 'the quick brown fox jumps over the lazy dog';
|
|
34
|
+
const result = slugify(input);
|
|
35
|
+
expect(result.length).toBeLessThanOrEqual(MAX_SLUG_LENGTH);
|
|
36
|
+
expect(result.endsWith('-')).toBe(false);
|
|
37
|
+
// Should not chop mid-word when a usable hyphen exists in the back half
|
|
38
|
+
expect(result.match(/-[^-]*$/)?.[0].slice(1)).not.toMatch(/^(ju|jum|jump)$/);
|
|
39
|
+
});
|
|
40
|
+
it('does not use hyphen boundary when it would leave too little', () => {
|
|
41
|
+
// Hyphen only in the first half → hard-cap without boundary
|
|
42
|
+
const input = `x-${'y'.repeat(40)}`;
|
|
43
|
+
const result = slugify(input);
|
|
44
|
+
expect(result.length).toBeLessThanOrEqual(MAX_SLUG_LENGTH);
|
|
45
|
+
expect(result).toBe(`x-${'y'.repeat(28)}`);
|
|
46
|
+
});
|
|
47
|
+
it('handles underscores as separators', () => {
|
|
48
|
+
expect(slugify('snake_case_name')).toBe('snake-case-name');
|
|
49
|
+
});
|
|
50
|
+
it('keeps digits', () => {
|
|
51
|
+
expect(slugify('Project 2026 v2')).toBe('project-2026-v2');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('isReservedSlug', () => {
|
|
55
|
+
it('returns true for route-colliding slugs', () => {
|
|
56
|
+
expect(isReservedSlug('journals')).toBe(true);
|
|
57
|
+
expect(isReservedSlug('settings')).toBe(true);
|
|
58
|
+
expect(isReservedSlug('new')).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
it('returns false for normal slugs', () => {
|
|
61
|
+
expect(isReservedSlug('my-workspace')).toBe(false);
|
|
62
|
+
expect(isReservedSlug('alice')).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
it('is case-sensitive (slugs are always lowercase post-slugify)', () => {
|
|
65
|
+
expect(isReservedSlug('Settings')).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
it('covers every entry in RESERVED_SLUGS', () => {
|
|
68
|
+
for (const slug of RESERVED_SLUGS) {
|
|
69
|
+
expect(isReservedSlug(slug)).toBe(true);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
//# sourceMappingURL=slug.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.test.js","sourceRoot":"","sources":["../../src/__tests__/slug.test.ts"],"names":[],"mappings":"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"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
export declare const API_VERSION: "v1";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
readonly ADMIN: {
|
|
12
|
-
readonly journals: 100;
|
|
13
|
-
readonly entriesPerJournal: 10000;
|
|
14
|
-
};
|
|
15
|
-
};
|
|
2
|
+
/**
|
|
3
|
+
* @breaking-change-pending
|
|
4
|
+
* The journal/entries/members/invitations/export helpers below still produce
|
|
5
|
+
* UUID-shaped paths from before the slug-routes cutover (PR #185, issue #172).
|
|
6
|
+
* The API now serves only `/v1/workspaces/:workspaceSlug/...` — these
|
|
7
|
+
* helpers will 404 against the live API. CLI (#176) and MCP (#177) update
|
|
8
|
+
* both the call sites and these helpers as part of their own breaking
|
|
9
|
+
* releases. Web (#175) uses `WS_API_PATHS` below instead.
|
|
10
|
+
*/
|
|
16
11
|
export declare const API_PATHS: {
|
|
17
12
|
readonly health: "/v1/health";
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
13
|
+
readonly workspaces: "/v1/workspaces";
|
|
14
|
+
readonly workspace: (id: string) => string;
|
|
20
15
|
readonly journals: "/v1/journals";
|
|
21
16
|
readonly journal: (id: string) => string;
|
|
22
17
|
readonly entries: (journalId: string) => string;
|
|
@@ -31,6 +26,26 @@ export declare const API_PATHS: {
|
|
|
31
26
|
readonly publicJournal: (slug: string) => string;
|
|
32
27
|
readonly publicEntries: (slug: string) => string;
|
|
33
28
|
};
|
|
29
|
+
export declare const WS_API_PATHS: {
|
|
30
|
+
readonly health: "/v1/health";
|
|
31
|
+
readonly workspaces: "/v1/workspaces";
|
|
32
|
+
readonly workspace: (workspaceSlug: string) => string;
|
|
33
|
+
readonly checkWorkspaceSlug: "/v1/workspaces/check-slug";
|
|
34
|
+
readonly journals: (workspaceSlug: string) => string;
|
|
35
|
+
readonly journal: (workspaceSlug: string, journalSlug: string) => string;
|
|
36
|
+
readonly checkJournalSlug: (workspaceSlug: string) => string;
|
|
37
|
+
readonly entries: (workspaceSlug: string, journalSlug: string) => string;
|
|
38
|
+
readonly entry: (workspaceSlug: string, journalSlug: string, index: number | string) => string;
|
|
39
|
+
readonly searchEntries: (workspaceSlug: string, journalSlug: string) => string;
|
|
40
|
+
readonly members: (workspaceSlug: string, journalSlug: string) => string;
|
|
41
|
+
readonly member: (workspaceSlug: string, journalSlug: string, userId: string) => string;
|
|
42
|
+
readonly invitations: (workspaceSlug: string, journalSlug: string) => string;
|
|
43
|
+
readonly invitation: (workspaceSlug: string, journalSlug: string, invitationId: string) => string;
|
|
44
|
+
readonly acceptInvitation: "/v1/invitations/accept";
|
|
45
|
+
readonly exportJournal: (workspaceSlug: string, journalSlug: string) => string;
|
|
46
|
+
readonly publicJournal: (slug: string) => string;
|
|
47
|
+
readonly publicEntries: (slug: string) => string;
|
|
48
|
+
};
|
|
34
49
|
export declare const ERROR_CODES: {
|
|
35
50
|
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
36
51
|
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
@@ -38,18 +53,34 @@ export declare const ERROR_CODES: {
|
|
|
38
53
|
readonly NOT_FOUND: "NOT_FOUND";
|
|
39
54
|
readonly CONFLICT: "CONFLICT";
|
|
40
55
|
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
56
|
+
readonly INVALID_TITLE: "INVALID_TITLE";
|
|
41
57
|
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
42
58
|
readonly EMAIL_MISMATCH: "EMAIL_MISMATCH";
|
|
43
59
|
readonly INVITATION_EXPIRED: "INVITATION_EXPIRED";
|
|
44
60
|
readonly INVITATION_REVOKED: "INVITATION_REVOKED";
|
|
45
61
|
readonly INVITATION_ALREADY_ACCEPTED: "INVITATION_ALREADY_ACCEPTED";
|
|
46
62
|
readonly TIER_LIMIT_REACHED: "TIER_LIMIT_REACHED";
|
|
63
|
+
readonly FEATURE_NOT_IN_TIER: "FEATURE_NOT_IN_TIER";
|
|
64
|
+
readonly NOT_IMPLEMENTED: "NOT_IMPLEMENTED";
|
|
47
65
|
};
|
|
48
66
|
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
49
67
|
export declare const MAX_SUMMARY_LENGTH = 500;
|
|
50
|
-
export declare const
|
|
68
|
+
export declare const MAX_TITLE_LENGTH = 80;
|
|
69
|
+
/** @deprecated Use `MAX_NAME_LENGTH` (20) from `./slug.ts`. Kept as an alias
|
|
70
|
+
* for backward compat until the breaking CLI/MCP releases (#176, #177) drop it. */
|
|
71
|
+
export declare const MAX_JOURNAL_NAME_LENGTH = 20;
|
|
51
72
|
export declare const MAX_EMAIL_LENGTH = 320;
|
|
52
73
|
export declare const DEFAULT_PAGE_LIMIT = 50;
|
|
53
74
|
export declare const MAX_PAGE_LIMIT = 100;
|
|
54
75
|
export declare const PUBLIC_SLUG_LENGTH = 12;
|
|
76
|
+
/**
|
|
77
|
+
* Currently-published versions of the legal documents. Bump these when the
|
|
78
|
+
* landing-page `terms.md` / `privacy.md` frontmatter version changes; the web
|
|
79
|
+
* app compares them against `user_profiles.tos_version_accepted` /
|
|
80
|
+
* `privacy_version_accepted` and prompts for re-acceptance when either is
|
|
81
|
+
* stale. Keep in sync with the `version:` field in the corresponding
|
|
82
|
+
* `apps/landing/src/pages/*.md` frontmatter.
|
|
83
|
+
*/
|
|
84
|
+
export declare const CURRENT_TOS_VERSION: "0.1";
|
|
85
|
+
export declare const CURRENT_PRIVACY_VERSION: "0.1";
|
|
55
86
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,EAAG,IAAa,CAAC;AAEzC
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,EAAG,IAAa,CAAC;AAEzC;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS;;;6BAGL,MAAM;;2BAER,MAAM;kCACC,MAAM;gCACR,MAAM,SAAS,MAAM,GAAG,MAAM;wCAEtB,MAAM;kCACZ,MAAM;iCACP,MAAM,UAAU,MAAM;sCAEjB,MAAM;qCACP,MAAM,gBAAgB,MAAM;;wCAGzB,MAAM;mCACX,MAAM;mCACN,MAAM;CACnB,CAAC;AAeX,eAAO,MAAM,YAAY;;;wCAGG,MAAM;;uCAEP,MAAM;sCACP,MAAM,eAAe,MAAM;+CAClB,MAAM;sCACf,MAAM,eAAe,MAAM;oCAE7B,MAAM,eAAe,MAAM,SAAS,MAAM,GAAG,MAAM;4CAE3C,MAAM,eAAe,MAAM;sCAEjC,MAAM,eAAe,MAAM;qCAE5B,MAAM,eAAe,MAAM,UAAU,MAAM;0CAEtC,MAAM,eAAe,MAAM;yCAE5B,MAAM,eAAe,MAAM,gBAAgB,MAAM;;4CAG9C,MAAM,eAAe,MAAM;mCAEpC,MAAM;mCACN,MAAM;CACnB,CAAC;AAEX,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;CAgBd,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEvE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC;mFACmF;AACnF,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;GAOG;AACH,eAAO,MAAM,mBAAmB,EAAG,KAAc,CAAC;AAClD,eAAO,MAAM,uBAAuB,EAAG,KAAc,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
export const API_VERSION = 'v1';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @breaking-change-pending
|
|
4
|
+
* The journal/entries/members/invitations/export helpers below still produce
|
|
5
|
+
* UUID-shaped paths from before the slug-routes cutover (PR #185, issue #172).
|
|
6
|
+
* The API now serves only `/v1/workspaces/:workspaceSlug/...` — these
|
|
7
|
+
* helpers will 404 against the live API. CLI (#176) and MCP (#177) update
|
|
8
|
+
* both the call sites and these helpers as part of their own breaking
|
|
9
|
+
* releases. Web (#175) uses `WS_API_PATHS` below instead.
|
|
10
|
+
*/
|
|
7
11
|
export const API_PATHS = {
|
|
8
12
|
health: `/${API_VERSION}/health`,
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
workspaces: `/${API_VERSION}/workspaces`,
|
|
14
|
+
workspace: (id) => `/${API_VERSION}/workspaces/${id}`,
|
|
11
15
|
journals: `/${API_VERSION}/journals`,
|
|
12
16
|
journal: (id) => `/${API_VERSION}/journals/${id}`,
|
|
13
17
|
entries: (journalId) => `/${API_VERSION}/journals/${journalId}/entries`,
|
|
@@ -22,6 +26,36 @@ export const API_PATHS = {
|
|
|
22
26
|
publicJournal: (slug) => `/${API_VERSION}/public/${slug}`,
|
|
23
27
|
publicEntries: (slug) => `/${API_VERSION}/public/${slug}/entries`,
|
|
24
28
|
};
|
|
29
|
+
/**
|
|
30
|
+
* Slug-based path helpers for the live API surface. Web uses these directly;
|
|
31
|
+
* CLI and MCP will switch from `API_PATHS` to these as part of their own
|
|
32
|
+
* breaking releases (#176, #177), at which point `API_PATHS` can be removed.
|
|
33
|
+
*
|
|
34
|
+
* `:index` is the per-journal monotonic integer from `entries.index`.
|
|
35
|
+
* URL-encoded for safety even though it's always a plain number.
|
|
36
|
+
*/
|
|
37
|
+
const ws = (workspaceSlug) => `/${API_VERSION}/workspaces/${encodeURIComponent(workspaceSlug)}`;
|
|
38
|
+
const jr = (workspaceSlug, journalSlug) => `${ws(workspaceSlug)}/journals/${encodeURIComponent(journalSlug)}`;
|
|
39
|
+
export const WS_API_PATHS = {
|
|
40
|
+
health: `/${API_VERSION}/health`,
|
|
41
|
+
workspaces: `/${API_VERSION}/workspaces`,
|
|
42
|
+
workspace: (workspaceSlug) => ws(workspaceSlug),
|
|
43
|
+
checkWorkspaceSlug: `/${API_VERSION}/workspaces/check-slug`,
|
|
44
|
+
journals: (workspaceSlug) => `${ws(workspaceSlug)}/journals`,
|
|
45
|
+
journal: (workspaceSlug, journalSlug) => jr(workspaceSlug, journalSlug),
|
|
46
|
+
checkJournalSlug: (workspaceSlug) => `${ws(workspaceSlug)}/journals/check-slug`,
|
|
47
|
+
entries: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/entries`,
|
|
48
|
+
entry: (workspaceSlug, journalSlug, index) => `${jr(workspaceSlug, journalSlug)}/entries/${encodeURIComponent(String(index))}`,
|
|
49
|
+
searchEntries: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/entries/search`,
|
|
50
|
+
members: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/members`,
|
|
51
|
+
member: (workspaceSlug, journalSlug, userId) => `${jr(workspaceSlug, journalSlug)}/members/${encodeURIComponent(userId)}`,
|
|
52
|
+
invitations: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/invitations`,
|
|
53
|
+
invitation: (workspaceSlug, journalSlug, invitationId) => `${jr(workspaceSlug, journalSlug)}/invitations/${encodeURIComponent(invitationId)}`,
|
|
54
|
+
acceptInvitation: `/${API_VERSION}/invitations/accept`,
|
|
55
|
+
exportJournal: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/export`,
|
|
56
|
+
publicJournal: (slug) => `/${API_VERSION}/public/${encodeURIComponent(slug)}`,
|
|
57
|
+
publicEntries: (slug) => `/${API_VERSION}/public/${encodeURIComponent(slug)}/entries`,
|
|
58
|
+
};
|
|
25
59
|
export const ERROR_CODES = {
|
|
26
60
|
BAD_REQUEST: 'BAD_REQUEST',
|
|
27
61
|
UNAUTHORIZED: 'UNAUTHORIZED',
|
|
@@ -29,17 +63,33 @@ export const ERROR_CODES = {
|
|
|
29
63
|
NOT_FOUND: 'NOT_FOUND',
|
|
30
64
|
CONFLICT: 'CONFLICT',
|
|
31
65
|
VALIDATION_ERROR: 'VALIDATION_ERROR',
|
|
66
|
+
INVALID_TITLE: 'INVALID_TITLE',
|
|
32
67
|
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
33
68
|
EMAIL_MISMATCH: 'EMAIL_MISMATCH',
|
|
34
69
|
INVITATION_EXPIRED: 'INVITATION_EXPIRED',
|
|
35
70
|
INVITATION_REVOKED: 'INVITATION_REVOKED',
|
|
36
71
|
INVITATION_ALREADY_ACCEPTED: 'INVITATION_ALREADY_ACCEPTED',
|
|
37
72
|
TIER_LIMIT_REACHED: 'TIER_LIMIT_REACHED',
|
|
73
|
+
FEATURE_NOT_IN_TIER: 'FEATURE_NOT_IN_TIER',
|
|
74
|
+
NOT_IMPLEMENTED: 'NOT_IMPLEMENTED',
|
|
38
75
|
};
|
|
39
76
|
export const MAX_SUMMARY_LENGTH = 500;
|
|
40
|
-
export const
|
|
77
|
+
export const MAX_TITLE_LENGTH = 80;
|
|
78
|
+
/** @deprecated Use `MAX_NAME_LENGTH` (20) from `./slug.ts`. Kept as an alias
|
|
79
|
+
* for backward compat until the breaking CLI/MCP releases (#176, #177) drop it. */
|
|
80
|
+
export const MAX_JOURNAL_NAME_LENGTH = 20;
|
|
41
81
|
export const MAX_EMAIL_LENGTH = 320;
|
|
42
82
|
export const DEFAULT_PAGE_LIMIT = 50;
|
|
43
83
|
export const MAX_PAGE_LIMIT = 100;
|
|
44
84
|
export const PUBLIC_SLUG_LENGTH = 12;
|
|
85
|
+
/**
|
|
86
|
+
* Currently-published versions of the legal documents. Bump these when the
|
|
87
|
+
* landing-page `terms.md` / `privacy.md` frontmatter version changes; the web
|
|
88
|
+
* app compares them against `user_profiles.tos_version_accepted` /
|
|
89
|
+
* `privacy_version_accepted` and prompts for re-acceptance when either is
|
|
90
|
+
* stale. Keep in sync with the `version:` field in the corresponding
|
|
91
|
+
* `apps/landing/src/pages/*.md` frontmatter.
|
|
92
|
+
*/
|
|
93
|
+
export const CURRENT_TOS_VERSION = '0.1';
|
|
94
|
+
export const CURRENT_PRIVACY_VERSION = '0.1';
|
|
45
95
|
//# sourceMappingURL=constants.js.map
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,IAAa,CAAC;AAEzC,MAAM,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export type { ErrorCode } from './constants.js';
|
|
2
|
-
export { API_PATHS, API_VERSION, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH,
|
|
2
|
+
export { API_PATHS, API_VERSION, CURRENT_PRIVACY_VERSION, CURRENT_TOS_VERSION, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH, MAX_TITLE_LENGTH, WS_API_PATHS, } from './constants.js';
|
|
3
3
|
export type { BearerResolution, WrappedPayload } from './opaque-token.js';
|
|
4
4
|
export { AAD_STRING, isOpaqueToken, KEY_LEN, NONCE_LEN, resolveBearer, TAG_LEN, unwrapToken, WJTK_PREFIX, wrapToken, } from './opaque-token.js';
|
|
5
|
-
export
|
|
5
|
+
export { proposeWorkspaceNameFromEmail } from './propose-workspace-name.js';
|
|
6
|
+
export { isReservedSlug, MAX_NAME_LENGTH, MAX_SLUG_LENGTH, RESERVED_SLUGS, slugify, } from './slug.js';
|
|
7
|
+
export type { FeatureFlag } from './tiers.js';
|
|
8
|
+
export { canUseFeature, canUseFeatureForCode } from './tiers.js';
|
|
9
|
+
export type { AcceptInvitationRequest, AcceptLegalRequest, ApiError, CreateEntryRequest, CreateInvitationRequest, CreateJournalRequest, CreateWorkspaceRequest, Entry, EntrySummary, Invitation, Journal, JournalMember, PaginatedResponse, Tier, TierCode, UpdateEntryRequest, UpdateJournalRequest, UserProfile, Workspace, } from './types.js';
|
|
6
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EACN,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EACN,SAAS,EACT,WAAW,EACX,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EACN,UAAU,EACV,aAAa,EACb,OAAO,EACP,SAAS,EACT,aAAa,EACb,OAAO,EACP,WAAW,EACX,WAAW,EACX,SAAS,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACN,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACjE,YAAY,EACX,uBAAuB,EACvB,kBAAkB,EAClB,QAAQ,EACR,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,QAAQ,EACR,kBAAkB,EAClB,oBAAoB,EACpB,WAAW,EACX,SAAS,GACT,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
export { API_PATHS, API_VERSION, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH,
|
|
1
|
+
export { API_PATHS, API_VERSION, CURRENT_PRIVACY_VERSION, CURRENT_TOS_VERSION, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH, MAX_TITLE_LENGTH, WS_API_PATHS, } from './constants.js';
|
|
2
2
|
export { AAD_STRING, isOpaqueToken, KEY_LEN, NONCE_LEN, resolveBearer, TAG_LEN, unwrapToken, WJTK_PREFIX, wrapToken, } from './opaque-token.js';
|
|
3
|
+
export { proposeWorkspaceNameFromEmail } from './propose-workspace-name.js';
|
|
4
|
+
export { isReservedSlug, MAX_NAME_LENGTH, MAX_SLUG_LENGTH, RESERVED_SLUGS, slugify, } from './slug.js';
|
|
5
|
+
export { canUseFeature, canUseFeatureForCode } from './tiers.js';
|
|
3
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,SAAS,EACT,WAAW,EACX,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,YAAY,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACN,UAAU,EACV,aAAa,EACb,OAAO,EACP,SAAS,EACT,aAAa,EACb,OAAO,EACP,WAAW,EACX,WAAW,EACX,SAAS,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EACN,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,GACP,MAAM,WAAW,CAAC;AAEnB,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggest a workspace name from the user's email as a sign-up default.
|
|
3
|
+
* `julius@example.com` → `julius-at-example-com`, truncated to
|
|
4
|
+
* `MAX_NAME_LENGTH` (20 chars) if longer. Always returns a non-empty
|
|
5
|
+
* string even if the input is missing or malformed — callers are using
|
|
6
|
+
* this to prefill a form, so a sensible default beats an empty field.
|
|
7
|
+
*
|
|
8
|
+
* Normalization is delegated to the shared `slugify()` (NFKD unicode
|
|
9
|
+
* normalization, strip combining marks, collapse non-alphanumerics) so
|
|
10
|
+
* diacritic handling stays consistent with slug generation elsewhere.
|
|
11
|
+
* We substitute `@` → `-at-` before the slugify pass so the email
|
|
12
|
+
* separator survives.
|
|
13
|
+
*/
|
|
14
|
+
export declare function proposeWorkspaceNameFromEmail(email: string | null | undefined): string;
|
|
15
|
+
//# sourceMappingURL=propose-workspace-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propose-workspace-name.d.ts","sourceRoot":"","sources":["../src/propose-workspace-name.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAiBtF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { MAX_NAME_LENGTH, slugify } from './slug.js';
|
|
2
|
+
/**
|
|
3
|
+
* Suggest a workspace name from the user's email as a sign-up default.
|
|
4
|
+
* `julius@example.com` → `julius-at-example-com`, truncated to
|
|
5
|
+
* `MAX_NAME_LENGTH` (20 chars) if longer. Always returns a non-empty
|
|
6
|
+
* string even if the input is missing or malformed — callers are using
|
|
7
|
+
* this to prefill a form, so a sensible default beats an empty field.
|
|
8
|
+
*
|
|
9
|
+
* Normalization is delegated to the shared `slugify()` (NFKD unicode
|
|
10
|
+
* normalization, strip combining marks, collapse non-alphanumerics) so
|
|
11
|
+
* diacritic handling stays consistent with slug generation elsewhere.
|
|
12
|
+
* We substitute `@` → `-at-` before the slugify pass so the email
|
|
13
|
+
* separator survives.
|
|
14
|
+
*/
|
|
15
|
+
export function proposeWorkspaceNameFromEmail(email) {
|
|
16
|
+
const raw = (email ?? '').trim();
|
|
17
|
+
if (raw.length === 0)
|
|
18
|
+
return 'my-workspace';
|
|
19
|
+
const dashed = slugify(raw.replace(/@/g, '-at-'));
|
|
20
|
+
if (dashed.length === 0)
|
|
21
|
+
return 'my-workspace';
|
|
22
|
+
if (dashed.length <= MAX_NAME_LENGTH)
|
|
23
|
+
return dashed;
|
|
24
|
+
// Hyphen-boundary trim if possible, else hard cap. Workspace names cap
|
|
25
|
+
// at MAX_NAME_LENGTH (20); slugify's built-in cap is the slug limit (30),
|
|
26
|
+
// so we still need to trim down here.
|
|
27
|
+
const capped = dashed.slice(0, MAX_NAME_LENGTH);
|
|
28
|
+
const lastHyphen = capped.lastIndexOf('-');
|
|
29
|
+
if (lastHyphen > Math.floor(MAX_NAME_LENGTH / 2)) {
|
|
30
|
+
return capped.slice(0, lastHyphen);
|
|
31
|
+
}
|
|
32
|
+
return capped;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=propose-workspace-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"propose-workspace-name.js","sourceRoot":"","sources":["../src/propose-workspace-name.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAAgC;IAC7E,MAAM,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACjC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAE5C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,cAAc,CAAC;IAC/C,IAAI,MAAM,CAAC,MAAM,IAAI,eAAe;QAAE,OAAO,MAAM,CAAC;IAEpD,uEAAuE;IACvE,0EAA0E;IAC1E,sCAAsC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IAChD,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
|
package/dist/slug.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare const MAX_NAME_LENGTH = 20;
|
|
2
|
+
export declare const MAX_SLUG_LENGTH = 30;
|
|
3
|
+
/**
|
|
4
|
+
* Slugs that would collide with route segments or endpoint names. The
|
|
5
|
+
* application layer should reject these (or auto-suffix with -1/-2) rather
|
|
6
|
+
* than letting a user claim `/v1/workspaces/settings` and break routing.
|
|
7
|
+
*/
|
|
8
|
+
export declare const RESERVED_SLUGS: ReadonlySet<string>;
|
|
9
|
+
export declare function isReservedSlug(slug: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Turn a free-form name into a URL-safe slug.
|
|
12
|
+
*
|
|
13
|
+
* - Unicode combining marks are stripped (via NFKD normalize), so `Café` becomes `cafe`.
|
|
14
|
+
* - Non-alphanumeric runs collapse to a single hyphen.
|
|
15
|
+
* - Leading and trailing hyphens are trimmed.
|
|
16
|
+
* - The result is capped at `MAX_SLUG_LENGTH`, preferring a hyphen boundary
|
|
17
|
+
* if one exists in the back half of the capped string.
|
|
18
|
+
*
|
|
19
|
+
* Returns an empty string when the input has no Latin alphanumerics (e.g.
|
|
20
|
+
* pure CJK, pure emoji, or pure punctuation). Callers should treat an empty
|
|
21
|
+
* result as "ask the user for an explicit slug" or pick a deterministic
|
|
22
|
+
* fallback.
|
|
23
|
+
*/
|
|
24
|
+
export declare function slugify(input: string): string;
|
|
25
|
+
//# sourceMappingURL=slug.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.d.ts","sourceRoot":"","sources":["../src/slug.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAmB7C,CAAC;AAEH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAgB7C"}
|
package/dist/slug.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export const MAX_NAME_LENGTH = 20;
|
|
2
|
+
export const MAX_SLUG_LENGTH = 30;
|
|
3
|
+
/**
|
|
4
|
+
* Slugs that would collide with route segments or endpoint names. The
|
|
5
|
+
* application layer should reject these (or auto-suffix with -1/-2) rather
|
|
6
|
+
* than letting a user claim `/v1/workspaces/settings` and break routing.
|
|
7
|
+
*/
|
|
8
|
+
export const RESERVED_SLUGS = new Set([
|
|
9
|
+
'new',
|
|
10
|
+
'settings',
|
|
11
|
+
'journals',
|
|
12
|
+
'entries',
|
|
13
|
+
'share',
|
|
14
|
+
'shares',
|
|
15
|
+
'members',
|
|
16
|
+
'invitations',
|
|
17
|
+
'invites',
|
|
18
|
+
'check-slug',
|
|
19
|
+
'workspaces',
|
|
20
|
+
'workspace',
|
|
21
|
+
'public',
|
|
22
|
+
'api',
|
|
23
|
+
'auth',
|
|
24
|
+
'login',
|
|
25
|
+
'logout',
|
|
26
|
+
'authorize',
|
|
27
|
+
]);
|
|
28
|
+
export function isReservedSlug(slug) {
|
|
29
|
+
return RESERVED_SLUGS.has(slug);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Turn a free-form name into a URL-safe slug.
|
|
33
|
+
*
|
|
34
|
+
* - Unicode combining marks are stripped (via NFKD normalize), so `Café` becomes `cafe`.
|
|
35
|
+
* - Non-alphanumeric runs collapse to a single hyphen.
|
|
36
|
+
* - Leading and trailing hyphens are trimmed.
|
|
37
|
+
* - The result is capped at `MAX_SLUG_LENGTH`, preferring a hyphen boundary
|
|
38
|
+
* if one exists in the back half of the capped string.
|
|
39
|
+
*
|
|
40
|
+
* Returns an empty string when the input has no Latin alphanumerics (e.g.
|
|
41
|
+
* pure CJK, pure emoji, or pure punctuation). Callers should treat an empty
|
|
42
|
+
* result as "ask the user for an explicit slug" or pick a deterministic
|
|
43
|
+
* fallback.
|
|
44
|
+
*/
|
|
45
|
+
export function slugify(input) {
|
|
46
|
+
const normalized = input.normalize('NFKD').replace(/\p{M}+/gu, '');
|
|
47
|
+
const lower = normalized.toLowerCase();
|
|
48
|
+
const hyphenated = lower.replace(/[^a-z0-9]+/g, '-');
|
|
49
|
+
const trimmed = hyphenated.replace(/^-+|-+$/g, '');
|
|
50
|
+
if (trimmed.length <= MAX_SLUG_LENGTH)
|
|
51
|
+
return trimmed;
|
|
52
|
+
const capped = trimmed.slice(0, MAX_SLUG_LENGTH);
|
|
53
|
+
const lastHyphen = capped.lastIndexOf('-');
|
|
54
|
+
// Only use the hyphen boundary if it preserves at least half the cap,
|
|
55
|
+
// otherwise a single short word with trailing junk would get over-truncated.
|
|
56
|
+
if (lastHyphen > Math.floor(MAX_SLUG_LENGTH / 2)) {
|
|
57
|
+
return capped.slice(0, lastHyphen);
|
|
58
|
+
}
|
|
59
|
+
return capped;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=slug.js.map
|
package/dist/slug.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slug.js","sourceRoot":"","sources":["../src/slug.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAC;AAElC;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAwB,IAAI,GAAG,CAAC;IAC1D,KAAK;IACL,UAAU;IACV,UAAU;IACV,SAAS;IACT,OAAO;IACP,QAAQ;IACR,SAAS;IACT,aAAa;IACb,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,KAAK;IACL,MAAM;IACN,OAAO;IACP,QAAQ;IACR,WAAW;CACX,CAAC,CAAC;AAEH,MAAM,UAAU,cAAc,CAAC,IAAY;IAC1C,OAAO,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACpC,MAAM,UAAU,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACvC,MAAM,UAAU,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,MAAM,IAAI,eAAe;QAAE,OAAO,OAAO,CAAC;IAEtD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3C,sEAAsE;IACtE,6EAA6E;IAC7E,IAAI,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC"}
|
package/dist/tiers.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Tier } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Per-feature gate. Tier code alone isn't enough — features can vary per tier
|
|
4
|
+
* beyond the current tier hierarchy, so look up the full tier row and read the
|
|
5
|
+
* flag by name. A `FEATURE_NOT_IN_TIER` error code sits in `ERROR_CODES` for
|
|
6
|
+
* servers to return when this returns false.
|
|
7
|
+
*
|
|
8
|
+
* Never gate by calling `workspace.tier_code === 'PRO'` — the feature matrix
|
|
9
|
+
* is the source of truth; tiers are just a bundle of features.
|
|
10
|
+
*/
|
|
11
|
+
export type FeatureFlag = 'can_export' | 'can_public_share' | 'can_private_share' | 'can_per_share_permissions' | 'can_word_search' | 'can_rag_search' | 'can_webhooks';
|
|
12
|
+
export declare function canUseFeature(tier: Tier, feature: FeatureFlag): boolean;
|
|
13
|
+
/** Safe fallback: treat unknown tier as FREE-equivalent (no premium features). */
|
|
14
|
+
export declare function canUseFeatureForCode(tier: Tier | null | undefined, feature: FeatureFlag): boolean;
|
|
15
|
+
//# sourceMappingURL=tiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../src/tiers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GACpB,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,2BAA2B,GAC3B,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,CAAC;AAElB,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAEvE;AAED,kFAAkF;AAClF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAGjG"}
|
package/dist/tiers.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function canUseFeature(tier, feature) {
|
|
2
|
+
return tier[feature] === true;
|
|
3
|
+
}
|
|
4
|
+
/** Safe fallback: treat unknown tier as FREE-equivalent (no premium features). */
|
|
5
|
+
export function canUseFeatureForCode(tier, feature) {
|
|
6
|
+
if (!tier)
|
|
7
|
+
return false;
|
|
8
|
+
return canUseFeature(tier, feature);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=tiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiers.js","sourceRoot":"","sources":["../src/tiers.ts"],"names":[],"mappings":"AAoBA,MAAM,UAAU,aAAa,CAAC,IAAU,EAAE,OAAoB;IAC7D,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AAC/B,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,oBAAoB,CAAC,IAA6B,EAAE,OAAoB;IACvF,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,OAAO,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,9 +1,61 @@
|
|
|
1
|
-
export type
|
|
2
|
-
|
|
1
|
+
export type TierCode = 'FREE' | 'PLUS' | 'PRO' | 'ADMIN';
|
|
2
|
+
/**
|
|
3
|
+
* Per-user metadata. Currently carries legal-acceptance version cursors; will
|
|
4
|
+
* grow with notification preferences, theme, etc. One row per `auth.users.id`.
|
|
5
|
+
*
|
|
6
|
+
* A NULL `*_version_accepted` means the user has never accepted that document.
|
|
7
|
+
* The acceptance endpoint always writes version + timestamp together, and a
|
|
8
|
+
* CHECK constraint enforces that both are either both NULL or both set.
|
|
9
|
+
*/
|
|
10
|
+
export interface UserProfile {
|
|
11
|
+
user_id: string;
|
|
12
|
+
tos_version_accepted: string | null;
|
|
13
|
+
tos_accepted_at: string | null;
|
|
14
|
+
privacy_version_accepted: string | null;
|
|
15
|
+
privacy_accepted_at: string | null;
|
|
16
|
+
created_at: string;
|
|
17
|
+
updated_at: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Payload for `POST /v1/me/legal/accept`. The API rejects an empty body with
|
|
21
|
+
* 400, so require at least one of the two version fields at compile time as a
|
|
22
|
+
* compile-time mirror of that runtime check.
|
|
23
|
+
*/
|
|
24
|
+
export type AcceptLegalRequest = {
|
|
25
|
+
tos_version: string;
|
|
26
|
+
privacy_version?: string;
|
|
27
|
+
} | {
|
|
28
|
+
tos_version?: string;
|
|
29
|
+
privacy_version: string;
|
|
30
|
+
};
|
|
31
|
+
export interface Workspace {
|
|
3
32
|
id: string;
|
|
4
33
|
owner_id: string;
|
|
5
34
|
name: string;
|
|
6
|
-
|
|
35
|
+
slug: string;
|
|
36
|
+
tier_code: TierCode;
|
|
37
|
+
created_at: string;
|
|
38
|
+
updated_at: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Mirror of the `tiers` table. Booleans/ints/timestamps map to TS as expected.
|
|
42
|
+
* `paddle_product_id` is populated in Phase 5 of the tier rollout; nullable
|
|
43
|
+
* until then.
|
|
44
|
+
*/
|
|
45
|
+
export interface Tier {
|
|
46
|
+
code: TierCode;
|
|
47
|
+
name: string;
|
|
48
|
+
sort_order: number;
|
|
49
|
+
max_journals: number;
|
|
50
|
+
max_entries_per_journal: number;
|
|
51
|
+
can_export: boolean;
|
|
52
|
+
can_public_share: boolean;
|
|
53
|
+
can_private_share: boolean;
|
|
54
|
+
can_per_share_permissions: boolean;
|
|
55
|
+
can_word_search: boolean;
|
|
56
|
+
can_rag_search: boolean;
|
|
57
|
+
can_webhooks: boolean;
|
|
58
|
+
paddle_product_id: string | null;
|
|
7
59
|
created_at: string;
|
|
8
60
|
updated_at: string;
|
|
9
61
|
}
|
|
@@ -11,8 +63,9 @@ export interface Account {
|
|
|
11
63
|
export interface Journal {
|
|
12
64
|
id: string;
|
|
13
65
|
name: string;
|
|
66
|
+
slug: string;
|
|
14
67
|
description: string | null;
|
|
15
|
-
|
|
68
|
+
workspace_id: string;
|
|
16
69
|
is_public: boolean;
|
|
17
70
|
public_slug: string | null;
|
|
18
71
|
created_at: string;
|
|
@@ -31,6 +84,7 @@ export interface Entry {
|
|
|
31
84
|
id: string;
|
|
32
85
|
journal_id: string;
|
|
33
86
|
index: number;
|
|
87
|
+
title: string;
|
|
34
88
|
summary: string;
|
|
35
89
|
what_changed: string;
|
|
36
90
|
client?: string | null;
|
|
@@ -43,6 +97,7 @@ export interface EntrySummary {
|
|
|
43
97
|
id: string;
|
|
44
98
|
journal_id: string;
|
|
45
99
|
index: number;
|
|
100
|
+
title: string;
|
|
46
101
|
summary: string;
|
|
47
102
|
created_at: string;
|
|
48
103
|
}
|
|
@@ -57,11 +112,11 @@ export interface Invitation {
|
|
|
57
112
|
accepted_at: string | null;
|
|
58
113
|
revoked_at: string | null;
|
|
59
114
|
}
|
|
60
|
-
export interface
|
|
115
|
+
export interface CreateWorkspaceRequest {
|
|
61
116
|
name?: string | undefined;
|
|
62
117
|
}
|
|
63
118
|
export interface CreateJournalRequest {
|
|
64
|
-
|
|
119
|
+
workspace_id?: string | undefined;
|
|
65
120
|
name: string;
|
|
66
121
|
description?: string | undefined;
|
|
67
122
|
}
|
|
@@ -71,11 +126,13 @@ export interface UpdateJournalRequest {
|
|
|
71
126
|
is_public?: boolean | undefined;
|
|
72
127
|
}
|
|
73
128
|
export interface CreateEntryRequest {
|
|
129
|
+
title: string;
|
|
74
130
|
summary: string;
|
|
75
131
|
what_changed: string;
|
|
76
132
|
client?: string;
|
|
77
133
|
}
|
|
78
134
|
export interface UpdateEntryRequest {
|
|
135
|
+
title?: string | undefined;
|
|
79
136
|
summary?: string | undefined;
|
|
80
137
|
what_changed?: string | undefined;
|
|
81
138
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAC3B;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD,MAAM,WAAW,SAAS;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,QAAQ,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,IAAI;IACpB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,uBAAuB,EAAE,MAAM,CAAC;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,yBAAyB,EAAE,OAAO,CAAC;IACnC,eAAe,EAAE,OAAO,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,qFAAqF;AACrF,MAAM,WAAW,OAAO;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,4EAA4E;AAC5E,MAAM,WAAW,KAAK;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,kDAAkD;AAClD,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACtC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACpC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC;AAED,MAAM,WAAW,oBAAoB;IACpC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IAClC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,uBAAuB;IACvC,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IACnC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACxB,KAAK,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KAChB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workjournal/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Shared types, constants, and credential helpers for the Workjournal CLI and MCP server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -41,10 +41,12 @@
|
|
|
41
41
|
],
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "22.15.29",
|
|
44
|
-
"typescript": "5.8.3"
|
|
44
|
+
"typescript": "5.8.3",
|
|
45
|
+
"vitest": "4.1.2"
|
|
45
46
|
},
|
|
46
47
|
"scripts": {
|
|
47
48
|
"build": "tsc --project tsconfig.json",
|
|
48
|
-
"check": "tsc --noEmit"
|
|
49
|
+
"check": "tsc --noEmit",
|
|
50
|
+
"test": "vitest run"
|
|
49
51
|
}
|
|
50
52
|
}
|