@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.
@@ -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
- * @param options - Either a user token or anonymous flag
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, options = {}) {
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 Firebase key format.
3693
- * Invalid characters: . $ # [ ] /
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
- if (/[.#$\[\]\/]/.test(key)) {
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-EK7OYLDG.mjs.map
5392
+ //# sourceMappingURL=chunk-CYCSP6TZ.mjs.map