@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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=propose-workspace-name.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=slug.test.d.ts.map
@@ -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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"}
@@ -1,22 +1,17 @@
1
1
  export declare const API_VERSION: "v1";
2
- export declare const TIER_LIMITS: {
3
- readonly PERSONAL: {
4
- readonly journals: 5;
5
- readonly entriesPerJournal: 500;
6
- };
7
- readonly PRO: {
8
- readonly journals: 20;
9
- readonly entriesPerJournal: 2000;
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 accounts: "/v1/accounts";
19
- readonly account: (id: string) => string;
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 MAX_JOURNAL_NAME_LENGTH = 255;
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
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,EAAG,IAAa,CAAC;AAEzC,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAId,CAAC;AAEX,eAAO,MAAM,SAAS;;;2BAGP,MAAM;;2BAEN,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,WAAW;;;;;;;;;;;;;CAad,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,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,eAAO,MAAM,kBAAkB,KAAK,CAAC"}
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
- export const TIER_LIMITS = {
3
- PERSONAL: { journals: 5, entriesPerJournal: 500 },
4
- PRO: { journals: 20, entriesPerJournal: 2000 },
5
- ADMIN: { journals: 100, entriesPerJournal: 10000 },
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
- accounts: `/${API_VERSION}/accounts`,
10
- account: (id) => `/${API_VERSION}/accounts/${id}`,
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 MAX_JOURNAL_NAME_LENGTH = 255;
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
@@ -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,WAAW,GAAG;IAC1B,QAAQ,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,iBAAiB,EAAE,GAAG,EAAE;IACjD,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE;IAC9C,KAAK,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE;CACzC,CAAC;AAEX,MAAM,CAAC,MAAM,SAAS,GAAG;IACxB,MAAM,EAAE,IAAI,WAAW,SAAS;IAChC,QAAQ,EAAE,IAAI,WAAW,WAAW;IACpC,OAAO,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,EAAE,EAAE;IACzD,QAAQ,EAAE,IAAI,WAAW,WAAW;IACpC,OAAO,EAAE,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,EAAE,EAAE;IACzD,OAAO,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,SAAS,UAAU;IAC/E,KAAK,EAAE,CAAC,SAAiB,EAAE,KAAsB,EAAE,EAAE,CACpD,IAAI,WAAW,aAAa,kBAAkB,CAAC,SAAS,CAAC,YAAY,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE;IACzG,aAAa,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,SAAS,iBAAiB;IAC5F,OAAO,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,SAAS,UAAU;IAC/E,MAAM,EAAE,CAAC,SAAiB,EAAE,MAAc,EAAE,EAAE,CAC7C,IAAI,WAAW,aAAa,SAAS,YAAY,MAAM,EAAE;IAC1D,WAAW,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,SAAS,cAAc;IACvF,UAAU,EAAE,CAAC,SAAiB,EAAE,YAAoB,EAAE,EAAE,CACvD,IAAI,WAAW,aAAa,SAAS,gBAAgB,YAAY,EAAE;IACpE,gBAAgB,EAAE,IAAI,WAAW,qBAAqB;IACtD,aAAa,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,IAAI,WAAW,aAAa,SAAS,SAAS;IACpF,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,WAAW,WAAW,IAAI,EAAE;IACjE,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,WAAW,WAAW,IAAI,UAAU;CAChE,CAAC;AAEX,MAAM,CAAC,MAAM,WAAW,GAAG;IAC1B,WAAW,EAAE,aAAa;IAC1B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;IACpB,gBAAgB,EAAE,kBAAkB;IACpC,cAAc,EAAE,gBAAgB;IAChC,cAAc,EAAE,gBAAgB;IAChC,kBAAkB,EAAE,oBAAoB;IACxC,kBAAkB,EAAE,oBAAoB;IACxC,2BAA2B,EAAE,6BAA6B;IAC1D,kBAAkB,EAAE,oBAAoB;CAC/B,CAAC;AAIX,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AACtC,MAAM,CAAC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAC3C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAEpC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AACrC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC"}
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, TIER_LIMITS, } from './constants.js';
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 type { AcceptInvitationRequest, Account, AccountType, ApiError, CreateAccountRequest, CreateEntryRequest, CreateInvitationRequest, CreateJournalRequest, Entry, EntrySummary, Invitation, Journal, JournalMember, PaginatedResponse, UpdateEntryRequest, UpdateJournalRequest, } from './types.js';
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
@@ -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,WAAW,GACX,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,YAAY,EACX,uBAAuB,EACvB,OAAO,EACP,WAAW,EACX,QAAQ,EACR,oBAAoB,EACpB,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACpB,MAAM,YAAY,CAAC"}
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, TIER_LIMITS, } from './constants.js';
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,WAAW,GACX,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"}
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
@@ -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"}
@@ -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 AccountType = 'PERSONAL' | 'PRO' | 'ADMIN';
2
- export interface Account {
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
- type: AccountType;
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
- account_id: string;
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 CreateAccountRequest {
115
+ export interface CreateWorkspaceRequest {
61
116
  name?: string | undefined;
62
117
  }
63
118
  export interface CreateJournalRequest {
64
- account_id?: string | undefined;
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
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workjournal/shared",
3
- "version": "0.2.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
  }