@liveblocks/core 3.5.0 → 3.5.2

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/index.js CHANGED
@@ -6,7 +6,7 @@ var __export = (target, all) => {
6
6
 
7
7
  // src/version.ts
8
8
  var PKG_NAME = "@liveblocks/core";
9
- var PKG_VERSION = "3.5.0";
9
+ var PKG_VERSION = "3.5.2";
10
10
  var PKG_FORMAT = "esm";
11
11
 
12
12
  // src/dupe-detection.ts
@@ -3817,99 +3817,174 @@ function parseAuthToken(rawTokenString) {
3817
3817
  };
3818
3818
  }
3819
3819
 
3820
- // src/lib/parsePartialJsonObject.ts
3821
- function parsePartialJsonObject(partial) {
3822
- partial = partial.trimStart();
3823
- if (partial.charAt(0) !== "{") {
3824
- return {};
3825
- }
3826
- if (partial.trimEnd().endsWith("}")) {
3827
- const quickCheck = tryParseJson(partial);
3828
- if (quickCheck) {
3829
- return quickCheck;
3830
- }
3831
- }
3832
- let result = partial;
3833
- let quoteCount = 0;
3834
- let escaped = false;
3835
- for (let i = 0; i < result.length; i++) {
3836
- const char = result[i];
3837
- if (escaped) {
3838
- escaped = false;
3839
- } else if (char === "\\") {
3840
- escaped = true;
3841
- } else if (char === '"') {
3842
- quoteCount++;
3843
- }
3844
- }
3845
- if (quoteCount % 2 === 1) {
3846
- result += '"';
3847
- }
3848
- result = result.trimEnd();
3849
- if (result.endsWith(",")) {
3850
- result = result.slice(0, -1);
3851
- }
3852
- if (result.endsWith(".")) {
3853
- result = result.slice(0, -1);
3854
- }
3855
- const stack = [];
3856
- let inString = false;
3857
- escaped = false;
3858
- for (let i = 0; i < result.length; i++) {
3859
- const char = result[i];
3860
- if (inString) {
3861
- if (escaped) {
3862
- escaped = false;
3863
- } else if (char === "\\") {
3864
- escaped = true;
3865
- } else if (char === '"') {
3866
- inString = false;
3867
- }
3868
- } else {
3869
- if (char === '"') {
3870
- inString = true;
3871
- } else if (char === "{") {
3872
- stack.push("}");
3873
- } else if (char === "[") {
3874
- stack.push("]");
3875
- } else if (char === "}" && stack.length > 0 && stack[stack.length - 1] === "}") {
3876
- stack.pop();
3877
- } else if (char === "]" && stack.length > 0 && stack[stack.length - 1] === "]") {
3878
- stack.pop();
3820
+ // src/lib/IncrementalJsonParser.ts
3821
+ var EMPTY_OBJECT = Object.freeze({});
3822
+ var NULL_KEYWORD_CHARS = Array.from(new Set("null"));
3823
+ var TRUE_KEYWORD_CHARS = Array.from(new Set("true"));
3824
+ var FALSE_KEYWORD_CHARS = Array.from(new Set("false"));
3825
+ var ALL_KEYWORD_CHARS = Array.from(new Set("nulltruefalse"));
3826
+ function stripChar(str, chars) {
3827
+ const lastChar = str[str.length - 1];
3828
+ if (chars.includes(lastChar)) {
3829
+ return str.slice(0, -1);
3830
+ }
3831
+ return str;
3832
+ }
3833
+ var IncrementalJsonParser = class {
3834
+ // Input
3835
+ #sourceText = "";
3836
+ // Output
3837
+ #cachedJson;
3838
+ /** How much we've already parsed */
3839
+ #scanIndex = 0;
3840
+ /** Whether the last char processed was a backslash */
3841
+ #escaped = false;
3842
+ /**
3843
+ * Start position of the last unterminated string, -1 if we're not inside
3844
+ * a string currently.
3845
+ *
3846
+ * Example: '{"a": "foo'
3847
+ * ^
3848
+ */
3849
+ #lastUnterminatedString = -1;
3850
+ /**
3851
+ * Start position of the last fully terminated string we've seen.
3852
+ *
3853
+ * Example: '{"a": "foo'
3854
+ * ^
3855
+ */
3856
+ #lastTerminatedString = -1;
3857
+ /** The bracket stack of expected closing chars. For input '{"a": ["foo', the stack would be ['}', ']']. */
3858
+ #stack = [];
3859
+ constructor(text = "") {
3860
+ this.append(text);
3861
+ }
3862
+ get source() {
3863
+ return this.#sourceText;
3864
+ }
3865
+ get json() {
3866
+ if (this.#cachedJson === void 0) {
3867
+ this.#cachedJson = this.#parse();
3868
+ }
3869
+ return this.#cachedJson;
3870
+ }
3871
+ /** Whether we're currently inside an unterminated string, e.g. '{"hello' */
3872
+ get #inString() {
3873
+ return this.#lastUnterminatedString >= 0;
3874
+ }
3875
+ append(delta) {
3876
+ if (delta) {
3877
+ if (this.#sourceText === "") {
3878
+ delta = delta.trimStart();
3879
3879
  }
3880
+ this.#sourceText += delta;
3881
+ this.#cachedJson = void 0;
3880
3882
  }
3881
3883
  }
3882
- const suffix = stack.reverse().join("");
3883
- {
3884
- const attempt = tryParseJson(result + suffix);
3885
- if (attempt) {
3886
- return attempt;
3887
- }
3888
- }
3889
- if (result.endsWith(":")) {
3890
- result = result.slice(0, -1);
3891
- }
3892
- if (result.endsWith('"')) {
3893
- let pos = result.length - 2;
3894
- escaped = false;
3895
- while (pos >= 0) {
3896
- if (escaped) {
3897
- escaped = false;
3898
- } else if (result[pos] === "\\") {
3899
- escaped = true;
3900
- } else if (result[pos] === '"') {
3901
- result = result.slice(0, pos);
3902
- break;
3884
+ #autocompleteTail(output) {
3885
+ if (this.#inString) {
3886
+ return "";
3887
+ }
3888
+ const lastChar = output.charAt(output.length - 1);
3889
+ if (lastChar === "") return "";
3890
+ if (lastChar === "-") {
3891
+ return "0";
3892
+ }
3893
+ if (!ALL_KEYWORD_CHARS.includes(lastChar)) return "";
3894
+ if (NULL_KEYWORD_CHARS.includes(lastChar)) {
3895
+ if (output.endsWith("nul")) return "l";
3896
+ if (output.endsWith("nu")) return "ll";
3897
+ if (output.endsWith("n")) return "ull";
3898
+ }
3899
+ if (TRUE_KEYWORD_CHARS.includes(lastChar)) {
3900
+ if (output.endsWith("tru")) return "e";
3901
+ if (output.endsWith("tr")) return "ue";
3902
+ if (output.endsWith("t")) return "rue";
3903
+ }
3904
+ if (FALSE_KEYWORD_CHARS.includes(lastChar)) {
3905
+ if (output.endsWith("fals")) return "e";
3906
+ if (output.endsWith("fal")) return "se";
3907
+ if (output.endsWith("fa")) return "lse";
3908
+ if (output.endsWith("f")) return "alse";
3909
+ }
3910
+ return "";
3911
+ }
3912
+ /**
3913
+ * Updates the internal parsing state by processing any new content
3914
+ * that has been appended since the last parse. This updates the state with
3915
+ * facts only. Any interpretation is left to the #parse() method.
3916
+ */
3917
+ #catchup() {
3918
+ const newContent = this.#sourceText.slice(this.#scanIndex);
3919
+ for (let i = 0; i < newContent.length; i++) {
3920
+ const ch = newContent[i];
3921
+ const absolutePos = this.#scanIndex + i;
3922
+ if (this.#inString) {
3923
+ if (this.#escaped) {
3924
+ this.#escaped = false;
3925
+ } else if (ch === "\\") {
3926
+ this.#escaped = true;
3927
+ } else if (ch === '"') {
3928
+ this.#lastTerminatedString = this.#lastUnterminatedString;
3929
+ this.#lastUnterminatedString = -1;
3930
+ }
3931
+ } else {
3932
+ if (ch === '"') {
3933
+ this.#lastUnterminatedString = absolutePos;
3934
+ } else if (ch === "{") {
3935
+ this.#stack.push("}");
3936
+ } else if (ch === "[") {
3937
+ this.#stack.push("]");
3938
+ } else if (ch === "}" && this.#stack.length > 0 && this.#stack[this.#stack.length - 1] === "}") {
3939
+ this.#stack.pop();
3940
+ } else if (ch === "]" && this.#stack.length > 0 && this.#stack[this.#stack.length - 1] === "]") {
3941
+ this.#stack.pop();
3942
+ }
3903
3943
  }
3904
- pos--;
3905
3944
  }
3945
+ this.#scanIndex = this.#sourceText.length;
3906
3946
  }
3907
- if (result.endsWith(",")) {
3908
- result = result.slice(0, -1);
3947
+ #parse() {
3948
+ this.#catchup();
3949
+ let result = this.#sourceText;
3950
+ if (result.charAt(0) !== "{") {
3951
+ return EMPTY_OBJECT;
3952
+ }
3953
+ if (result.endsWith("}")) {
3954
+ const quickCheck = tryParseJson(result);
3955
+ if (quickCheck) {
3956
+ return quickCheck;
3957
+ }
3958
+ }
3959
+ if (this.#inString) {
3960
+ if (this.#escaped) {
3961
+ result = result.slice(0, -1);
3962
+ }
3963
+ result += '"';
3964
+ }
3965
+ result = result.trimEnd();
3966
+ result = stripChar(result, ",.");
3967
+ result = result + this.#autocompleteTail(result);
3968
+ const suffix = this.#stack.reduceRight((acc, ch) => acc + ch, "");
3969
+ {
3970
+ const attempt = tryParseJson(result + suffix);
3971
+ if (attempt) {
3972
+ return attempt;
3973
+ }
3974
+ }
3975
+ if (this.#inString) {
3976
+ result = result.slice(0, this.#lastUnterminatedString);
3977
+ } else {
3978
+ result = stripChar(result, ":");
3979
+ if (result.endsWith('"')) {
3980
+ result = result.slice(0, this.#lastTerminatedString);
3981
+ }
3982
+ }
3983
+ result = stripChar(result, ",");
3984
+ result += suffix;
3985
+ return tryParseJson(result) ?? EMPTY_OBJECT;
3909
3986
  }
3910
- result += suffix;
3911
- return tryParseJson(result) ?? {};
3912
- }
3987
+ };
3913
3988
 
3914
3989
  // src/types/ai.ts
3915
3990
  function patchContentWithDelta(content, delta) {
@@ -3933,28 +4008,16 @@ function patchContentWithDelta(content, delta) {
3933
4008
  }
3934
4009
  break;
3935
4010
  case "tool-stream": {
3936
- let _cacheKey = "";
3937
- let _cachedArgs = {};
3938
- const toolInvocation = {
3939
- type: "tool-invocation",
3940
- stage: "receiving",
3941
- invocationId: delta.invocationId,
3942
- name: delta.name,
3943
- partialArgsText: "",
3944
- get partialArgs() {
3945
- if (this.partialArgsText !== _cacheKey) {
3946
- _cachedArgs = parsePartialJsonObject(this.partialArgsText);
3947
- _cacheKey = this.partialArgsText;
3948
- }
3949
- return _cachedArgs;
3950
- }
3951
- };
4011
+ const toolInvocation = createReceivingToolInvocation(
4012
+ delta.invocationId,
4013
+ delta.name
4014
+ );
3952
4015
  content.push(toolInvocation);
3953
4016
  break;
3954
4017
  }
3955
4018
  case "tool-delta": {
3956
4019
  if (lastPart?.type === "tool-invocation" && lastPart.stage === "receiving") {
3957
- lastPart.partialArgsText += delta.delta;
4020
+ lastPart.__appendDelta?.(delta.delta);
3958
4021
  }
3959
4022
  break;
3960
4023
  }
@@ -3974,6 +4037,25 @@ function patchContentWithDelta(content, delta) {
3974
4037
  return assertNever(delta, "Unhandled case");
3975
4038
  }
3976
4039
  }
4040
+ function createReceivingToolInvocation(invocationId, name, partialArgsText = "") {
4041
+ const parser = new IncrementalJsonParser(partialArgsText);
4042
+ return {
4043
+ type: "tool-invocation",
4044
+ stage: "receiving",
4045
+ invocationId,
4046
+ name,
4047
+ get partialArgsText() {
4048
+ return parser.source;
4049
+ },
4050
+ get partialArgs() {
4051
+ return parser.json;
4052
+ },
4053
+ // Internal method to append deltas
4054
+ __appendDelta(delta) {
4055
+ parser.append(delta);
4056
+ }
4057
+ };
4058
+ }
3977
4059
 
3978
4060
  // src/ai.ts
3979
4061
  var DEFAULT_REQUEST_TIMEOUT = 4e3;
@@ -4425,6 +4507,28 @@ function createAi(config) {
4425
4507
  toolsStore,
4426
4508
  knowledge: new KnowledgeStack()
4427
4509
  };
4510
+ const DELTA_THROTTLE = 25;
4511
+ let pendingDeltas = [];
4512
+ let deltaBatchTimer = null;
4513
+ function flushPendingDeltas() {
4514
+ const currentQueue = pendingDeltas;
4515
+ pendingDeltas = [];
4516
+ if (deltaBatchTimer !== null) {
4517
+ clearTimeout(deltaBatchTimer);
4518
+ deltaBatchTimer = null;
4519
+ }
4520
+ batch(() => {
4521
+ for (const { id, delta } of currentQueue) {
4522
+ context.messagesStore.addDelta(id, delta);
4523
+ }
4524
+ });
4525
+ }
4526
+ function enqueueDelta(id, delta) {
4527
+ pendingDeltas.push({ id, delta });
4528
+ if (deltaBatchTimer === null) {
4529
+ deltaBatchTimer = setTimeout(flushPendingDeltas, DELTA_THROTTLE);
4530
+ }
4531
+ }
4428
4532
  let lastTokenKey;
4429
4533
  function onStatusDidChange(_newStatus) {
4430
4534
  const authValue = managedSocket.authValue;
@@ -4464,6 +4568,7 @@ function createAi(config) {
4464
4568
  function onDidConnect() {
4465
4569
  }
4466
4570
  function onDidDisconnect() {
4571
+ flushPendingDeltas();
4467
4572
  }
4468
4573
  function handleServerMessage(event) {
4469
4574
  if (typeof event.data !== "string")
@@ -4478,50 +4583,51 @@ function createAi(config) {
4478
4583
  return;
4479
4584
  }
4480
4585
  if ("event" in msg) {
4481
- switch (msg.event) {
4482
- case "cmd-failed":
4483
- pendingCmd?.reject(new Error(msg.error));
4484
- break;
4485
- case "delta": {
4486
- const { id, delta } = msg;
4487
- context.messagesStore.addDelta(id, delta);
4488
- break;
4489
- }
4490
- case "settle": {
4491
- context.messagesStore.upsert(msg.message);
4492
- break;
4493
- }
4494
- case "warning":
4495
- warn(msg.message);
4496
- break;
4497
- case "error":
4498
- error2(msg.error);
4499
- break;
4500
- case "rebooted":
4501
- context.messagesStore.failAllPending();
4502
- break;
4503
- case "sync":
4504
- batch(() => {
4505
- for (const m of msg["-messages"] ?? []) {
4506
- context.messagesStore.remove(m.chatId, m.id);
4507
- }
4508
- for (const chatId of msg["-chats"] ?? []) {
4509
- context.chatsStore.markDeleted(chatId);
4510
- context.messagesStore.removeByChatId(chatId);
4511
- }
4512
- for (const chatId of msg.clear ?? []) {
4513
- context.messagesStore.removeByChatId(chatId);
4514
- }
4515
- if (msg.chats) {
4516
- context.chatsStore.upsertMany(msg.chats);
4517
- }
4518
- if (msg.messages) {
4519
- context.messagesStore.upsertMany(msg.messages);
4586
+ if (msg.event === "delta") {
4587
+ const { id, delta } = msg;
4588
+ enqueueDelta(id, delta);
4589
+ } else {
4590
+ batch(() => {
4591
+ flushPendingDeltas();
4592
+ switch (msg.event) {
4593
+ case "cmd-failed":
4594
+ pendingCmd?.reject(new Error(msg.error));
4595
+ break;
4596
+ case "settle": {
4597
+ context.messagesStore.upsert(msg.message);
4598
+ break;
4520
4599
  }
4521
- });
4522
- break;
4523
- default:
4524
- return assertNever(msg, "Unhandled case");
4600
+ case "warning":
4601
+ warn(msg.message);
4602
+ break;
4603
+ case "error":
4604
+ error2(msg.error);
4605
+ break;
4606
+ case "rebooted":
4607
+ context.messagesStore.failAllPending();
4608
+ break;
4609
+ case "sync":
4610
+ for (const m of msg["-messages"] ?? []) {
4611
+ context.messagesStore.remove(m.chatId, m.id);
4612
+ }
4613
+ for (const chatId of msg["-chats"] ?? []) {
4614
+ context.chatsStore.markDeleted(chatId);
4615
+ context.messagesStore.removeByChatId(chatId);
4616
+ }
4617
+ for (const chatId of msg.clear ?? []) {
4618
+ context.messagesStore.removeByChatId(chatId);
4619
+ }
4620
+ if (msg.chats) {
4621
+ context.chatsStore.upsertMany(msg.chats);
4622
+ }
4623
+ if (msg.messages) {
4624
+ context.messagesStore.upsertMany(msg.messages);
4625
+ }
4626
+ break;
4627
+ default:
4628
+ return assertNever(msg, "Unhandled case");
4629
+ }
4630
+ });
4525
4631
  }
4526
4632
  } else {
4527
4633
  switch (msg.cmd) {