@lark-sh/client 0.1.15 → 0.1.16
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/{chunk-EK7OYLDG.mjs → chunk-CYCSP6TZ.mjs} +150 -46
- package/dist/chunk-CYCSP6TZ.mjs.map +1 -0
- package/dist/fb-v8/index.js +149 -45
- package/dist/fb-v8/index.js.map +1 -1
- package/dist/fb-v8/index.mjs +1 -1
- package/dist/index.d.mts +2 -3
- package/dist/index.d.ts +2 -3
- package/dist/index.js +149 -45
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-EK7OYLDG.mjs.map +0 -1
package/dist/fb-v8/index.js
CHANGED
|
@@ -72,18 +72,10 @@ var Coordinator = class {
|
|
|
72
72
|
* Request connection details from the coordinator.
|
|
73
73
|
*
|
|
74
74
|
* @param database - Database ID in format "project/database"
|
|
75
|
-
* @
|
|
76
|
-
* @returns Connection details including host, port, and connection token
|
|
75
|
+
* @returns Connection details including WebSocket URL and UDP host/port
|
|
77
76
|
*/
|
|
78
|
-
async connect(database
|
|
77
|
+
async connect(database) {
|
|
79
78
|
const body = { database };
|
|
80
|
-
if (options.token) {
|
|
81
|
-
body.token = options.token;
|
|
82
|
-
} else if (options.anonymous) {
|
|
83
|
-
body.anonymous = true;
|
|
84
|
-
} else {
|
|
85
|
-
body.anonymous = true;
|
|
86
|
-
}
|
|
87
79
|
const response = await fetch(`${this.baseUrl}/connect`, {
|
|
88
80
|
method: "POST",
|
|
89
81
|
headers: {
|
|
@@ -2919,6 +2911,125 @@ function generatePushId() {
|
|
|
2919
2911
|
return id;
|
|
2920
2912
|
}
|
|
2921
2913
|
|
|
2914
|
+
// src/utils/validation.ts
|
|
2915
|
+
var MAX_KEY_BYTES = 768;
|
|
2916
|
+
var INVALID_KEY_CHARS = /[.#$\[\]\/]/;
|
|
2917
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f]/;
|
|
2918
|
+
function hasControlChars(str) {
|
|
2919
|
+
return CONTROL_CHARS.test(str);
|
|
2920
|
+
}
|
|
2921
|
+
function getByteLength(str) {
|
|
2922
|
+
if (typeof TextEncoder !== "undefined") {
|
|
2923
|
+
return new TextEncoder().encode(str).length;
|
|
2924
|
+
}
|
|
2925
|
+
return Buffer.byteLength(str, "utf8");
|
|
2926
|
+
}
|
|
2927
|
+
function validateKey(key, context) {
|
|
2928
|
+
const prefix = context ? `${context}: ` : "";
|
|
2929
|
+
if (typeof key !== "string") {
|
|
2930
|
+
throw new LarkError(
|
|
2931
|
+
ErrorCode.INVALID_DATA,
|
|
2932
|
+
`${prefix}key must be a string`
|
|
2933
|
+
);
|
|
2934
|
+
}
|
|
2935
|
+
if (key.length === 0) {
|
|
2936
|
+
throw new LarkError(
|
|
2937
|
+
ErrorCode.INVALID_DATA,
|
|
2938
|
+
`${prefix}key cannot be empty`
|
|
2939
|
+
);
|
|
2940
|
+
}
|
|
2941
|
+
if (INVALID_KEY_CHARS.test(key)) {
|
|
2942
|
+
throw new LarkError(
|
|
2943
|
+
ErrorCode.INVALID_DATA,
|
|
2944
|
+
`${prefix}key "${key}" contains invalid characters (. $ # [ ] / are not allowed)`
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2947
|
+
if (hasControlChars(key)) {
|
|
2948
|
+
throw new LarkError(
|
|
2949
|
+
ErrorCode.INVALID_DATA,
|
|
2950
|
+
`${prefix}key "${key}" contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2951
|
+
);
|
|
2952
|
+
}
|
|
2953
|
+
const byteLength = getByteLength(key);
|
|
2954
|
+
if (byteLength > MAX_KEY_BYTES) {
|
|
2955
|
+
throw new LarkError(
|
|
2956
|
+
ErrorCode.INVALID_DATA,
|
|
2957
|
+
`${prefix}key "${key.substring(0, 50)}..." exceeds maximum size of ${MAX_KEY_BYTES} bytes (got ${byteLength})`
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
}
|
|
2961
|
+
function validatePath(path, context) {
|
|
2962
|
+
if (typeof path !== "string") {
|
|
2963
|
+
throw new LarkError(
|
|
2964
|
+
ErrorCode.INVALID_PATH,
|
|
2965
|
+
`${context ? `${context}: ` : ""}path must be a string`
|
|
2966
|
+
);
|
|
2967
|
+
}
|
|
2968
|
+
if (path === "" || path === "/") {
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
2972
|
+
for (const segment of segments) {
|
|
2973
|
+
if (segment === ".priority" || segment === ".value" || segment === ".sv" || segment === ".info") {
|
|
2974
|
+
continue;
|
|
2975
|
+
}
|
|
2976
|
+
validateKey(segment, context);
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
function validateValue(value, context, path = "") {
|
|
2980
|
+
const prefix = context ? `${context}: ` : "";
|
|
2981
|
+
const location = path ? ` at "${path}"` : "";
|
|
2982
|
+
if (value === null || value === void 0) {
|
|
2983
|
+
return;
|
|
2984
|
+
}
|
|
2985
|
+
if (typeof value === "string") {
|
|
2986
|
+
if (hasControlChars(value)) {
|
|
2987
|
+
throw new LarkError(
|
|
2988
|
+
ErrorCode.INVALID_DATA,
|
|
2989
|
+
`${prefix}string value${location} contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2990
|
+
);
|
|
2991
|
+
}
|
|
2992
|
+
return;
|
|
2993
|
+
}
|
|
2994
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
2995
|
+
return;
|
|
2996
|
+
}
|
|
2997
|
+
if (Array.isArray(value)) {
|
|
2998
|
+
for (let i = 0; i < value.length; i++) {
|
|
2999
|
+
validateValue(value[i], context, path ? `${path}/${i}` : String(i));
|
|
3000
|
+
}
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
if (typeof value === "object") {
|
|
3004
|
+
const obj = value;
|
|
3005
|
+
const keys = Object.keys(obj);
|
|
3006
|
+
if (".value" in obj) {
|
|
3007
|
+
const otherKeys = keys.filter((k) => k !== ".value" && k !== ".priority");
|
|
3008
|
+
if (otherKeys.length > 0) {
|
|
3009
|
+
const loc = path || "/";
|
|
3010
|
+
throw new LarkError(
|
|
3011
|
+
ErrorCode.INVALID_DATA,
|
|
3012
|
+
`${prefix}data at ${loc} contains ".value" alongside other children (${otherKeys.join(", ")}). ".value" can only be used with ".priority" for primitives with priority.`
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
for (const [key, childValue] of Object.entries(obj)) {
|
|
3017
|
+
if (key !== ".priority" && key !== ".value" && key !== ".sv") {
|
|
3018
|
+
validateKey(key, context);
|
|
3019
|
+
}
|
|
3020
|
+
validateValue(childValue, context, path ? `${path}/${key}` : key);
|
|
3021
|
+
}
|
|
3022
|
+
return;
|
|
3023
|
+
}
|
|
3024
|
+
throw new LarkError(
|
|
3025
|
+
ErrorCode.INVALID_DATA,
|
|
3026
|
+
`${prefix}invalid value type${location}: ${typeof value}`
|
|
3027
|
+
);
|
|
3028
|
+
}
|
|
3029
|
+
function validateWriteData(value, context) {
|
|
3030
|
+
validateValue(value, context);
|
|
3031
|
+
}
|
|
3032
|
+
|
|
2922
3033
|
// src/DatabaseReference.ts
|
|
2923
3034
|
function isInfoPath(path) {
|
|
2924
3035
|
return path === "/.info" || path.startsWith("/.info/");
|
|
@@ -3090,6 +3201,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3090
3201
|
* Get a reference to a child path.
|
|
3091
3202
|
*/
|
|
3092
3203
|
child(path) {
|
|
3204
|
+
validatePath(path, "child");
|
|
3093
3205
|
const childPath = joinPath(this._path, path);
|
|
3094
3206
|
return new _DatabaseReference(this._db, childPath);
|
|
3095
3207
|
}
|
|
@@ -3106,6 +3218,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3106
3218
|
*/
|
|
3107
3219
|
async set(value) {
|
|
3108
3220
|
validateNotInfoPath(this._path, "set");
|
|
3221
|
+
validateWriteData(value, "set");
|
|
3109
3222
|
if (this._db.isVolatilePath(this._path)) {
|
|
3110
3223
|
this._db._sendVolatileSet(this._path, value);
|
|
3111
3224
|
return;
|
|
@@ -3135,6 +3248,16 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3135
3248
|
*/
|
|
3136
3249
|
async update(values) {
|
|
3137
3250
|
validateNotInfoPath(this._path, "update");
|
|
3251
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3252
|
+
if (key.includes("/")) {
|
|
3253
|
+
validatePath(key, "update");
|
|
3254
|
+
} else {
|
|
3255
|
+
validateKey(key, "update");
|
|
3256
|
+
}
|
|
3257
|
+
if (value !== null) {
|
|
3258
|
+
validateWriteData(value, "update");
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3138
3261
|
const hasPathKeys = Object.keys(values).some((key) => key.startsWith("/"));
|
|
3139
3262
|
if (hasPathKeys) {
|
|
3140
3263
|
const ops = [];
|
|
@@ -3193,6 +3316,10 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3193
3316
|
*/
|
|
3194
3317
|
async setWithPriority(value, priority) {
|
|
3195
3318
|
validateNotInfoPath(this._path, "setWithPriority");
|
|
3319
|
+
validateWriteData(value, "setWithPriority");
|
|
3320
|
+
if (typeof priority === "string") {
|
|
3321
|
+
validateWriteData(priority, "setWithPriority (priority)");
|
|
3322
|
+
}
|
|
3196
3323
|
if (value === null || value === void 0) {
|
|
3197
3324
|
await this._db._sendSet(this._path, value);
|
|
3198
3325
|
return;
|
|
@@ -3734,17 +3861,11 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3734
3861
|
}
|
|
3735
3862
|
}
|
|
3736
3863
|
/**
|
|
3737
|
-
* Validate that a key is a valid
|
|
3738
|
-
*
|
|
3739
|
-
* Also cannot start or end with .
|
|
3864
|
+
* Validate that a key is a valid key format.
|
|
3865
|
+
* Delegates to the shared validation utility.
|
|
3740
3866
|
*/
|
|
3741
3867
|
_validateKeyFormat(methodName, key) {
|
|
3742
|
-
|
|
3743
|
-
throw new LarkError(
|
|
3744
|
-
ErrorCode.INVALID_QUERY,
|
|
3745
|
-
`Query.${methodName}: invalid key "${key}" - keys cannot contain . # $ [ ] /`
|
|
3746
|
-
);
|
|
3747
|
-
}
|
|
3868
|
+
validateKey(key, `Query.${methodName}`);
|
|
3748
3869
|
}
|
|
3749
3870
|
// ============================================
|
|
3750
3871
|
// Internal Helpers
|
|
@@ -4112,28 +4233,6 @@ function isVolatilePath(path, patterns) {
|
|
|
4112
4233
|
}
|
|
4113
4234
|
|
|
4114
4235
|
// src/LarkDatabase.ts
|
|
4115
|
-
function validateWriteData(data, path = "") {
|
|
4116
|
-
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
4117
|
-
return;
|
|
4118
|
-
}
|
|
4119
|
-
const obj = data;
|
|
4120
|
-
const keys = Object.keys(obj);
|
|
4121
|
-
if (".value" in obj) {
|
|
4122
|
-
const otherKeys = keys.filter((k) => k !== ".value" && k !== ".priority");
|
|
4123
|
-
if (otherKeys.length > 0) {
|
|
4124
|
-
const location = path || "/";
|
|
4125
|
-
throw new LarkError(
|
|
4126
|
-
"invalid_data",
|
|
4127
|
-
`Data at ${location} contains ".value" alongside other children (${otherKeys.join(", ")}). ".value" can only be used with ".priority" for primitives with priority.`
|
|
4128
|
-
);
|
|
4129
|
-
}
|
|
4130
|
-
}
|
|
4131
|
-
for (const key of keys) {
|
|
4132
|
-
if (key !== ".priority" && key !== ".value") {
|
|
4133
|
-
validateWriteData(obj[key], path ? `${path}/${key}` : `/${key}`);
|
|
4134
|
-
}
|
|
4135
|
-
}
|
|
4136
|
-
}
|
|
4137
4236
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
4138
4237
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
4139
4238
|
var RECONNECT_JITTER_FACTOR = 0.5;
|
|
@@ -4356,10 +4455,7 @@ var LarkDatabase = class {
|
|
|
4356
4455
|
try {
|
|
4357
4456
|
const coordinatorUrl = this._coordinatorUrl;
|
|
4358
4457
|
const coordinator = new Coordinator(coordinatorUrl);
|
|
4359
|
-
const connectResponse = await coordinator.connect(databaseId
|
|
4360
|
-
token: options.token,
|
|
4361
|
-
anonymous: options.anonymous
|
|
4362
|
-
});
|
|
4458
|
+
const connectResponse = await coordinator.connect(databaseId);
|
|
4363
4459
|
const wsUrl = connectResponse.ws_url;
|
|
4364
4460
|
const transportResult = await createTransport(
|
|
4365
4461
|
wsUrl,
|
|
@@ -4756,14 +4852,20 @@ var LarkDatabase = class {
|
|
|
4756
4852
|
*/
|
|
4757
4853
|
async convertToTxOp(op) {
|
|
4758
4854
|
const path = normalizePath(op.path) || "/";
|
|
4855
|
+
validatePath(op.path, "transaction");
|
|
4759
4856
|
switch (op.op) {
|
|
4760
4857
|
case "set":
|
|
4858
|
+
validateWriteData(op.value, "transaction");
|
|
4761
4859
|
return { o: "s", p: path, v: op.value };
|
|
4762
4860
|
case "update":
|
|
4861
|
+
validateWriteData(op.value, "transaction");
|
|
4763
4862
|
return { o: "u", p: path, v: op.value };
|
|
4764
4863
|
case "delete":
|
|
4765
4864
|
return { o: "d", p: path };
|
|
4766
4865
|
case "condition":
|
|
4866
|
+
if (op.value !== void 0 && op.value !== null) {
|
|
4867
|
+
validateWriteData(op.value, "transaction condition");
|
|
4868
|
+
}
|
|
4767
4869
|
if (isPrimitive(op.value)) {
|
|
4768
4870
|
return { o: "c", p: path, v: op.value };
|
|
4769
4871
|
} else {
|
|
@@ -4781,10 +4883,12 @@ var LarkDatabase = class {
|
|
|
4781
4883
|
convertObjectToTxOps(obj) {
|
|
4782
4884
|
const ops = [];
|
|
4783
4885
|
for (const [path, value] of Object.entries(obj)) {
|
|
4886
|
+
validatePath(path, "transaction");
|
|
4784
4887
|
const normalizedPath = normalizePath(path) || "/";
|
|
4785
4888
|
if (value === null) {
|
|
4786
4889
|
ops.push({ o: "d", p: normalizedPath });
|
|
4787
4890
|
} else {
|
|
4891
|
+
validateWriteData(value, "transaction");
|
|
4788
4892
|
ops.push({ o: "s", p: normalizedPath, v: value });
|
|
4789
4893
|
}
|
|
4790
4894
|
}
|