@basaltkit/comments 1.0.2 → 1.1.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/comments.d.ts +28 -1
- package/dist/comments.js +27 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin.js +5 -2
- package/package.json +10 -6
package/dist/comments.d.ts
CHANGED
|
@@ -12,6 +12,12 @@ export declare class CommentTenantRequiredError extends BasaltError {
|
|
|
12
12
|
export interface CommentNode extends Comment {
|
|
13
13
|
replies: CommentNode[];
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Store key every comment is filed under when the app has no tenancy at all.
|
|
17
|
+
* The {@link CommentStore} contract is tenant-keyed, so a single-tenant app
|
|
18
|
+
* still needs one stable key — it just shouldn't have to invent it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const SINGLE_TENANT_SCOPE = "default";
|
|
15
21
|
export interface CommentsOptions {
|
|
16
22
|
store?: CommentStore;
|
|
17
23
|
hooks?: HookBus;
|
|
@@ -36,11 +42,25 @@ export interface ResourceComments {
|
|
|
36
42
|
* updates (@basaltkit/realtime) and notifications wire up without coupling.
|
|
37
43
|
*/
|
|
38
44
|
export declare class Comments {
|
|
45
|
+
/**
|
|
46
|
+
* Whether the host app registered `@basaltkit/tenancy`. `commentsPlugin`
|
|
47
|
+
* wires this to the container's `'tenancy:active'` metadata marker — a
|
|
48
|
+
* signal, not an import, so this generic package never depends on the
|
|
49
|
+
* opt-in SaaS layer. Defaults to `false` (single-tenant).
|
|
50
|
+
*/
|
|
51
|
+
private readonly tenancyActive;
|
|
39
52
|
private readonly store;
|
|
40
53
|
private readonly hooks;
|
|
41
54
|
private readonly mentionPattern;
|
|
42
55
|
private readonly now;
|
|
43
|
-
constructor(options?: CommentsOptions
|
|
56
|
+
constructor(options?: CommentsOptions,
|
|
57
|
+
/**
|
|
58
|
+
* Whether the host app registered `@basaltkit/tenancy`. `commentsPlugin`
|
|
59
|
+
* wires this to the container's `'tenancy:active'` metadata marker — a
|
|
60
|
+
* signal, not an import, so this generic package never depends on the
|
|
61
|
+
* opt-in SaaS layer. Defaults to `false` (single-tenant).
|
|
62
|
+
*/
|
|
63
|
+
tenancyActive?: () => boolean);
|
|
44
64
|
on(resourceType: string, resourceId: string, tenantId?: string): ResourceComments;
|
|
45
65
|
get(id: string, tenantId?: string): Promise<Comment | null>;
|
|
46
66
|
edit(id: string, body: string, tenantId?: string): Promise<Comment>;
|
|
@@ -50,5 +70,12 @@ export declare class Comments {
|
|
|
50
70
|
private add;
|
|
51
71
|
private mutate;
|
|
52
72
|
private mentions;
|
|
73
|
+
/**
|
|
74
|
+
* The tenant a call is scoped to.
|
|
75
|
+
*
|
|
76
|
+
* With `@basaltkit/tenancy` registered an unresolvable tenant is an error: an
|
|
77
|
+
* unscoped read or write would cross tenants. Without it there is no tenant
|
|
78
|
+
* dimension, so every comment shares {@link SINGLE_TENANT_SCOPE}.
|
|
79
|
+
*/
|
|
53
80
|
private tenant;
|
|
54
81
|
}
|
package/dist/comments.js
CHANGED
|
@@ -13,6 +13,12 @@ export class CommentTenantRequiredError extends BasaltError {
|
|
|
13
13
|
super('COMMENT_TENANT_REQUIRED', 'A tenant is required — pass tenantId or run inside a tenant context.');
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Store key every comment is filed under when the app has no tenancy at all.
|
|
18
|
+
* The {@link CommentStore} contract is tenant-keyed, so a single-tenant app
|
|
19
|
+
* still needs one stable key — it just shouldn't have to invent it.
|
|
20
|
+
*/
|
|
21
|
+
export const SINGLE_TENANT_SCOPE = 'default';
|
|
16
22
|
const buildTree = (comments) => {
|
|
17
23
|
const nodes = new Map(comments.map((c) => [c.id, { ...c, replies: [] }]));
|
|
18
24
|
const roots = [];
|
|
@@ -31,11 +37,20 @@ const buildTree = (comments) => {
|
|
|
31
37
|
* updates (@basaltkit/realtime) and notifications wire up without coupling.
|
|
32
38
|
*/
|
|
33
39
|
export class Comments {
|
|
40
|
+
tenancyActive;
|
|
34
41
|
store;
|
|
35
42
|
hooks;
|
|
36
43
|
mentionPattern;
|
|
37
44
|
now;
|
|
38
|
-
constructor(options = {}
|
|
45
|
+
constructor(options = {},
|
|
46
|
+
/**
|
|
47
|
+
* Whether the host app registered `@basaltkit/tenancy`. `commentsPlugin`
|
|
48
|
+
* wires this to the container's `'tenancy:active'` metadata marker — a
|
|
49
|
+
* signal, not an import, so this generic package never depends on the
|
|
50
|
+
* opt-in SaaS layer. Defaults to `false` (single-tenant).
|
|
51
|
+
*/
|
|
52
|
+
tenancyActive = () => false) {
|
|
53
|
+
this.tenancyActive = tenancyActive;
|
|
39
54
|
this.store = options.store ?? new MemoryCommentStore();
|
|
40
55
|
this.hooks = options.hooks;
|
|
41
56
|
this.mentionPattern = options.mentionPattern ?? /@([\w-]+)/g;
|
|
@@ -115,10 +130,19 @@ export class Comments {
|
|
|
115
130
|
ids.add(match[1]);
|
|
116
131
|
return [...ids];
|
|
117
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* The tenant a call is scoped to.
|
|
135
|
+
*
|
|
136
|
+
* With `@basaltkit/tenancy` registered an unresolvable tenant is an error: an
|
|
137
|
+
* unscoped read or write would cross tenants. Without it there is no tenant
|
|
138
|
+
* dimension, so every comment shares {@link SINGLE_TENANT_SCOPE}.
|
|
139
|
+
*/
|
|
118
140
|
tenant(explicit) {
|
|
119
141
|
const id = explicit ?? tryCtx()?.['tenant']?.id;
|
|
120
|
-
if (
|
|
142
|
+
if (id)
|
|
143
|
+
return id;
|
|
144
|
+
if (this.tenancyActive())
|
|
121
145
|
throw new CommentTenantRequiredError();
|
|
122
|
-
return
|
|
146
|
+
return SINGLE_TENANT_SCOPE;
|
|
123
147
|
}
|
|
124
148
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Comments, CommentNotFoundError, CommentTenantRequiredError, type CommentsOptions, type AddCommentInput, type ResourceComments, type CommentNode, } from './comments.js';
|
|
1
|
+
export { Comments, CommentNotFoundError, CommentTenantRequiredError, SINGLE_TENANT_SCOPE, type CommentsOptions, type AddCommentInput, type ResourceComments, type CommentNode, } from './comments.js';
|
|
2
2
|
export { MemoryCommentStore, type Comment, type CommentStore, type CommentPatch } from './store.js';
|
|
3
3
|
export { commentsPlugin, commentRoutes, COMMENTS, type CommentsPluginOptions } from './plugin.js';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { Comments, CommentNotFoundError, CommentTenantRequiredError, } from './comments.js';
|
|
1
|
+
export { Comments, CommentNotFoundError, CommentTenantRequiredError, SINGLE_TENANT_SCOPE, } from './comments.js';
|
|
2
2
|
export { MemoryCommentStore } from './store.js';
|
|
3
3
|
export { commentsPlugin, commentRoutes, COMMENTS } from './plugin.js';
|
package/dist/plugin.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createToken, ctx, definePlugin, BasaltError } from '@basaltkit/core';
|
|
1
|
+
import { createToken, ctx, definePlugin, BasaltError, ensureMetadata } from '@basaltkit/core';
|
|
2
2
|
import { route } from '@basaltkit/http';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { Comments } from './comments.js';
|
|
@@ -7,7 +7,10 @@ export function commentsPlugin(options = {}) {
|
|
|
7
7
|
return definePlugin({
|
|
8
8
|
name: 'basalt:comments',
|
|
9
9
|
register({ container, hooks }) {
|
|
10
|
-
|
|
10
|
+
// 'tenancy:active' is tenancyPlugin's marker: how a generic package
|
|
11
|
+
// learns the app is multi-tenant without importing @basaltkit/tenancy.
|
|
12
|
+
const metadata = ensureMetadata(container);
|
|
13
|
+
container.singleton(COMMENTS, () => new Comments({ ...options, hooks }, () => metadata.get('tenancy:active').length > 0));
|
|
11
14
|
},
|
|
12
15
|
});
|
|
13
16
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basaltkit/comments",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=22.5.0"
|
|
6
|
+
},
|
|
4
7
|
"description": "Comments for Basalt: per-resource threads with @mentions and resolve/reopen, tenant-scoped, emitting events that bridge to realtime and notifications.",
|
|
5
8
|
"license": "MIT",
|
|
6
9
|
"type": "module",
|
|
10
|
+
"sideEffects": false,
|
|
7
11
|
"exports": {
|
|
8
12
|
".": {
|
|
9
13
|
"types": "./dist/index.d.ts",
|
|
@@ -14,8 +18,8 @@
|
|
|
14
18
|
"dist"
|
|
15
19
|
],
|
|
16
20
|
"dependencies": {
|
|
17
|
-
"@basaltkit/core": "^1.1
|
|
18
|
-
"@basaltkit/http": "^1.
|
|
21
|
+
"@basaltkit/core": "^1.3.1",
|
|
22
|
+
"@basaltkit/http": "^1.14.0"
|
|
19
23
|
},
|
|
20
24
|
"peerDependencies": {
|
|
21
25
|
"zod": "^3.24.0 || ^4.0.0"
|
|
@@ -25,9 +29,9 @@
|
|
|
25
29
|
"typescript": "^7.0.2",
|
|
26
30
|
"vitest": "^4.1.11",
|
|
27
31
|
"zod": "^3.24.0 || ^4.0.0",
|
|
28
|
-
"@basaltkit/auth": "^1.
|
|
29
|
-
"@basaltkit/
|
|
30
|
-
"@basaltkit/
|
|
32
|
+
"@basaltkit/auth": "^1.8.0",
|
|
33
|
+
"@basaltkit/fastify": "^1.8.1",
|
|
34
|
+
"@basaltkit/tenancy": "^1.4.2",
|
|
31
35
|
"@basaltkit/tsconfig": "^0.24.0"
|
|
32
36
|
},
|
|
33
37
|
"publishConfig": {
|