@lark-sh/client 0.1.15 → 0.1.17
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-VLAAYUVX.mjs} +190 -127
- package/dist/chunk-VLAAYUVX.mjs.map +1 -0
- package/dist/fb-v8/index.js +189 -126
- package/dist/fb-v8/index.js.map +1 -1
- package/dist/fb-v8/index.mjs +1 -1
- package/dist/index.d.mts +16 -22
- package/dist/index.d.ts +16 -22
- package/dist/index.js +189 -126
- 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
|
@@ -43,70 +43,6 @@ __export(fb_v8_exports, {
|
|
|
43
43
|
});
|
|
44
44
|
module.exports = __toCommonJS(fb_v8_exports);
|
|
45
45
|
|
|
46
|
-
// src/protocol/constants.ts
|
|
47
|
-
var OnDisconnectAction = {
|
|
48
|
-
SET: "s",
|
|
49
|
-
UPDATE: "u",
|
|
50
|
-
DELETE: "d",
|
|
51
|
-
CANCEL: "c"
|
|
52
|
-
};
|
|
53
|
-
var ErrorCode = {
|
|
54
|
-
PERMISSION_DENIED: "permission_denied",
|
|
55
|
-
INVALID_DATA: "invalid_data",
|
|
56
|
-
NOT_FOUND: "not_found",
|
|
57
|
-
INVALID_PATH: "invalid_path",
|
|
58
|
-
INVALID_OPERATION: "invalid_operation",
|
|
59
|
-
INTERNAL_ERROR: "internal_error",
|
|
60
|
-
CONDITION_FAILED: "condition_failed",
|
|
61
|
-
INVALID_QUERY: "invalid_query",
|
|
62
|
-
AUTH_REQUIRED: "auth_required"
|
|
63
|
-
};
|
|
64
|
-
var DEFAULT_COORDINATOR_URL = "https://db.lark.sh";
|
|
65
|
-
|
|
66
|
-
// src/connection/Coordinator.ts
|
|
67
|
-
var Coordinator = class {
|
|
68
|
-
constructor(baseUrl = DEFAULT_COORDINATOR_URL) {
|
|
69
|
-
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Request connection details from the coordinator.
|
|
73
|
-
*
|
|
74
|
-
* @param database - Database ID in format "project/database"
|
|
75
|
-
* @param options - Either a user token or anonymous flag
|
|
76
|
-
* @returns Connection details including host, port, and connection token
|
|
77
|
-
*/
|
|
78
|
-
async connect(database, options = {}) {
|
|
79
|
-
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
|
-
const response = await fetch(`${this.baseUrl}/connect`, {
|
|
88
|
-
method: "POST",
|
|
89
|
-
headers: {
|
|
90
|
-
"Content-Type": "application/json"
|
|
91
|
-
},
|
|
92
|
-
body: JSON.stringify(body)
|
|
93
|
-
});
|
|
94
|
-
if (!response.ok) {
|
|
95
|
-
let errorMessage = `Coordinator request failed: ${response.status}`;
|
|
96
|
-
try {
|
|
97
|
-
const errorBody = await response.json();
|
|
98
|
-
if (errorBody.message) {
|
|
99
|
-
errorMessage = errorBody.message;
|
|
100
|
-
}
|
|
101
|
-
} catch {
|
|
102
|
-
}
|
|
103
|
-
throw new Error(errorMessage);
|
|
104
|
-
}
|
|
105
|
-
const data = await response.json();
|
|
106
|
-
return data;
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
|
|
110
46
|
// src/connection/WebSocketTransport.ts
|
|
111
47
|
var import_ws = __toESM(require("ws"));
|
|
112
48
|
var WebSocketImpl = typeof WebSocket !== "undefined" ? WebSocket : import_ws.default;
|
|
@@ -1473,7 +1409,6 @@ var View = class {
|
|
|
1473
1409
|
/**
|
|
1474
1410
|
* Check if this View loads all data (no limits, no range filters).
|
|
1475
1411
|
* A View that loads all data can serve as a "complete" cache for child paths.
|
|
1476
|
-
* This matches Firebase's loadsAllData() semantics.
|
|
1477
1412
|
*/
|
|
1478
1413
|
loadsAllData() {
|
|
1479
1414
|
if (!this.queryParams) return true;
|
|
@@ -1889,8 +1824,8 @@ var SubscriptionManager = class {
|
|
|
1889
1824
|
/**
|
|
1890
1825
|
* Detect and fire child_moved events for children that changed position OR sort value.
|
|
1891
1826
|
*
|
|
1892
|
-
*
|
|
1893
|
-
* the position actually changes.
|
|
1827
|
+
* child_moved fires for ANY priority/sort value change, regardless of whether
|
|
1828
|
+
* the position actually changes.
|
|
1894
1829
|
*
|
|
1895
1830
|
* IMPORTANT: child_moved should only fire for children whose VALUE or PRIORITY changed.
|
|
1896
1831
|
* Children that are merely "displaced" by another child moving should NOT fire child_moved.
|
|
@@ -2093,9 +2028,6 @@ var SubscriptionManager = class {
|
|
|
2093
2028
|
* A complete View has no limits and no range filters, so it contains all data
|
|
2094
2029
|
* for its subtree. Child subscriptions can use the ancestor's data instead
|
|
2095
2030
|
* of creating their own server subscription.
|
|
2096
|
-
*
|
|
2097
|
-
* This matches Firebase's behavior where child listeners don't need their own
|
|
2098
|
-
* server subscription if an ancestor has an unlimited listener.
|
|
2099
2031
|
*/
|
|
2100
2032
|
hasAncestorCompleteView(path) {
|
|
2101
2033
|
const normalized = normalizePath(path);
|
|
@@ -2221,8 +2153,8 @@ var SubscriptionManager = class {
|
|
|
2221
2153
|
/**
|
|
2222
2154
|
* Recursively sort object keys for consistent comparison.
|
|
2223
2155
|
* This ensures {a:1, b:2} and {b:2, a:1} compare as equal.
|
|
2224
|
-
* Uses simple alphabetical sorting
|
|
2225
|
-
*
|
|
2156
|
+
* Uses simple alphabetical sorting since we're just checking data equality,
|
|
2157
|
+
* not display order.
|
|
2226
2158
|
*/
|
|
2227
2159
|
sortKeysForComparison(value) {
|
|
2228
2160
|
if (value === null || typeof value !== "object") {
|
|
@@ -2816,6 +2748,26 @@ var SubscriptionManager = class {
|
|
|
2816
2748
|
}
|
|
2817
2749
|
};
|
|
2818
2750
|
|
|
2751
|
+
// src/protocol/constants.ts
|
|
2752
|
+
var OnDisconnectAction = {
|
|
2753
|
+
SET: "s",
|
|
2754
|
+
UPDATE: "u",
|
|
2755
|
+
DELETE: "d",
|
|
2756
|
+
CANCEL: "c"
|
|
2757
|
+
};
|
|
2758
|
+
var ErrorCode = {
|
|
2759
|
+
PERMISSION_DENIED: "permission_denied",
|
|
2760
|
+
INVALID_DATA: "invalid_data",
|
|
2761
|
+
NOT_FOUND: "not_found",
|
|
2762
|
+
INVALID_PATH: "invalid_path",
|
|
2763
|
+
INVALID_OPERATION: "invalid_operation",
|
|
2764
|
+
INTERNAL_ERROR: "internal_error",
|
|
2765
|
+
CONDITION_FAILED: "condition_failed",
|
|
2766
|
+
INVALID_QUERY: "invalid_query",
|
|
2767
|
+
AUTH_REQUIRED: "auth_required"
|
|
2768
|
+
};
|
|
2769
|
+
var DEFAULT_LARK_DOMAIN = "larkdb.net";
|
|
2770
|
+
|
|
2819
2771
|
// src/OnDisconnect.ts
|
|
2820
2772
|
var OnDisconnect = class {
|
|
2821
2773
|
constructor(db, path) {
|
|
@@ -2919,6 +2871,125 @@ function generatePushId() {
|
|
|
2919
2871
|
return id;
|
|
2920
2872
|
}
|
|
2921
2873
|
|
|
2874
|
+
// src/utils/validation.ts
|
|
2875
|
+
var MAX_KEY_BYTES = 768;
|
|
2876
|
+
var INVALID_KEY_CHARS = /[.#$\[\]\/]/;
|
|
2877
|
+
var CONTROL_CHARS = /[\x00-\x1f\x7f]/;
|
|
2878
|
+
function hasControlChars(str) {
|
|
2879
|
+
return CONTROL_CHARS.test(str);
|
|
2880
|
+
}
|
|
2881
|
+
function getByteLength(str) {
|
|
2882
|
+
if (typeof TextEncoder !== "undefined") {
|
|
2883
|
+
return new TextEncoder().encode(str).length;
|
|
2884
|
+
}
|
|
2885
|
+
return Buffer.byteLength(str, "utf8");
|
|
2886
|
+
}
|
|
2887
|
+
function validateKey(key, context) {
|
|
2888
|
+
const prefix = context ? `${context}: ` : "";
|
|
2889
|
+
if (typeof key !== "string") {
|
|
2890
|
+
throw new LarkError(
|
|
2891
|
+
ErrorCode.INVALID_DATA,
|
|
2892
|
+
`${prefix}key must be a string`
|
|
2893
|
+
);
|
|
2894
|
+
}
|
|
2895
|
+
if (key.length === 0) {
|
|
2896
|
+
throw new LarkError(
|
|
2897
|
+
ErrorCode.INVALID_DATA,
|
|
2898
|
+
`${prefix}key cannot be empty`
|
|
2899
|
+
);
|
|
2900
|
+
}
|
|
2901
|
+
if (INVALID_KEY_CHARS.test(key)) {
|
|
2902
|
+
throw new LarkError(
|
|
2903
|
+
ErrorCode.INVALID_DATA,
|
|
2904
|
+
`${prefix}key "${key}" contains invalid characters (. $ # [ ] / are not allowed)`
|
|
2905
|
+
);
|
|
2906
|
+
}
|
|
2907
|
+
if (hasControlChars(key)) {
|
|
2908
|
+
throw new LarkError(
|
|
2909
|
+
ErrorCode.INVALID_DATA,
|
|
2910
|
+
`${prefix}key "${key}" contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2911
|
+
);
|
|
2912
|
+
}
|
|
2913
|
+
const byteLength = getByteLength(key);
|
|
2914
|
+
if (byteLength > MAX_KEY_BYTES) {
|
|
2915
|
+
throw new LarkError(
|
|
2916
|
+
ErrorCode.INVALID_DATA,
|
|
2917
|
+
`${prefix}key "${key.substring(0, 50)}..." exceeds maximum size of ${MAX_KEY_BYTES} bytes (got ${byteLength})`
|
|
2918
|
+
);
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
function validatePath(path, context) {
|
|
2922
|
+
if (typeof path !== "string") {
|
|
2923
|
+
throw new LarkError(
|
|
2924
|
+
ErrorCode.INVALID_PATH,
|
|
2925
|
+
`${context ? `${context}: ` : ""}path must be a string`
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
if (path === "" || path === "/") {
|
|
2929
|
+
return;
|
|
2930
|
+
}
|
|
2931
|
+
const segments = path.split("/").filter((s) => s.length > 0);
|
|
2932
|
+
for (const segment of segments) {
|
|
2933
|
+
if (segment === ".priority" || segment === ".value" || segment === ".sv" || segment === ".info") {
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2936
|
+
validateKey(segment, context);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
function validateValue(value, context, path = "") {
|
|
2940
|
+
const prefix = context ? `${context}: ` : "";
|
|
2941
|
+
const location = path ? ` at "${path}"` : "";
|
|
2942
|
+
if (value === null || value === void 0) {
|
|
2943
|
+
return;
|
|
2944
|
+
}
|
|
2945
|
+
if (typeof value === "string") {
|
|
2946
|
+
if (hasControlChars(value)) {
|
|
2947
|
+
throw new LarkError(
|
|
2948
|
+
ErrorCode.INVALID_DATA,
|
|
2949
|
+
`${prefix}string value${location} contains control characters (ASCII 0-31, 127 are not allowed)`
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
2955
|
+
return;
|
|
2956
|
+
}
|
|
2957
|
+
if (Array.isArray(value)) {
|
|
2958
|
+
for (let i = 0; i < value.length; i++) {
|
|
2959
|
+
validateValue(value[i], context, path ? `${path}/${i}` : String(i));
|
|
2960
|
+
}
|
|
2961
|
+
return;
|
|
2962
|
+
}
|
|
2963
|
+
if (typeof value === "object") {
|
|
2964
|
+
const obj = value;
|
|
2965
|
+
const keys = Object.keys(obj);
|
|
2966
|
+
if (".value" in obj) {
|
|
2967
|
+
const otherKeys = keys.filter((k) => k !== ".value" && k !== ".priority");
|
|
2968
|
+
if (otherKeys.length > 0) {
|
|
2969
|
+
const loc = path || "/";
|
|
2970
|
+
throw new LarkError(
|
|
2971
|
+
ErrorCode.INVALID_DATA,
|
|
2972
|
+
`${prefix}data at ${loc} contains ".value" alongside other children (${otherKeys.join(", ")}). ".value" can only be used with ".priority" for primitives with priority.`
|
|
2973
|
+
);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
for (const [key, childValue] of Object.entries(obj)) {
|
|
2977
|
+
if (key !== ".priority" && key !== ".value" && key !== ".sv") {
|
|
2978
|
+
validateKey(key, context);
|
|
2979
|
+
}
|
|
2980
|
+
validateValue(childValue, context, path ? `${path}/${key}` : key);
|
|
2981
|
+
}
|
|
2982
|
+
return;
|
|
2983
|
+
}
|
|
2984
|
+
throw new LarkError(
|
|
2985
|
+
ErrorCode.INVALID_DATA,
|
|
2986
|
+
`${prefix}invalid value type${location}: ${typeof value}`
|
|
2987
|
+
);
|
|
2988
|
+
}
|
|
2989
|
+
function validateWriteData(value, context) {
|
|
2990
|
+
validateValue(value, context);
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2922
2993
|
// src/DatabaseReference.ts
|
|
2923
2994
|
function isInfoPath(path) {
|
|
2924
2995
|
return path === "/.info" || path.startsWith("/.info/");
|
|
@@ -2994,7 +3065,6 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
2994
3065
|
* For queries (created via orderBy*, limitTo*, startAt, etc.), this returns
|
|
2995
3066
|
* a reference to the same path without query constraints.
|
|
2996
3067
|
* For non-query references, this returns the reference itself.
|
|
2997
|
-
* This matches Firebase's Query.ref behavior.
|
|
2998
3068
|
*/
|
|
2999
3069
|
get ref() {
|
|
3000
3070
|
if (Object.keys(this._query).length === 0) {
|
|
@@ -3019,7 +3089,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3019
3089
|
* Returns "default" for non-query references (no constraints).
|
|
3020
3090
|
* Returns a sorted JSON string of wire-format params for queries.
|
|
3021
3091
|
*
|
|
3022
|
-
*
|
|
3092
|
+
* Used for wire protocol and subscription deduplication.
|
|
3023
3093
|
*/
|
|
3024
3094
|
get queryIdentifier() {
|
|
3025
3095
|
const queryObj = {};
|
|
@@ -3078,7 +3148,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3078
3148
|
}
|
|
3079
3149
|
/**
|
|
3080
3150
|
* Get the data at this location. Alias for once('value').
|
|
3081
|
-
*
|
|
3151
|
+
* Alternative to once('value') for reading data.
|
|
3082
3152
|
*/
|
|
3083
3153
|
async get() {
|
|
3084
3154
|
return this.once("value");
|
|
@@ -3090,6 +3160,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3090
3160
|
* Get a reference to a child path.
|
|
3091
3161
|
*/
|
|
3092
3162
|
child(path) {
|
|
3163
|
+
validatePath(path, "child");
|
|
3093
3164
|
const childPath = joinPath(this._path, path);
|
|
3094
3165
|
return new _DatabaseReference(this._db, childPath);
|
|
3095
3166
|
}
|
|
@@ -3106,6 +3177,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3106
3177
|
*/
|
|
3107
3178
|
async set(value) {
|
|
3108
3179
|
validateNotInfoPath(this._path, "set");
|
|
3180
|
+
validateWriteData(value, "set");
|
|
3109
3181
|
if (this._db.isVolatilePath(this._path)) {
|
|
3110
3182
|
this._db._sendVolatileSet(this._path, value);
|
|
3111
3183
|
return;
|
|
@@ -3115,7 +3187,7 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3115
3187
|
/**
|
|
3116
3188
|
* Update specific children at this location without overwriting other children.
|
|
3117
3189
|
*
|
|
3118
|
-
* Also supports
|
|
3190
|
+
* Also supports multi-path updates when keys look like paths
|
|
3119
3191
|
* (start with '/'). In this mode, each path is written atomically as a transaction.
|
|
3120
3192
|
*
|
|
3121
3193
|
* @example
|
|
@@ -3135,6 +3207,16 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3135
3207
|
*/
|
|
3136
3208
|
async update(values) {
|
|
3137
3209
|
validateNotInfoPath(this._path, "update");
|
|
3210
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3211
|
+
if (key.includes("/")) {
|
|
3212
|
+
validatePath(key, "update");
|
|
3213
|
+
} else {
|
|
3214
|
+
validateKey(key, "update");
|
|
3215
|
+
}
|
|
3216
|
+
if (value !== null) {
|
|
3217
|
+
validateWriteData(value, "update");
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3138
3220
|
const hasPathKeys = Object.keys(values).some((key) => key.startsWith("/"));
|
|
3139
3221
|
if (hasPathKeys) {
|
|
3140
3222
|
const ops = [];
|
|
@@ -3186,13 +3268,17 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3186
3268
|
* For objects: injects `.priority` into the value object.
|
|
3187
3269
|
* For primitives: wraps as `{ '.value': primitive, '.priority': priority }`.
|
|
3188
3270
|
*
|
|
3189
|
-
*
|
|
3271
|
+
* Uses { '.value': value, '.priority': priority } format for the wire protocol.
|
|
3190
3272
|
*
|
|
3191
3273
|
* @param value - The value to write
|
|
3192
3274
|
* @param priority - The priority for ordering
|
|
3193
3275
|
*/
|
|
3194
3276
|
async setWithPriority(value, priority) {
|
|
3195
3277
|
validateNotInfoPath(this._path, "setWithPriority");
|
|
3278
|
+
validateWriteData(value, "setWithPriority");
|
|
3279
|
+
if (typeof priority === "string") {
|
|
3280
|
+
validateWriteData(priority, "setWithPriority (priority)");
|
|
3281
|
+
}
|
|
3196
3282
|
if (value === null || value === void 0) {
|
|
3197
3283
|
await this._db._sendSet(this._path, value);
|
|
3198
3284
|
return;
|
|
@@ -3734,17 +3820,11 @@ var DatabaseReference = class _DatabaseReference {
|
|
|
3734
3820
|
}
|
|
3735
3821
|
}
|
|
3736
3822
|
/**
|
|
3737
|
-
* Validate that a key is a valid
|
|
3738
|
-
*
|
|
3739
|
-
* Also cannot start or end with .
|
|
3823
|
+
* Validate that a key is a valid key format.
|
|
3824
|
+
* Delegates to the shared validation utility.
|
|
3740
3825
|
*/
|
|
3741
3826
|
_validateKeyFormat(methodName, key) {
|
|
3742
|
-
|
|
3743
|
-
throw new LarkError(
|
|
3744
|
-
ErrorCode.INVALID_QUERY,
|
|
3745
|
-
`Query.${methodName}: invalid key "${key}" - keys cannot contain . # $ [ ] /`
|
|
3746
|
-
);
|
|
3747
|
-
}
|
|
3827
|
+
validateKey(key, `Query.${methodName}`);
|
|
3748
3828
|
}
|
|
3749
3829
|
// ============================================
|
|
3750
3830
|
// Internal Helpers
|
|
@@ -3948,9 +4028,9 @@ var DataSnapshot = class _DataSnapshot {
|
|
|
3948
4028
|
* Get a child snapshot at the specified path.
|
|
3949
4029
|
*
|
|
3950
4030
|
* Special handling:
|
|
3951
|
-
* - `.priority` returns the priority value
|
|
4031
|
+
* - `.priority` returns the priority value
|
|
3952
4032
|
* - For wrapped primitives, only `.priority` returns data; other paths return null
|
|
3953
|
-
* - Non-existent paths return a snapshot with val() === null
|
|
4033
|
+
* - Non-existent paths return a snapshot with val() === null
|
|
3954
4034
|
*/
|
|
3955
4035
|
child(path) {
|
|
3956
4036
|
const childPath = joinPath(this._path, path);
|
|
@@ -4054,7 +4134,6 @@ var DataSnapshot = class _DataSnapshot {
|
|
|
4054
4134
|
}
|
|
4055
4135
|
/**
|
|
4056
4136
|
* Check if this snapshot was from a volatile (high-frequency) update.
|
|
4057
|
-
* This is a Lark extension not present in Firebase.
|
|
4058
4137
|
*/
|
|
4059
4138
|
isVolatile() {
|
|
4060
4139
|
return this._volatile;
|
|
@@ -4063,7 +4142,6 @@ var DataSnapshot = class _DataSnapshot {
|
|
|
4063
4142
|
* Get the server timestamp for this snapshot (milliseconds since Unix epoch).
|
|
4064
4143
|
* Only present on volatile value events. Use deltas between timestamps for
|
|
4065
4144
|
* interpolation rather than absolute times to avoid clock sync issues.
|
|
4066
|
-
* This is a Lark extension not present in Firebase.
|
|
4067
4145
|
*/
|
|
4068
4146
|
getServerTimestamp() {
|
|
4069
4147
|
return this._serverTimestamp;
|
|
@@ -4112,28 +4190,6 @@ function isVolatilePath(path, patterns) {
|
|
|
4112
4190
|
}
|
|
4113
4191
|
|
|
4114
4192
|
// 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
4193
|
var RECONNECT_BASE_DELAY_MS = 1e3;
|
|
4138
4194
|
var RECONNECT_MAX_DELAY_MS = 3e4;
|
|
4139
4195
|
var RECONNECT_JITTER_FACTOR = 0.5;
|
|
@@ -4208,7 +4264,7 @@ var LarkDatabase = class {
|
|
|
4208
4264
|
this._state = "disconnected";
|
|
4209
4265
|
this._auth = null;
|
|
4210
4266
|
this._databaseId = null;
|
|
4211
|
-
this.
|
|
4267
|
+
this._domain = null;
|
|
4212
4268
|
this._volatilePaths = [];
|
|
4213
4269
|
this._transportType = null;
|
|
4214
4270
|
// Auth state
|
|
@@ -4271,8 +4327,9 @@ var LarkDatabase = class {
|
|
|
4271
4327
|
* @internal Get the base URL for reference toString().
|
|
4272
4328
|
*/
|
|
4273
4329
|
_getBaseUrl() {
|
|
4274
|
-
if (this.
|
|
4275
|
-
|
|
4330
|
+
if (this._domain && this._databaseId) {
|
|
4331
|
+
const projectId = this._databaseId.split("/")[0];
|
|
4332
|
+
return `https://${projectId}.${this._domain}`;
|
|
4276
4333
|
}
|
|
4277
4334
|
return "lark://";
|
|
4278
4335
|
}
|
|
@@ -4324,7 +4381,7 @@ var LarkDatabase = class {
|
|
|
4324
4381
|
* Connect to a database.
|
|
4325
4382
|
*
|
|
4326
4383
|
* @param databaseId - Database ID in format "project/database"
|
|
4327
|
-
* @param options - Connection options (token, anonymous,
|
|
4384
|
+
* @param options - Connection options (token, anonymous, domain)
|
|
4328
4385
|
*/
|
|
4329
4386
|
async connect(databaseId, options = {}) {
|
|
4330
4387
|
if (this._state !== "disconnected") {
|
|
@@ -4348,19 +4405,17 @@ var LarkDatabase = class {
|
|
|
4348
4405
|
const previousState = this._state;
|
|
4349
4406
|
this._state = isReconnect ? "reconnecting" : "connecting";
|
|
4350
4407
|
this._databaseId = databaseId;
|
|
4351
|
-
this.
|
|
4408
|
+
this._domain = options.domain || DEFAULT_LARK_DOMAIN;
|
|
4352
4409
|
if (!isReconnect) {
|
|
4353
4410
|
this._currentToken = options.token || "";
|
|
4354
4411
|
this._isAnonymous = !options.token && options.anonymous !== false;
|
|
4355
4412
|
}
|
|
4356
4413
|
try {
|
|
4357
|
-
const
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
});
|
|
4363
|
-
const wsUrl = connectResponse.ws_url;
|
|
4414
|
+
const projectId = databaseId.split("/")[0];
|
|
4415
|
+
if (!projectId) {
|
|
4416
|
+
throw new Error('Invalid database ID: must be in format "projectId/databaseName"');
|
|
4417
|
+
}
|
|
4418
|
+
const wsUrl = `wss://${projectId}.${this._domain}/ws`;
|
|
4364
4419
|
const transportResult = await createTransport(
|
|
4365
4420
|
wsUrl,
|
|
4366
4421
|
{
|
|
@@ -4521,7 +4576,7 @@ var LarkDatabase = class {
|
|
|
4521
4576
|
this._auth = null;
|
|
4522
4577
|
this._databaseId = null;
|
|
4523
4578
|
this._volatilePaths = [];
|
|
4524
|
-
this.
|
|
4579
|
+
this._domain = null;
|
|
4525
4580
|
this._connectionId = null;
|
|
4526
4581
|
this._connectOptions = null;
|
|
4527
4582
|
this._transportType = null;
|
|
@@ -4718,7 +4773,7 @@ var LarkDatabase = class {
|
|
|
4718
4773
|
*
|
|
4719
4774
|
* Supports two syntaxes:
|
|
4720
4775
|
*
|
|
4721
|
-
* **Object syntax** (
|
|
4776
|
+
* **Object syntax** (multi-path update):
|
|
4722
4777
|
* ```javascript
|
|
4723
4778
|
* await db.transaction({
|
|
4724
4779
|
* '/users/alice/name': 'Alice',
|
|
@@ -4756,14 +4811,20 @@ var LarkDatabase = class {
|
|
|
4756
4811
|
*/
|
|
4757
4812
|
async convertToTxOp(op) {
|
|
4758
4813
|
const path = normalizePath(op.path) || "/";
|
|
4814
|
+
validatePath(op.path, "transaction");
|
|
4759
4815
|
switch (op.op) {
|
|
4760
4816
|
case "set":
|
|
4817
|
+
validateWriteData(op.value, "transaction");
|
|
4761
4818
|
return { o: "s", p: path, v: op.value };
|
|
4762
4819
|
case "update":
|
|
4820
|
+
validateWriteData(op.value, "transaction");
|
|
4763
4821
|
return { o: "u", p: path, v: op.value };
|
|
4764
4822
|
case "delete":
|
|
4765
4823
|
return { o: "d", p: path };
|
|
4766
4824
|
case "condition":
|
|
4825
|
+
if (op.value !== void 0 && op.value !== null) {
|
|
4826
|
+
validateWriteData(op.value, "transaction condition");
|
|
4827
|
+
}
|
|
4767
4828
|
if (isPrimitive(op.value)) {
|
|
4768
4829
|
return { o: "c", p: path, v: op.value };
|
|
4769
4830
|
} else {
|
|
@@ -4781,10 +4842,12 @@ var LarkDatabase = class {
|
|
|
4781
4842
|
convertObjectToTxOps(obj) {
|
|
4782
4843
|
const ops = [];
|
|
4783
4844
|
for (const [path, value] of Object.entries(obj)) {
|
|
4845
|
+
validatePath(path, "transaction");
|
|
4784
4846
|
const normalizedPath = normalizePath(path) || "/";
|
|
4785
4847
|
if (value === null) {
|
|
4786
4848
|
ops.push({ o: "d", p: normalizedPath });
|
|
4787
4849
|
} else {
|
|
4850
|
+
validateWriteData(value, "transaction");
|
|
4788
4851
|
ops.push({ o: "s", p: normalizedPath, v: value });
|
|
4789
4852
|
}
|
|
4790
4853
|
}
|