@workjournal/shared 0.2.0 → 0.7.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__/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 +34 -3
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +44 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- 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/types.d.ts +8 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
|
@@ -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
|
@@ -13,10 +13,19 @@ export declare const TIER_LIMITS: {
|
|
|
13
13
|
readonly entriesPerJournal: 10000;
|
|
14
14
|
};
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* @breaking-change-pending
|
|
18
|
+
* The journal/entries/members/invitations/export helpers below still produce
|
|
19
|
+
* UUID-shaped paths from before the slug-routes cutover (PR #185, issue #172).
|
|
20
|
+
* The API now serves only `/v1/workspaces/:workspaceSlug/...` — these
|
|
21
|
+
* helpers will 404 against the live API. CLI (#176) and MCP (#177) update
|
|
22
|
+
* both the call sites and these helpers as part of their own breaking
|
|
23
|
+
* releases. Web (#175) uses `WS_API_PATHS` below instead.
|
|
24
|
+
*/
|
|
16
25
|
export declare const API_PATHS: {
|
|
17
26
|
readonly health: "/v1/health";
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
27
|
+
readonly workspaces: "/v1/workspaces";
|
|
28
|
+
readonly workspace: (id: string) => string;
|
|
20
29
|
readonly journals: "/v1/journals";
|
|
21
30
|
readonly journal: (id: string) => string;
|
|
22
31
|
readonly entries: (journalId: string) => string;
|
|
@@ -31,6 +40,26 @@ export declare const API_PATHS: {
|
|
|
31
40
|
readonly publicJournal: (slug: string) => string;
|
|
32
41
|
readonly publicEntries: (slug: string) => string;
|
|
33
42
|
};
|
|
43
|
+
export declare const WS_API_PATHS: {
|
|
44
|
+
readonly health: "/v1/health";
|
|
45
|
+
readonly workspaces: "/v1/workspaces";
|
|
46
|
+
readonly workspace: (workspaceSlug: string) => string;
|
|
47
|
+
readonly checkWorkspaceSlug: "/v1/workspaces/check-slug";
|
|
48
|
+
readonly journals: (workspaceSlug: string) => string;
|
|
49
|
+
readonly journal: (workspaceSlug: string, journalSlug: string) => string;
|
|
50
|
+
readonly checkJournalSlug: (workspaceSlug: string) => string;
|
|
51
|
+
readonly entries: (workspaceSlug: string, journalSlug: string) => string;
|
|
52
|
+
readonly entry: (workspaceSlug: string, journalSlug: string, index: number | string) => string;
|
|
53
|
+
readonly searchEntries: (workspaceSlug: string, journalSlug: string) => string;
|
|
54
|
+
readonly members: (workspaceSlug: string, journalSlug: string) => string;
|
|
55
|
+
readonly member: (workspaceSlug: string, journalSlug: string, userId: string) => string;
|
|
56
|
+
readonly invitations: (workspaceSlug: string, journalSlug: string) => string;
|
|
57
|
+
readonly invitation: (workspaceSlug: string, journalSlug: string, invitationId: string) => string;
|
|
58
|
+
readonly acceptInvitation: "/v1/invitations/accept";
|
|
59
|
+
readonly exportJournal: (workspaceSlug: string, journalSlug: string) => string;
|
|
60
|
+
readonly publicJournal: (slug: string) => string;
|
|
61
|
+
readonly publicEntries: (slug: string) => string;
|
|
62
|
+
};
|
|
34
63
|
export declare const ERROR_CODES: {
|
|
35
64
|
readonly BAD_REQUEST: "BAD_REQUEST";
|
|
36
65
|
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
@@ -47,7 +76,9 @@ export declare const ERROR_CODES: {
|
|
|
47
76
|
};
|
|
48
77
|
export type ErrorCode = (typeof ERROR_CODES)[keyof typeof ERROR_CODES];
|
|
49
78
|
export declare const MAX_SUMMARY_LENGTH = 500;
|
|
50
|
-
|
|
79
|
+
/** @deprecated Use `MAX_NAME_LENGTH` (20) from `./slug.ts`. Kept as an alias
|
|
80
|
+
* for backward compat until the breaking CLI/MCP releases (#176, #177) drop it. */
|
|
81
|
+
export declare const MAX_JOURNAL_NAME_LENGTH = 20;
|
|
51
82
|
export declare const MAX_EMAIL_LENGTH = 320;
|
|
52
83
|
export declare const DEFAULT_PAGE_LIMIT = 50;
|
|
53
84
|
export declare const MAX_PAGE_LIMIT = 100;
|
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,eAAO,MAAM,WAAW;;;;;;;;;;;;;CAId,CAAC;AAEX,eAAO,MAAM,SAAS;;;
|
|
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;;;;;;;;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;;;;;;;;;;;;;CAad,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEvE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AACtC;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"}
|
package/dist/constants.js
CHANGED
|
@@ -4,10 +4,19 @@ export const TIER_LIMITS = {
|
|
|
4
4
|
PRO: { journals: 20, entriesPerJournal: 2000 },
|
|
5
5
|
ADMIN: { journals: 100, entriesPerJournal: 10000 },
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* @breaking-change-pending
|
|
9
|
+
* The journal/entries/members/invitations/export helpers below still produce
|
|
10
|
+
* UUID-shaped paths from before the slug-routes cutover (PR #185, issue #172).
|
|
11
|
+
* The API now serves only `/v1/workspaces/:workspaceSlug/...` — these
|
|
12
|
+
* helpers will 404 against the live API. CLI (#176) and MCP (#177) update
|
|
13
|
+
* both the call sites and these helpers as part of their own breaking
|
|
14
|
+
* releases. Web (#175) uses `WS_API_PATHS` below instead.
|
|
15
|
+
*/
|
|
7
16
|
export const API_PATHS = {
|
|
8
17
|
health: `/${API_VERSION}/health`,
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
workspaces: `/${API_VERSION}/workspaces`,
|
|
19
|
+
workspace: (id) => `/${API_VERSION}/workspaces/${id}`,
|
|
11
20
|
journals: `/${API_VERSION}/journals`,
|
|
12
21
|
journal: (id) => `/${API_VERSION}/journals/${id}`,
|
|
13
22
|
entries: (journalId) => `/${API_VERSION}/journals/${journalId}/entries`,
|
|
@@ -22,6 +31,36 @@ export const API_PATHS = {
|
|
|
22
31
|
publicJournal: (slug) => `/${API_VERSION}/public/${slug}`,
|
|
23
32
|
publicEntries: (slug) => `/${API_VERSION}/public/${slug}/entries`,
|
|
24
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Slug-based path helpers for the live API surface. Web uses these directly;
|
|
36
|
+
* CLI and MCP will switch from `API_PATHS` to these as part of their own
|
|
37
|
+
* breaking releases (#176, #177), at which point `API_PATHS` can be removed.
|
|
38
|
+
*
|
|
39
|
+
* `:index` is the per-journal monotonic integer from `entries.index`.
|
|
40
|
+
* URL-encoded for safety even though it's always a plain number.
|
|
41
|
+
*/
|
|
42
|
+
const ws = (workspaceSlug) => `/${API_VERSION}/workspaces/${encodeURIComponent(workspaceSlug)}`;
|
|
43
|
+
const jr = (workspaceSlug, journalSlug) => `${ws(workspaceSlug)}/journals/${encodeURIComponent(journalSlug)}`;
|
|
44
|
+
export const WS_API_PATHS = {
|
|
45
|
+
health: `/${API_VERSION}/health`,
|
|
46
|
+
workspaces: `/${API_VERSION}/workspaces`,
|
|
47
|
+
workspace: (workspaceSlug) => ws(workspaceSlug),
|
|
48
|
+
checkWorkspaceSlug: `/${API_VERSION}/workspaces/check-slug`,
|
|
49
|
+
journals: (workspaceSlug) => `${ws(workspaceSlug)}/journals`,
|
|
50
|
+
journal: (workspaceSlug, journalSlug) => jr(workspaceSlug, journalSlug),
|
|
51
|
+
checkJournalSlug: (workspaceSlug) => `${ws(workspaceSlug)}/journals/check-slug`,
|
|
52
|
+
entries: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/entries`,
|
|
53
|
+
entry: (workspaceSlug, journalSlug, index) => `${jr(workspaceSlug, journalSlug)}/entries/${encodeURIComponent(String(index))}`,
|
|
54
|
+
searchEntries: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/entries/search`,
|
|
55
|
+
members: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/members`,
|
|
56
|
+
member: (workspaceSlug, journalSlug, userId) => `${jr(workspaceSlug, journalSlug)}/members/${encodeURIComponent(userId)}`,
|
|
57
|
+
invitations: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/invitations`,
|
|
58
|
+
invitation: (workspaceSlug, journalSlug, invitationId) => `${jr(workspaceSlug, journalSlug)}/invitations/${encodeURIComponent(invitationId)}`,
|
|
59
|
+
acceptInvitation: `/${API_VERSION}/invitations/accept`,
|
|
60
|
+
exportJournal: (workspaceSlug, journalSlug) => `${jr(workspaceSlug, journalSlug)}/export`,
|
|
61
|
+
publicJournal: (slug) => `/${API_VERSION}/public/${encodeURIComponent(slug)}`,
|
|
62
|
+
publicEntries: (slug) => `/${API_VERSION}/public/${encodeURIComponent(slug)}/entries`,
|
|
63
|
+
};
|
|
25
64
|
export const ERROR_CODES = {
|
|
26
65
|
BAD_REQUEST: 'BAD_REQUEST',
|
|
27
66
|
UNAUTHORIZED: 'UNAUTHORIZED',
|
|
@@ -37,7 +76,9 @@ export const ERROR_CODES = {
|
|
|
37
76
|
TIER_LIMIT_REACHED: 'TIER_LIMIT_REACHED',
|
|
38
77
|
};
|
|
39
78
|
export const MAX_SUMMARY_LENGTH = 500;
|
|
40
|
-
|
|
79
|
+
/** @deprecated Use `MAX_NAME_LENGTH` (20) from `./slug.ts`. Kept as an alias
|
|
80
|
+
* for backward compat until the breaking CLI/MCP releases (#176, #177) drop it. */
|
|
81
|
+
export const MAX_JOURNAL_NAME_LENGTH = 20;
|
|
41
82
|
export const MAX_EMAIL_LENGTH = 320;
|
|
42
83
|
export const DEFAULT_PAGE_LIMIT = 50;
|
|
43
84
|
export const MAX_PAGE_LIMIT = 100;
|
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,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,
|
|
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,7 @@
|
|
|
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, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH, TIER_LIMITS, 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 { isReservedSlug, MAX_NAME_LENGTH, MAX_SLUG_LENGTH, RESERVED_SLUGS, slugify, } from './slug.js';
|
|
6
|
+
export type { AcceptInvitationRequest, ApiError, CreateEntryRequest, CreateInvitationRequest, CreateJournalRequest, CreateWorkspaceRequest, Entry, EntrySummary, Invitation, Journal, JournalMember, PaginatedResponse, UpdateEntryRequest, UpdateJournalRequest, Workspace, WorkspaceType, } from './types.js';
|
|
6
7
|
//# 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,WAAW,
|
|
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,EACX,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,EACN,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,GACP,MAAM,WAAW,CAAC;AACnB,YAAY,EACX,uBAAuB,EACvB,QAAQ,EACR,kBAAkB,EAClB,uBAAuB,EACvB,oBAAoB,EACpB,sBAAsB,EACtB,KAAK,EACL,YAAY,EACZ,UAAU,EACV,OAAO,EACP,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,SAAS,EACT,aAAa,GACb,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
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, DEFAULT_PAGE_LIMIT, ERROR_CODES, MAX_EMAIL_LENGTH, MAX_JOURNAL_NAME_LENGTH, MAX_PAGE_LIMIT, MAX_SUMMARY_LENGTH, TIER_LIMITS, 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 { isReservedSlug, MAX_NAME_LENGTH, MAX_SLUG_LENGTH, RESERVED_SLUGS, slugify, } from './slug.js';
|
|
3
4
|
//# 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,
|
|
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,EACX,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,EACN,cAAc,EACd,eAAe,EACf,eAAe,EACf,cAAc,EACd,OAAO,GACP,MAAM,WAAW,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/types.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
export type
|
|
2
|
-
export interface
|
|
1
|
+
export type WorkspaceType = 'PERSONAL' | 'PRO' | 'ADMIN';
|
|
2
|
+
export interface Workspace {
|
|
3
3
|
id: string;
|
|
4
4
|
owner_id: string;
|
|
5
5
|
name: string;
|
|
6
|
-
|
|
6
|
+
slug: string;
|
|
7
|
+
type: WorkspaceType;
|
|
7
8
|
created_at: string;
|
|
8
9
|
updated_at: string;
|
|
9
10
|
}
|
|
@@ -11,8 +12,9 @@ export interface Account {
|
|
|
11
12
|
export interface Journal {
|
|
12
13
|
id: string;
|
|
13
14
|
name: string;
|
|
15
|
+
slug: string;
|
|
14
16
|
description: string | null;
|
|
15
|
-
|
|
17
|
+
workspace_id: string;
|
|
16
18
|
is_public: boolean;
|
|
17
19
|
public_slug: string | null;
|
|
18
20
|
created_at: string;
|
|
@@ -57,11 +59,11 @@ export interface Invitation {
|
|
|
57
59
|
accepted_at: string | null;
|
|
58
60
|
revoked_at: string | null;
|
|
59
61
|
}
|
|
60
|
-
export interface
|
|
62
|
+
export interface CreateWorkspaceRequest {
|
|
61
63
|
name?: string | undefined;
|
|
62
64
|
}
|
|
63
65
|
export interface CreateJournalRequest {
|
|
64
|
-
|
|
66
|
+
workspace_id?: string | undefined;
|
|
65
67
|
name: string;
|
|
66
68
|
description?: string | undefined;
|
|
67
69
|
}
|
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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workjournal/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.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
|
}
|