@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
|
@@ -27,18 +27,10 @@ var Coordinator = class {
|
|
|
27
27
|
* Request connection details from the coordinator.
|
|
28
28
|
*
|
|
29
29
|
* @param database - Database ID in format "project/database"
|
|
30
|
-
* @
|
|
31
|
-
* @returns Connection details including host, port, and connection token
|
|
30
|
+
* @returns Connection details including WebSocket URL and UDP host/port
|
|
32
31
|
*/
|
|
33
|
-
async connect(database
|
|
32
|
+
async connect(database) {
|
|
34
33
|
const body = { database };
|
|
35
|
-
if (options.token) {
|
|
36
|
-
body.token = options.token;
|
|
37
|
-
} else if (options.anonymous) {
|
|
38
|
-
body.anonymous = true;
|
|
39
|
-
} else {
|
|
40
|
-
body.anonymous = true;
|
|
41
|
-
}
|
|
42
34
|
const response = await fetch(`${this.baseUrl}/connect`, {
|
|
43
35
|
method: "POST",
|
|
44
36
|
headers: {
|
|
@@ -2874,6 +2866,125 @@ function generatePushId() {
|
|
|
2874
2866
|
return id;
|
|
2875
2867
|
}
|
|
2876
2868
|
|
|
2869
|
+
// src/utils/validation.ts
|
|
2870
|
+
var MAX_KEY_BYTES = 768;
|
|
2871
|
+
var INVALID_KEY_CHARS = /[.#$\[\]\/]/;
|
|
2872
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f]/;
|
|
2873
|
+
function hasControlChars(str) {
|
|
2874
|
+
return CONTROL_CHARS.test(str);
|
|
2875
|
+
}
|
|
2876
|
+
function getByteLength(str) {
|
|
2877
|
+
if (typeof TextEncoder !== "undefined") {
|
|
2878
|
+
return new TextEncoder().encode(str).length;
|
|
2879
|
+
}
|
|
2880
|
+
return Buffer.byteLength(str, "utf8");
|
|
2881
|
+
}
|
|
2882
|
+
function validateKey(key, context) {
|
|
2883
|
+
const prefix = context ? `${context}: ` : "";
|
|
2884
|
+
if (typeof key !== "string") {
|
|
2885
|
+
throw new LarkError(
|
|
2886
|
+
ErrorCode.INVALID_DATA,
|
|
2887
|
+
`${prefix}key must be a string`
|
|
2888
|
+
);
|
|
2889
|
+
}
|
|
2890
|
+
if (key.length === 0) {
|
|
2891
|
+
throw new LarkError(
|
|
2892
|
+
ErrorCode.INVALID_DATA,
|
|
2893
|
+
`${prefix}key cannot be empty`
|
|
2894
|
+
);
|
|
2895
|
+
}
|
|
2896
|
+
if (INVALID_KEY_CHARS.test(key)) {
|
|
2897
|
+
throw new LarkError(
|
|
2898
|
+
ErrorCode.INVALID_DATA,
|
|
2899
|
+
`${prefix}key "${key}" contains invalid characters (. $ # [ ] / are not allowed)`
|
|
2900
|
+
);
|
|
2901
|
+
}
|
|
2902
|
+
if (hasControlChars(key)) {
|
|
2903
|
+
throw new LarkError(
|
|
2904
|
+
ErrorCode.INVALID_DATA,
|
|
2905
|
+
`${prefix}key "${key}" contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2906
|
+
);
|
|
2907
|
+
}
|
|
2908
|
+
const byteLength = getByteLength(key);
|
|
2909
|
+
if (byteLength > MAX_KEY_BYTES) {
|
|
2910
|
+
throw new LarkError(
|
|
2911
|
+
ErrorCode.INVALID_DATA,
|
|
2912
|
+
`${prefix}key "${key.substring(0, 50)}..." exceeds maximum size of ${MAX_KEY_BYTES} bytes (got ${byteLength})`
|
|
2913
|
+
);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
function validatePath(path, context) {
|
|
2917
|
+
if (typeof path !== "string") {
|
|
2918
|
+
throw new LarkError(
|
|
2919
|
+
ErrorCode.INVALID_PATH,
|
|
2920
|
+
`${context ? `${context}: ` : ""}path must be a string`
|
|
2921
|
+
);
|
|
2922
|
+
}
|
|
2923
|
+
if (path === "" || path === "/") {
|
|
2924
|
+
return;
|
|
2925
|
+
}
|
|
2926
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
2927
|
+
for (const segment of segments) {
|
|
2928
|
+
if (segment === ".priority" || segment === ".value" || segment === ".sv" || segment === ".info") {
|
|
2929
|
+
continue;
|
|
2930
|
+
}
|
|
2931
|
+
validateKey(segment, context);
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
function validateValue(value, context, path = "") {
|
|
2935
|
+
const prefix = context ? `${context}: ` : "";
|
|
2936
|
+
const location = path ? ` at "${path}"` : "";
|
|
2937
|
+
if (value === null || value === void 0) {
|
|
2938
|
+
return;
|
|
2939
|
+
}
|
|
2940
|
+
if (typeof value === "string") {
|
|
2941
|
+
if (hasControlChars(value)) {
|
|
2942
|
+
throw new LarkError(
|
|
2943
|
+
ErrorCode.INVALID_DATA,
|
|
2944
|
+
`${prefix}string value${location} contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2945
|
+
);
|
|
2946
|
+
}
|
|
2947
|
+
return;
|
|
2948
|
+
}
|
|
2949
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
if (Array.isArray(value)) {
|
|
2953
|
+
for (let i = 0; i < value.length; i++) {
|
|
2954
|
+
validateValue(value[i], context, path ? `${path}/${i}` : String(i));
|
|
2955
|
+
}
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (typeof value === "object") {
|
|
2959
|
+
const obj = value;
|
|
2960
|
+
const keys = Object.keys(obj);
|
|
2961
|
+
if (".value" in obj) {
|
|
2962
|
+
const otherKeys = keys.filter((k) => k !== ".value" && k !== ".priority");
|
|
2963
|
+
if (otherKeys.length > 0) {
|
|
2964
|
+
const loc = path || "/";
|
|
2965
|
+
throw new LarkError(
|
|
2966
|
+
ErrorCode.INVALID_DATA,
|
|
2967
|
+
`${prefix}data at ${loc} contains ".value" alongside other children (${otherKeys.join(", ")}). ".value" can only be used with ".priority" for primitives with priority.`
|
|
2968
|
+
);
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
for (const [key, childValue] of Object.entries(obj)) {
|
|
2972
|
+
if (key !== ".priority" && key !== ".value" && key !== ".sv") {
|
|
2973
|
+
validateKey(key, context);
|
|
2974
|
+
}
|
|
2975
|
+
validateValue(childValue, context, path ? `${path}/${key}` : key);
|
|
2976
|
+
}
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
throw new LarkError(
|
|
2980
|
+
ErrorCode.INVALID_DATA,
|
|
2981
|
+
`${prefix}invalid value type${location}: ${typeof value}`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
function validateWriteData(value, context) {
|
|
2985
|
+
validateValue(value, context);
|
|
2986
|
+
}
|
|
2987
|
+
|
|
2877
2988
|
// src/DatabaseReference.ts
|
|
2878
2989
|
function isInfoPath(path) {
|
|
2879
2990
|
return path === "/.info" || path.startsWith("/.info/");
|
|
@@ -3045,6 +3156,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3045
3156
|
* Get a reference to a child path.
|
|
3046
3157
|
*/
|
|
3047
3158
|
child(path) {
|
|
3159
|
+
validatePath(path, "child");
|
|
3048
3160
|
const childPath = joinPath(this._path, path);
|
|
3049
3161
|
return new _DatabaseReference(this._db, childPath);
|
|
3050
3162
|
}
|
|
@@ -3061,6 +3173,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3061
3173
|
*/
|
|
3062
3174
|
async set(value) {
|
|
3063
3175
|
validateNotInfoPath(this._path, "set");
|
|
3176
|
+
validateWriteData(value, "set");
|
|
3064
3177
|
if (this._db.isVolatilePath(this._path)) {
|
|
3065
3178
|
this._db._sendVolatileSet(this._path, value);
|
|
3066
3179
|
return;
|
|
@@ -3090,6 +3203,16 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3090
3203
|
*/
|
|
3091
3204
|
async update(values) {
|
|
3092
3205
|
validateNotInfoPath(this._path, "update");
|
|
3206
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3207
|
+
if (key.includes("/")) {
|
|
3208
|
+
validatePath(key, "update");
|
|
3209
|
+
} else {
|
|
3210
|
+
validateKey(key, "update");
|
|
3211
|
+
}
|
|
3212
|
+
if (value !== null) {
|
|
3213
|
+
validateWriteData(value, "update");
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3093
3216
|
const hasPathKeys = Object.keys(values).some((key) => key.startsWith("/"));
|
|
3094
3217
|
if (hasPathKeys) {
|
|
3095
3218
|
const ops = [];
|
|
@@ -3148,6 +3271,10 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3148
3271
|
*/
|
|
3149
3272
|
async setWithPriority(value, priority) {
|
|
3150
3273
|
validateNotInfoPath(this._path, "setWithPriority");
|
|
3274
|
+
validateWriteData(value, "setWithPriority");
|
|
3275
|
+
if (typeof priority === "string") {
|
|
3276
|
+
validateWriteData(priority, "setWithPriority (priority)");
|
|
3277
|
+
}
|
|
3151
3278
|
if (value === null || value === void 0) {
|
|
3152
3279
|
await this._db._sendSet(this._path, value);
|
|
3153
3280
|
return;
|
|
@@ -3689,17 +3816,11 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3689
3816
|
}
|
|
3690
3817
|
}
|
|
3691
3818
|
/**
|
|
3692
|
-
* Validate that a key is a valid
|
|
3693
|
-
*
|
|
3694
|
-
* Also cannot start or end with .
|
|
3819
|
+
* Validate that a key is a valid key format.
|
|
3820
|
+
* Delegates to the shared validation utility.
|
|
3695
3821
|
*/
|
|
3696
3822
|
_validateKeyFormat(methodName, key) {
|
|
3697
|
-
|
|
3698
|
-
throw new LarkError(
|
|
3699
|
-
ErrorCode.INVALID_QUERY,
|
|
3700
|
-
`Query.${methodName}: invalid key "${key}" - keys cannot contain . # $ [ ] /`
|
|
3701
|
-
);
|
|
3702
|
-
}
|
|
3823
|
+
validateKey(key, `Query.${methodName}`);
|
|
3703
3824
|
}
|
|
3704
3825
|
// ============================================
|
|
3705
3826
|
// Internal Helpers
|
|
@@ -4067,28 +4188,6 @@ function isVolatilePath(path, patterns) {
|
|
|
4067
4188
|
}
|
|
4068
4189
|
|
|
4069
4190
|
// src/LarkDatabase.ts
|
|
4070
|
-
function validateWriteData(data, path = "") {
|
|
4071
|
-
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
4072
|
-
return;
|
|
4073
|
-
}
|
|
4074
|
-
const obj = data;
|
|
4075
|
-
const keys = Object.keys(obj);
|
|
4076
|
-
if (".value" in obj) {
|
|
4077
|
-
const otherKeys = keys.filter((k) => k !== ".value" && k !== ".priority");
|
|
4078
|
-
if (otherKeys.length > 0) {
|
|
4079
|
-
const location = path || "/";
|
|
4080
|
-
throw new LarkError(
|
|
4081
|
-
"invalid_data",
|
|
4082
|
-
`Data at ${location} contains ".value" alongside other children (${otherKeys.join(", ")}). ".value" can only be used with ".priority" for primitives with priority.`
|
|
4083
|
-
);
|
|
4084
|
-
}
|
|
4085
|
-
}
|
|
4086
|
-
for (const key of keys) {
|
|
4087
|
-
if (key !== ".priority" && key !== ".value") {
|
|
4088
|
-
validateWriteData(obj[key], path ? `${path}/${key}` : `/${key}`);
|
|
4089
|
-
}
|
|
4090
|
-
}
|
|
4091
|
-
}
|
|
4092
4191
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
4093
4192
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
4094
4193
|
var RECONNECT_JITTER_FACTOR = 0.5;
|
|
@@ -4311,10 +4410,7 @@ var LarkDatabase = class {
|
|
|
4311
4410
|
try {
|
|
4312
4411
|
const coordinatorUrl = this._coordinatorUrl;
|
|
4313
4412
|
const coordinator = new Coordinator(coordinatorUrl);
|
|
4314
|
-
const connectResponse = await coordinator.connect(databaseId
|
|
4315
|
-
token: options.token,
|
|
4316
|
-
anonymous: options.anonymous
|
|
4317
|
-
});
|
|
4413
|
+
const connectResponse = await coordinator.connect(databaseId);
|
|
4318
4414
|
const wsUrl = connectResponse.ws_url;
|
|
4319
4415
|
const transportResult = await createTransport(
|
|
4320
4416
|
wsUrl,
|
|
@@ -4711,14 +4807,20 @@ var LarkDatabase = class {
|
|
|
4711
4807
|
*/
|
|
4712
4808
|
async convertToTxOp(op) {
|
|
4713
4809
|
const path = normalizePath(op.path) || "/";
|
|
4810
|
+
validatePath(op.path, "transaction");
|
|
4714
4811
|
switch (op.op) {
|
|
4715
4812
|
case "set":
|
|
4813
|
+
validateWriteData(op.value, "transaction");
|
|
4716
4814
|
return { o: "s", p: path, v: op.value };
|
|
4717
4815
|
case "update":
|
|
4816
|
+
validateWriteData(op.value, "transaction");
|
|
4718
4817
|
return { o: "u", p: path, v: op.value };
|
|
4719
4818
|
case "delete":
|
|
4720
4819
|
return { o: "d", p: path };
|
|
4721
4820
|
case "condition":
|
|
4821
|
+
if (op.value !== void 0 && op.value !== null) {
|
|
4822
|
+
validateWriteData(op.value, "transaction condition");
|
|
4823
|
+
}
|
|
4722
4824
|
if (isPrimitive(op.value)) {
|
|
4723
4825
|
return { o: "c", p: path, v: op.value };
|
|
4724
4826
|
} else {
|
|
@@ -4736,10 +4838,12 @@ var LarkDatabase = class {
|
|
|
4736
4838
|
convertObjectToTxOps(obj) {
|
|
4737
4839
|
const ops = [];
|
|
4738
4840
|
for (const [path, value] of Object.entries(obj)) {
|
|
4841
|
+
validatePath(path, "transaction");
|
|
4739
4842
|
const normalizedPath = normalizePath(path) || "/";
|
|
4740
4843
|
if (value === null) {
|
|
4741
4844
|
ops.push({ o: "d", p: normalizedPath });
|
|
4742
4845
|
} else {
|
|
4846
|
+
validateWriteData(value, "transaction");
|
|
4743
4847
|
ops.push({ o: "s", p: normalizedPath, v: value });
|
|
4744
4848
|
}
|
|
4745
4849
|
}
|
|
@@ -5285,4 +5389,4 @@ export {
|
|
|
5285
5389
|
ServerValue,
|
|
5286
5390
|
LarkDatabase
|
|
5287
5391
|
};
|
|
5288
|
-
//# sourceMappingURL=chunk-
|
|
5392
|
+
//# sourceMappingURL=chunk-CYCSP6TZ.mjs.map
|