@blocklet/sdk 1.16.51-beta-20250904-031834-c611f059 → 1.16.51-beta-20250905-051437-fe05adb2
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/lib/middlewares/csrf.d.ts +9 -1
- package/lib/middlewares/csrf.js +21 -10
- package/lib/service/blocklet.d.ts +3 -3
- package/lib/service/blocklet.js +14 -6
- package/lib/util/csrf.d.ts +6 -18
- package/lib/util/csrf.js +14 -131
- package/package.json +10 -10
|
@@ -8,7 +8,15 @@ export interface CSRFOptions {
|
|
|
8
8
|
generateToken?: (req: Request, res: CSRFOptionsResponse) => void | Promise<void>;
|
|
9
9
|
verifyToken?: (req: Request, res: CSRFOptionsResponse) => Promise<void> | void;
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
* @param req
|
|
14
|
+
* @param res
|
|
15
|
+
* @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#employing-hmac-csrf-tokens
|
|
16
|
+
* @note: 我们需要意识到 1. csrf token 不会被攻击者从 cookie 中得到 2. csrf token 的校验是需要当前用户的 login token 的,3. csrf token 不应该有过期时间
|
|
17
|
+
* @returns
|
|
18
|
+
*/
|
|
19
|
+
declare function defaultGenerateToken(req: Request, res: Response): void;
|
|
12
20
|
declare function defaultVerifyToken(req: Request): void;
|
|
13
21
|
export declare function csrf(options?: CSRFOptions): RequestHandler;
|
|
14
22
|
export {};
|
package/lib/middlewares/csrf.js
CHANGED
|
@@ -14,28 +14,39 @@ const wallet = (0, wallet_2.default)();
|
|
|
14
14
|
function printCookieParserNotInstalledWarning() {
|
|
15
15
|
config_1.default.logger.warn('cookie-parser middleware is required for the csrf middleware to work properly.');
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
*
|
|
19
|
+
* @param req
|
|
20
|
+
* @param res
|
|
21
|
+
* @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#employing-hmac-csrf-tokens
|
|
22
|
+
* @note: 我们需要意识到 1. csrf token 不会被攻击者从 cookie 中得到 2. csrf token 的校验是需要当前用户的 login token 的,3. csrf token 不应该有过期时间
|
|
23
|
+
* @returns
|
|
24
|
+
*/
|
|
25
|
+
function defaultGenerateToken(req, res) {
|
|
18
26
|
if (!req.cookies) {
|
|
19
27
|
printCookieParserNotInstalledWarning();
|
|
20
28
|
return;
|
|
21
29
|
}
|
|
22
30
|
if (req.cookies.login_token) {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
const newCsrfToken = (0, csrf_1.sign)(wallet.secretKey, req.cookies.login_token);
|
|
32
|
+
const oldCsrfToken = req.cookies['x-csrf-token'];
|
|
33
|
+
if (newCsrfToken !== oldCsrfToken) {
|
|
34
|
+
res.cookie('x-csrf-token', newCsrfToken, {
|
|
35
|
+
sameSite: 'strict',
|
|
36
|
+
secure: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
28
39
|
}
|
|
29
40
|
}
|
|
30
41
|
function defaultVerifyToken(req) {
|
|
31
42
|
if (!req.cookies) {
|
|
32
43
|
printCookieParserNotInstalledWarning();
|
|
33
44
|
}
|
|
34
|
-
if (req.cookies &&
|
|
45
|
+
if (req.cookies?.login_token &&
|
|
35
46
|
!(0, isEmpty_1.default)(req.cookies['x-csrf-token']) &&
|
|
36
47
|
req.cookies['x-csrf-token'] === req.headers['x-csrf-token']) {
|
|
37
|
-
const
|
|
38
|
-
if ((0, csrf_1.verify)(wallet.secretKey,
|
|
48
|
+
const csrfTokenFromRequest = req.cookies['x-csrf-token'];
|
|
49
|
+
if ((0, csrf_1.verify)(wallet.secretKey, csrfTokenFromRequest, req.cookies.login_token)) {
|
|
39
50
|
return;
|
|
40
51
|
}
|
|
41
52
|
config_1.default.logger.warn('Invalid request: csrf token mismatch', {
|
|
@@ -49,7 +60,7 @@ function defaultVerifyToken(req) {
|
|
|
49
60
|
csrfTokenFromHeader: req.headers['x-csrf-token'],
|
|
50
61
|
});
|
|
51
62
|
}
|
|
52
|
-
throw new Error('Invalid request: csrf token mismatch');
|
|
63
|
+
throw new Error('Invalid request: csrf token mismatch, please refresh the page try again');
|
|
53
64
|
}
|
|
54
65
|
function shouldGenerateToken(req) {
|
|
55
66
|
return ['GET'].includes(req.method);
|
|
@@ -77,9 +77,9 @@ interface BlockletService {
|
|
|
77
77
|
verifyAccessKey(params: OmitTeamDid<Client.RequestVerifyAccessKeyInput>): Promise<Client.ResponseAccessKey>;
|
|
78
78
|
getAccessKeys(params: OmitTeamDid<Client.RequestAccessKeysInput>): Promise<Client.ResponseAccessKeys>;
|
|
79
79
|
getAccessKey(params: OmitTeamDid<Client.RequestAccessKeyInput>): Promise<Client.ResponseAccessKey>;
|
|
80
|
-
getUserFollowers(args
|
|
81
|
-
getUserFollowing(args
|
|
82
|
-
getUserFollowStats(args
|
|
80
|
+
getUserFollowers(args: OmitTeamDid<Client.RequestUserFollowsInput>, options: RequestHeaders): Promise<Client.ResponseUserFollows>;
|
|
81
|
+
getUserFollowing(args: OmitTeamDid<Client.RequestUserFollowsInput>, options: RequestHeaders): Promise<Client.ResponseUserFollows>;
|
|
82
|
+
getUserFollowStats(args: OmitTeamDid<Client.RequestUserFollowsStatsInput>, options: RequestHeaders): Promise<Client.ResponseFollowStats>;
|
|
83
83
|
checkFollowing(args: OmitTeamDid<Client.RequestCheckFollowingInput>): Promise<Client.ResponseCheckFollowing>;
|
|
84
84
|
followUser(args: OmitTeamDid<Client.RequestFollowUserActionInput>): Promise<Client.GeneralResponse>;
|
|
85
85
|
unfollowUser(args: OmitTeamDid<Client.RequestFollowUserActionInput>): Promise<Client.GeneralResponse>;
|
package/lib/service/blocklet.js
CHANGED
|
@@ -81,6 +81,9 @@ class BlockletClient extends server_js_1.default {
|
|
|
81
81
|
return headers;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
+
// 通用的客户端调用方法,用于减少代码重复
|
|
85
|
+
// 需要 cookie 验证的方法列表
|
|
86
|
+
const AUTH_REQUIRED_METHODS = Object.freeze(['getUserFollowers', 'getUserFollowing', 'getUserFollowStats']);
|
|
84
87
|
// Blocklet 相关的功能 API 都在这里
|
|
85
88
|
// core/state/lib/api/team.js L42
|
|
86
89
|
// 所有可配置调用的函数在这里,如果需要额外增加,则需要在这里新增对应的函数
|
|
@@ -302,20 +305,25 @@ class BlockletService {
|
|
|
302
305
|
throw new Error((0, error_1.formatError)(err));
|
|
303
306
|
}
|
|
304
307
|
};
|
|
305
|
-
|
|
306
|
-
const callClientMethod = async (methodName, args) => {
|
|
308
|
+
const callClientMethod = async (methodName, args, options) => {
|
|
307
309
|
try {
|
|
310
|
+
// 检查是否为需要认证的方法
|
|
311
|
+
if (AUTH_REQUIRED_METHODS.includes(methodName)) {
|
|
312
|
+
if (!options?.headers?.cookie) {
|
|
313
|
+
throw new Error('Missing required authentication cookie in request headers');
|
|
314
|
+
}
|
|
315
|
+
}
|
|
308
316
|
const clientMethod = client[methodName];
|
|
309
|
-
const res = await clientMethod({ input: { teamDid, ...args } });
|
|
317
|
+
const res = await clientMethod({ input: { teamDid, ...args } }, options);
|
|
310
318
|
return res;
|
|
311
319
|
}
|
|
312
320
|
catch (err) {
|
|
313
321
|
throw new Error((0, error_1.formatError)(err)); // 这几个错误消息需要 format
|
|
314
322
|
}
|
|
315
323
|
};
|
|
316
|
-
this.getUserFollowers = (args) => callClientMethod('getUserFollowers', args);
|
|
317
|
-
this.getUserFollowing = (args) => callClientMethod('getUserFollowing', args);
|
|
318
|
-
this.getUserFollowStats = (args) => callClientMethod('getUserFollowStats', args);
|
|
324
|
+
this.getUserFollowers = (args, options) => callClientMethod('getUserFollowers', args, options);
|
|
325
|
+
this.getUserFollowing = (args, options) => callClientMethod('getUserFollowing', args, options);
|
|
326
|
+
this.getUserFollowStats = (args, options) => callClientMethod('getUserFollowStats', args, options);
|
|
319
327
|
this.checkFollowing = (args) => callClientMethod('checkFollowing', args);
|
|
320
328
|
this.followUser = (args) => callClientMethod('followUser', args);
|
|
321
329
|
this.unfollowUser = (args) => callClientMethod('unfollowUser', args);
|
package/lib/util/csrf.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LiteralUnion } from 'type-fest';
|
|
2
|
+
export declare function hmac(secretKey: string, message: string, algorithm?: LiteralUnion<'md5' | 'sha256', string>): string;
|
|
1
3
|
/**
|
|
2
4
|
* 生成 CSRF Token
|
|
3
5
|
* @param secretKey 服务器密钥(必填,需保密)
|
|
@@ -5,25 +7,11 @@
|
|
|
5
7
|
* @returns 签名后的Token字符串
|
|
6
8
|
*/
|
|
7
9
|
export declare function sign(secretKey: string, loginToken: string): string;
|
|
8
|
-
/**
|
|
9
|
-
* 带缓存的 CSRF Token 生成函数
|
|
10
|
-
* 半小时内,相同的 secretKey 和 loginToken 会返回缓存的结果
|
|
11
|
-
* @param secretKey 服务器密钥(必填,需保密)
|
|
12
|
-
* @param loginToken 登录token
|
|
13
|
-
* @returns 签名后的Token字符串
|
|
14
|
-
*/
|
|
15
|
-
export declare function signWithCache(secretKey: string, loginToken: string): Promise<string>;
|
|
16
|
-
/**
|
|
17
|
-
* 清除缓存(主要用于测试)
|
|
18
|
-
* @param secretKey 可选,指定清除特定密钥的缓存
|
|
19
|
-
* @param loginToken 可选,指定清除特定登录token的缓存
|
|
20
|
-
*/
|
|
21
|
-
export declare function clearSignCache(secretKey?: string, loginToken?: string): Promise<void>;
|
|
22
10
|
/**
|
|
23
11
|
* 验证CSRF Token有效性
|
|
24
|
-
* @param
|
|
25
|
-
* @param
|
|
26
|
-
* @param expiresIn 有效期(需与生成时一致,默认
|
|
12
|
+
* @param secretKey 服务器密钥(需与生成时一致)
|
|
13
|
+
* @param csrfTokenFromRequest 待验证的Token
|
|
14
|
+
* @param expiresIn 有效期(需与生成时一致,默认5400)
|
|
27
15
|
* @returns 是否有效(布尔值)
|
|
28
16
|
*/
|
|
29
|
-
export declare function verify(
|
|
17
|
+
export declare function verify(secretKey: string, csrfTokenFromRequest: string, loginToken: string): boolean;
|
package/lib/util/csrf.js
CHANGED
|
@@ -1,32 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hmac = hmac;
|
|
3
4
|
exports.sign = sign;
|
|
4
|
-
exports.signWithCache = signWithCache;
|
|
5
|
-
exports.clearSignCache = clearSignCache;
|
|
6
5
|
exports.verify = verify;
|
|
7
6
|
const crypto_1 = require("crypto");
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const RANDOM_LENGTH = 4; // 随机数长度(4位)
|
|
13
|
-
const PREFIX_LENGTH = TIMESTAMP_LENGTH + RANDOM_LENGTH; // 前缀总长度(14位)
|
|
14
|
-
const MIN_TOKEN_LENGTH = PREFIX_LENGTH + 1 + 40; // 最小Token长度(14+1+40)
|
|
15
|
-
const HASH_ALGORITHM = 'sha256'; // 哈希算法
|
|
16
|
-
const CACHE_DURATION = 30 * 60 * 1000; // 缓存时长:30分钟(毫秒)
|
|
17
|
-
// 创建缓存实例
|
|
18
|
-
const cache = new lru_cache_1.LRUCache({ max: 100 * 1000, ttl: CACHE_DURATION });
|
|
19
|
-
/**
|
|
20
|
-
* Base64URL编码去除补位符
|
|
21
|
-
*/
|
|
22
|
-
const trimBase64UrlPadding = (str) => str.replace(/=+$/, '');
|
|
23
|
-
/**
|
|
24
|
-
* Base64URL编码补全补位符
|
|
25
|
-
*/
|
|
26
|
-
const padBase64Url = (str) => {
|
|
27
|
-
const padLength = (4 - (str.length % 4)) % 4;
|
|
28
|
-
return str.padEnd(str.length + padLength, '=');
|
|
29
|
-
};
|
|
7
|
+
function hmac(secretKey, message, algorithm = 'md5') {
|
|
8
|
+
const hmacFunc = (0, crypto_1.createHmac)(algorithm, secretKey);
|
|
9
|
+
return hmacFunc.update(message).digest('base64url');
|
|
10
|
+
}
|
|
30
11
|
/**
|
|
31
12
|
* 生成 CSRF Token
|
|
32
13
|
* @param secretKey 服务器密钥(必填,需保密)
|
|
@@ -34,116 +15,18 @@ const padBase64Url = (str) => {
|
|
|
34
15
|
* @returns 签名后的Token字符串
|
|
35
16
|
*/
|
|
36
17
|
function sign(secretKey, loginToken) {
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
const salt = loginToken.slice(-4).padStart(4, '0');
|
|
41
|
-
// 组合前缀:时间戳 + 随机数
|
|
42
|
-
const prefix = `${timestampStr}${salt}`;
|
|
43
|
-
// 生成HMAC签名并处理编码
|
|
44
|
-
const signature = (0, crypto_1.createHmac)(HASH_ALGORITHM, secretKey).update(prefix).digest('base64url');
|
|
45
|
-
// 拼接前缀和签名,使用.分隔(移除补位符减少长度)
|
|
46
|
-
return `${prefix}.${trimBase64UrlPadding(signature)}`;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* 生成缓存键
|
|
50
|
-
* @param secretKey 服务器密钥
|
|
51
|
-
* @param loginToken 登录token
|
|
52
|
-
* @returns 缓存键
|
|
53
|
-
*/
|
|
54
|
-
function generateCacheKey(secretKey, loginToken) {
|
|
55
|
-
// 使用哈希值作为键的一部分,避免敏感信息直接存储在键中
|
|
56
|
-
const keyHash = (0, crypto_1.createHmac)('md5', 'cache-key-salt').update(`${secretKey}:${loginToken}`).digest('hex');
|
|
57
|
-
return `csrf:${keyHash}`;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* 带缓存的 CSRF Token 生成函数
|
|
61
|
-
* 半小时内,相同的 secretKey 和 loginToken 会返回缓存的结果
|
|
62
|
-
* @param secretKey 服务器密钥(必填,需保密)
|
|
63
|
-
* @param loginToken 登录token
|
|
64
|
-
* @returns 签名后的Token字符串
|
|
65
|
-
*/
|
|
66
|
-
async function signWithCache(secretKey, loginToken) {
|
|
67
|
-
const cacheKey = generateCacheKey(secretKey, loginToken);
|
|
68
|
-
// 尝试从缓存获取
|
|
69
|
-
const cachedToken = await cache.get(cacheKey);
|
|
70
|
-
if (cachedToken && typeof cachedToken === 'string') {
|
|
71
|
-
return cachedToken;
|
|
72
|
-
}
|
|
73
|
-
// 缓存未命中,生成新的token
|
|
74
|
-
const newToken = sign(secretKey, loginToken);
|
|
75
|
-
// 存储到缓存
|
|
76
|
-
await cache.set(cacheKey, newToken);
|
|
77
|
-
return newToken;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* 清除缓存(主要用于测试)
|
|
81
|
-
* @param secretKey 可选,指定清除特定密钥的缓存
|
|
82
|
-
* @param loginToken 可选,指定清除特定登录token的缓存
|
|
83
|
-
*/
|
|
84
|
-
async function clearSignCache(secretKey, loginToken) {
|
|
85
|
-
if (secretKey && loginToken) {
|
|
86
|
-
const cacheKey = generateCacheKey(secretKey, loginToken);
|
|
87
|
-
await cache.delete(cacheKey);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
await cache.clear();
|
|
91
|
-
}
|
|
18
|
+
const xCsrfTokenMd5 = hmac(secretKey, loginToken);
|
|
19
|
+
const xCsrfTokenSigned = hmac(secretKey, xCsrfTokenMd5, 'sha256');
|
|
20
|
+
return [xCsrfTokenMd5, xCsrfTokenSigned].join('.');
|
|
92
21
|
}
|
|
93
22
|
/**
|
|
94
23
|
* 验证CSRF Token有效性
|
|
95
|
-
* @param
|
|
96
|
-
* @param
|
|
97
|
-
* @param expiresIn 有效期(需与生成时一致,默认
|
|
24
|
+
* @param secretKey 服务器密钥(需与生成时一致)
|
|
25
|
+
* @param csrfTokenFromRequest 待验证的Token
|
|
26
|
+
* @param expiresIn 有效期(需与生成时一致,默认5400)
|
|
98
27
|
* @returns 是否有效(布尔值)
|
|
99
28
|
*/
|
|
100
|
-
function verify(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
config_1.logger.warn('CSRF token invalid: format error', {
|
|
104
|
-
token: typeof token === 'string' ? `${token.slice(0, 20)}...` : token,
|
|
105
|
-
});
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
// 拆分前缀和签名部分(以.分隔)
|
|
109
|
-
const dotIndex = token.indexOf('.', PREFIX_LENGTH);
|
|
110
|
-
if (dotIndex === -1 || dotIndex !== PREFIX_LENGTH) {
|
|
111
|
-
config_1.logger.warn('CSRF token invalid: separator error', { token: `${token.slice(0, 20)}...` });
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
const prefix = token.slice(0, PREFIX_LENGTH);
|
|
115
|
-
const signatureB64Url = token.slice(PREFIX_LENGTH + 1);
|
|
116
|
-
// 提取时间戳部分(前10位)
|
|
117
|
-
const timestampStr = prefix.slice(0, TIMESTAMP_LENGTH);
|
|
118
|
-
// 时间戳格式校验
|
|
119
|
-
const timestamp = parseInt(timestampStr, 10);
|
|
120
|
-
if (Number.isNaN(timestamp)) {
|
|
121
|
-
config_1.logger.warn('CSRF token invalid: timestamp error', { token: `${token.slice(0, 20)}...` });
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
// 有效期校验
|
|
125
|
-
const currentTime = Math.floor(Date.now() / 1000);
|
|
126
|
-
if (currentTime - timestamp > expiresIn) {
|
|
127
|
-
config_1.logger.warn('CSRF token invalid: expired', { token: `${token.slice(0, 20)}...` });
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
// 签名校验
|
|
131
|
-
try {
|
|
132
|
-
// 补全Base64URL补位符并解码
|
|
133
|
-
const paddedSignature = padBase64Url(signatureB64Url);
|
|
134
|
-
const receivedSignature = Buffer.from(paddedSignature, 'base64url');
|
|
135
|
-
// 重新计算预期签名(使用完整的前缀)
|
|
136
|
-
const expectedSignature = (0, crypto_1.createHmac)(HASH_ALGORITHM, secret).update(prefix).digest();
|
|
137
|
-
// 时序安全的对比(防止时序攻击)
|
|
138
|
-
const isValid = (0, crypto_1.timingSafeEqual)(receivedSignature, expectedSignature);
|
|
139
|
-
if (!isValid) {
|
|
140
|
-
config_1.logger.warn('CSRF token invalid: signature error', { token: `${token.slice(0, 20)}...` });
|
|
141
|
-
}
|
|
142
|
-
return isValid;
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
// 任何解码或计算异常都视为无效
|
|
146
|
-
config_1.logger.warn('CSRF token invalid: decode error', { token: `${token.slice(0, 20)}...` });
|
|
147
|
-
return false;
|
|
148
|
-
}
|
|
29
|
+
function verify(secretKey, csrfTokenFromRequest, loginToken) {
|
|
30
|
+
const expectCsrfToken = sign(secretKey, loginToken);
|
|
31
|
+
return expectCsrfToken === csrfTokenFromRequest;
|
|
149
32
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.16.51-beta-
|
|
6
|
+
"version": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
7
7
|
"description": "graphql client to read/write data on abt node",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"typings": "lib/index.d.ts",
|
|
@@ -27,19 +27,19 @@
|
|
|
27
27
|
"author": "linchen1987 <linchen.1987@foxmail.com> (http://github.com/linchen1987)",
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@abtnode/constant": "1.16.51-beta-
|
|
31
|
-
"@abtnode/db-cache": "1.16.51-beta-
|
|
32
|
-
"@abtnode/util": "1.16.51-beta-
|
|
30
|
+
"@abtnode/constant": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
31
|
+
"@abtnode/db-cache": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
32
|
+
"@abtnode/util": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
33
33
|
"@arcblock/did": "1.24.0",
|
|
34
34
|
"@arcblock/did-connect-js": "1.24.0",
|
|
35
35
|
"@arcblock/jwt": "1.24.0",
|
|
36
36
|
"@arcblock/ws": "1.24.0",
|
|
37
|
-
"@blocklet/constant": "1.16.51-beta-
|
|
38
|
-
"@blocklet/env": "1.16.51-beta-
|
|
37
|
+
"@blocklet/constant": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
38
|
+
"@blocklet/env": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
39
39
|
"@blocklet/error": "^0.2.5",
|
|
40
|
-
"@blocklet/meta": "1.16.51-beta-
|
|
41
|
-
"@blocklet/server-js": "1.16.51-beta-
|
|
42
|
-
"@blocklet/theme": "^3.1.
|
|
40
|
+
"@blocklet/meta": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
41
|
+
"@blocklet/server-js": "1.16.51-beta-20250905-051437-fe05adb2",
|
|
42
|
+
"@blocklet/theme": "^3.1.34",
|
|
43
43
|
"@did-connect/authenticator": "^2.2.8",
|
|
44
44
|
"@did-connect/handler": "^2.2.8",
|
|
45
45
|
"@nedb/core": "^2.1.5",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"ts-node": "^10.9.1",
|
|
86
86
|
"typescript": "^5.6.3"
|
|
87
87
|
},
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "cadff492e3858895a3e0565b0b1778f166fb34f7"
|
|
89
89
|
}
|