@jchat/addon-sdk 1.0.0-alpha.27 → 1.0.0-alpha.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/cli.d.mts +30 -11
- package/dist/cli.mjs +28 -10
- package/dist/contracts2.d.mts +1 -0
- package/dist/index.mjs +2 -193
- package/dist/index2.d.mts +173 -4
- package/dist/signing.mjs +63 -0
- package/dist/signing2.d.mts +63 -0
- package/dist/src.mjs +467 -0
- package/dist/testing.mjs +1 -143
- package/dist/ui.d.mts +42 -1
- package/dist/ui.mjs +32 -1
- package/package.json +3 -1
package/dist/testing.mjs
CHANGED
|
@@ -1,144 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Creates an in-memory, zero-dependency AddonServerContext mock for testing addon server factories
|
|
4
|
-
* and pipeline hooks offline in CI without requiring live databases or Redis.
|
|
5
|
-
*/
|
|
6
|
-
function createMockAddonServerContext(addonId, options = {}) {
|
|
7
|
-
const { config = {}, user = {}, isEntitled = true } = options;
|
|
8
|
-
const emittedEvents = [];
|
|
9
|
-
const registeredHooks = [];
|
|
10
|
-
const cacheEntries = /* @__PURE__ */ new Map();
|
|
11
|
-
const cacheSets = /* @__PURE__ */ new Map();
|
|
12
|
-
const cacheTtld = /* @__PURE__ */ new Map();
|
|
13
|
-
const rateLimitTimestamps = /* @__PURE__ */ new Map();
|
|
14
|
-
const prefix = `addon:${addonId}:`;
|
|
15
|
-
const cache = {
|
|
16
|
-
async get(key) {
|
|
17
|
-
const fullKey = prefix + key;
|
|
18
|
-
const exp = cacheTtld.get(fullKey);
|
|
19
|
-
if (exp && Date.now() > exp) {
|
|
20
|
-
cacheEntries.delete(fullKey);
|
|
21
|
-
cacheTtld.delete(fullKey);
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
const val = cacheEntries.get(fullKey);
|
|
25
|
-
return val !== void 0 ? val : null;
|
|
26
|
-
},
|
|
27
|
-
async set(key, value, ttlSeconds) {
|
|
28
|
-
const fullKey = prefix + key;
|
|
29
|
-
cacheEntries.set(fullKey, value);
|
|
30
|
-
if (ttlSeconds && ttlSeconds > 0) cacheTtld.set(fullKey, Date.now() + ttlSeconds * 1e3);
|
|
31
|
-
else cacheTtld.delete(fullKey);
|
|
32
|
-
},
|
|
33
|
-
async del(...keys) {
|
|
34
|
-
let count = 0;
|
|
35
|
-
for (const k of keys) {
|
|
36
|
-
const fullKey = prefix + k;
|
|
37
|
-
if (cacheEntries.delete(fullKey)) count++;
|
|
38
|
-
cacheTtld.delete(fullKey);
|
|
39
|
-
cacheSets.delete(fullKey);
|
|
40
|
-
}
|
|
41
|
-
return count;
|
|
42
|
-
},
|
|
43
|
-
async incr(key) {
|
|
44
|
-
const fullKey = prefix + key;
|
|
45
|
-
const next = (Number(cacheEntries.get(fullKey)) || 0) + 1;
|
|
46
|
-
cacheEntries.set(fullKey, next);
|
|
47
|
-
return next;
|
|
48
|
-
},
|
|
49
|
-
async expire(key, ttlSeconds) {
|
|
50
|
-
const fullKey = prefix + key;
|
|
51
|
-
if (!cacheEntries.has(fullKey) && !cacheSets.has(fullKey)) return false;
|
|
52
|
-
cacheTtld.set(fullKey, Date.now() + ttlSeconds * 1e3);
|
|
53
|
-
return true;
|
|
54
|
-
},
|
|
55
|
-
async ttl(key) {
|
|
56
|
-
const fullKey = prefix + key;
|
|
57
|
-
if (!cacheEntries.has(fullKey) && !cacheSets.has(fullKey)) return -2;
|
|
58
|
-
const exp = cacheTtld.get(fullKey);
|
|
59
|
-
if (!exp) return -1;
|
|
60
|
-
const remaining = Math.ceil((exp - Date.now()) / 1e3);
|
|
61
|
-
return remaining > 0 ? remaining : -2;
|
|
62
|
-
},
|
|
63
|
-
async setAdd(key, member) {
|
|
64
|
-
const fullKey = prefix + key;
|
|
65
|
-
let set = cacheSets.get(fullKey);
|
|
66
|
-
if (!set) {
|
|
67
|
-
set = /* @__PURE__ */ new Set();
|
|
68
|
-
cacheSets.set(fullKey, set);
|
|
69
|
-
}
|
|
70
|
-
const initialSize = set.size;
|
|
71
|
-
set.add(member);
|
|
72
|
-
return set.size > initialSize ? 1 : 0;
|
|
73
|
-
},
|
|
74
|
-
async setRemove(key, member) {
|
|
75
|
-
const fullKey = prefix + key;
|
|
76
|
-
const set = cacheSets.get(fullKey);
|
|
77
|
-
if (!set) return 0;
|
|
78
|
-
return set.delete(member) ? 1 : 0;
|
|
79
|
-
},
|
|
80
|
-
async setMembers(key) {
|
|
81
|
-
const fullKey = prefix + key;
|
|
82
|
-
const set = cacheSets.get(fullKey);
|
|
83
|
-
return set ? Array.from(set) : [];
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
const rateLimiter = { async consume(key, opts) {
|
|
87
|
-
const fullKey = `addon:${addonId}:ratelimit:${key}`;
|
|
88
|
-
const now = Date.now();
|
|
89
|
-
const windowStart = now - opts.windowMs;
|
|
90
|
-
const valid = (rateLimitTimestamps.get(fullKey) ?? []).filter((t) => t > windowStart);
|
|
91
|
-
if (valid.length >= opts.limit) {
|
|
92
|
-
rateLimitTimestamps.set(fullKey, valid);
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
valid.push(now);
|
|
96
|
-
rateLimitTimestamps.set(fullKey, valid);
|
|
97
|
-
return true;
|
|
98
|
-
} };
|
|
99
|
-
const defaultUser = {
|
|
100
|
-
id: user.id ?? "mock-user-1",
|
|
101
|
-
name: user.name ?? "Mock User",
|
|
102
|
-
displayName: user.displayName ?? "Mock User",
|
|
103
|
-
avatar: user.avatar ?? "https://example.com/avatar.png",
|
|
104
|
-
gender: user.gender ?? "neutral"
|
|
105
|
-
};
|
|
106
|
-
return {
|
|
107
|
-
context: {
|
|
108
|
-
t: {},
|
|
109
|
-
db: {},
|
|
110
|
-
cache,
|
|
111
|
-
rateLimiter,
|
|
112
|
-
emit: async (event, payload, target) => {
|
|
113
|
-
emittedEvents.push({
|
|
114
|
-
event: `addon:${addonId}:${event}`,
|
|
115
|
-
payload,
|
|
116
|
-
target
|
|
117
|
-
});
|
|
118
|
-
},
|
|
119
|
-
pipeline: { register: (hooks) => {
|
|
120
|
-
registeredHooks.push(hooks);
|
|
121
|
-
} },
|
|
122
|
-
getConfig: async (_targetSiteId) => config,
|
|
123
|
-
getUser: async (userId) => userId === defaultUser.id ? defaultUser : null,
|
|
124
|
-
getUsers: async (userIds) => {
|
|
125
|
-
const map = /* @__PURE__ */ new Map();
|
|
126
|
-
for (const id of userIds) if (id === defaultUser.id) map.set(id, defaultUser);
|
|
127
|
-
return map;
|
|
128
|
-
},
|
|
129
|
-
hasPermission: (reqUser, permission) => {
|
|
130
|
-
return (reqUser?.permissions ?? []).includes(permission);
|
|
131
|
-
},
|
|
132
|
-
verifyEntitlement: (expectedAddonId) => {
|
|
133
|
-
if (expectedAddonId && expectedAddonId !== addonId) return false;
|
|
134
|
-
return isEntitled;
|
|
135
|
-
}
|
|
136
|
-
},
|
|
137
|
-
emittedEvents,
|
|
138
|
-
registeredHooks,
|
|
139
|
-
cacheEntries,
|
|
140
|
-
cacheSets
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
//#endregion
|
|
1
|
+
import { s as createMockAddonServerContext } from "./src.mjs";
|
|
144
2
|
export { createMockAddonServerContext };
|
package/dist/ui.d.mts
CHANGED
|
@@ -1 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
import { d as AddonRequestUser, g as AddonUser } from "./contracts2.mjs";
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { Can } from "@casl/react";
|
|
4
|
+
export * from "@jchat/ui";
|
|
5
|
+
//#region src/ui.d.ts
|
|
6
|
+
export interface AddonCanProps {
|
|
7
|
+
/** The current user attempting the action */
|
|
8
|
+
actor?: (Partial<AddonRequestUser | AddonUser> & {
|
|
9
|
+
permissions?: string[];
|
|
10
|
+
isOwner?: boolean;
|
|
11
|
+
}) | null;
|
|
12
|
+
/** The owner ID of the target resource, if evaluating an ownership-restricted action */
|
|
13
|
+
ownerId?: string;
|
|
14
|
+
/** Permission required if actor is the resource owner (e.g. 'addon.pollwise.close.own') */
|
|
15
|
+
ownPermission?: string;
|
|
16
|
+
/** Permission required if actor is NOT the resource owner (e.g. 'addon.pollwise.close.any') */
|
|
17
|
+
anyPermission?: string;
|
|
18
|
+
/** Simple permission string when not evaluating ownership (e.g. 'addon.pollwise.create') */
|
|
19
|
+
permission?: string;
|
|
20
|
+
/** Fallback content rendered when permission is denied (defaults to null) */
|
|
21
|
+
fallback?: React.ReactNode;
|
|
22
|
+
/** Content rendered when access is granted */
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Declarative component for conditional UI rendering based on RBAC/ABAC permissions and resource
|
|
27
|
+
* ownership.
|
|
28
|
+
*
|
|
29
|
+
* @param actor The current user attempting the action
|
|
30
|
+
* @param ownerId The owner ID of the target resource, if evaluating an ownership-restricted action
|
|
31
|
+
* @param ownPermission Permission required if actor is the resource owner (e.g.
|
|
32
|
+
* 'addon.pollwise.close.own')
|
|
33
|
+
* @param anyPermission Permission required if actor is NOT the resource owner (e.g.
|
|
34
|
+
* 'addon.pollwise.close.any')
|
|
35
|
+
* @param permission Simple permission string when not evaluating ownership (e.g.
|
|
36
|
+
* 'addon.pollwise.create')
|
|
37
|
+
* @param fallback Fallback content rendered when permission is denied (defaults to null)
|
|
38
|
+
* @param children Content rendered when access is granted
|
|
39
|
+
*/
|
|
40
|
+
export declare function AddonCan({ actor, ownerId, ownPermission, anyPermission, permission, fallback, children }: AddonCanProps): React.ReactElement | null;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { Can };
|
package/dist/ui.mjs
CHANGED
|
@@ -1,2 +1,33 @@
|
|
|
1
|
+
import { _ as hasPermission } from "./src.mjs";
|
|
2
|
+
import { Can } from "@casl/react";
|
|
1
3
|
export * from "@jchat/ui";
|
|
2
|
-
|
|
4
|
+
//#region src/ui.ts
|
|
5
|
+
/**
|
|
6
|
+
* Declarative component for conditional UI rendering based on RBAC/ABAC permissions and resource
|
|
7
|
+
* ownership.
|
|
8
|
+
*
|
|
9
|
+
* @param actor The current user attempting the action
|
|
10
|
+
* @param ownerId The owner ID of the target resource, if evaluating an ownership-restricted action
|
|
11
|
+
* @param ownPermission Permission required if actor is the resource owner (e.g.
|
|
12
|
+
* 'addon.pollwise.close.own')
|
|
13
|
+
* @param anyPermission Permission required if actor is NOT the resource owner (e.g.
|
|
14
|
+
* 'addon.pollwise.close.any')
|
|
15
|
+
* @param permission Simple permission string when not evaluating ownership (e.g.
|
|
16
|
+
* 'addon.pollwise.create')
|
|
17
|
+
* @param fallback Fallback content rendered when permission is denied (defaults to null)
|
|
18
|
+
* @param children Content rendered when access is granted
|
|
19
|
+
*/
|
|
20
|
+
function AddonCan({ actor, ownerId, ownPermission, anyPermission, permission, fallback = null, children }) {
|
|
21
|
+
if (!actor) return fallback;
|
|
22
|
+
const perms = actor.permissions ?? [];
|
|
23
|
+
if (actor.isOwner || hasPermission(perms, "*")) return children;
|
|
24
|
+
if (permission && !hasPermission(perms, permission)) return fallback;
|
|
25
|
+
if (ownPermission || anyPermission) {
|
|
26
|
+
if (Boolean(actor.id && ownerId && actor.id === ownerId) && ownPermission && hasPermission(perms, ownPermission)) return children;
|
|
27
|
+
if (anyPermission && hasPermission(perms, anyPermission)) return children;
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
return children;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { AddonCan, Can };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jchat/addon-sdk",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "JChat Addon SDK for building JChat addons.",
|
|
6
6
|
"keywords": [
|
|
@@ -156,6 +156,8 @@
|
|
|
156
156
|
"typecheck": "tsc --noEmit"
|
|
157
157
|
},
|
|
158
158
|
"dependencies": {
|
|
159
|
+
"@casl/ability": "catalog:",
|
|
160
|
+
"@casl/react": "catalog:",
|
|
159
161
|
"lucide-react": "catalog:"
|
|
160
162
|
},
|
|
161
163
|
"devDependencies": {
|