@openclaw/feishu 2026.5.24-beta.2 → 2026.5.26-beta.1

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.
@@ -1,9 +1,9 @@
1
1
  import { i as resolveReceiveIdType, r as normalizeFeishuTarget } from "./targets-BUjQ1TcA.js";
2
- import { c as createFeishuApiError, f as isRecord$2, g as requestFeishuApi, s as resolveFeishuRuntimeAccount } from "./accounts-CRcvqpsl.js";
3
- import { c as toFeishuSendResult, o as assertFeishuMessageApiSuccess, s as resolveFeishuReceiptKind } from "./channel-Br99IozO.js";
2
+ import { c as createFeishuApiError, f as isRecord$1, g as requestFeishuApi, s as resolveFeishuRuntimeAccount } from "./accounts-CXnY5H8g.js";
3
+ import { i as toFeishuSendResult, r as resolveFeishuReceiptKind, t as assertFeishuMessageApiSuccess } from "./send-result-CHvu8Rr7.js";
4
4
  import { t as getFeishuRuntime } from "./runtime-C5JxBWZp.js";
5
5
  import { r as createFeishuClient } from "./client-BnH2fRL2.js";
6
- import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
+ import { isRecord, normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { convertMarkdownTables } from "openclaw/plugin-sdk/text-chunking";
8
8
  import fs from "node:fs";
9
9
  import path from "node:path";
@@ -657,7 +657,7 @@ function sanitizeFenceLanguage(language) {
657
657
  }
658
658
  function renderTextElement(element) {
659
659
  const text = toStringOrEmpty(element.text);
660
- const style = isRecord$2(element.style) ? element.style : void 0;
660
+ const style = isRecord$1(element.style) ? element.style : void 0;
661
661
  if (isStyleEnabled(style, "code")) return wrapInlineCode(text);
662
662
  let rendered = escapeMarkdownText(text);
663
663
  if (!rendered) return "";
@@ -688,7 +688,7 @@ function renderCodeBlockElement(element) {
688
688
  return `\`\`\`${language}\n${code}${code.endsWith("\n") ? "" : "\n"}\`\`\``;
689
689
  }
690
690
  function renderElement(element, imageKeys, mediaKeys, mentionedOpenIds) {
691
- if (!isRecord$2(element)) return escapeMarkdownText(toStringOrEmpty(element));
691
+ if (!isRecord$1(element)) return escapeMarkdownText(toStringOrEmpty(element));
692
692
  switch (normalizeLowercaseStringOrEmpty(toStringOrEmpty(element.tag))) {
693
693
  case "text": return renderTextElement(element);
694
694
  case "a": return renderLinkElement(element);
@@ -729,7 +729,7 @@ function renderElement(element, imageKeys, mediaKeys, mentionedOpenIds) {
729
729
  }
730
730
  }
731
731
  function toPostPayload(candidate) {
732
- if (!isRecord$2(candidate) || !Array.isArray(candidate.content)) return null;
732
+ if (!isRecord$1(candidate) || !Array.isArray(candidate.content)) return null;
733
733
  return {
734
734
  title: toStringOrEmpty(candidate.title),
735
735
  content: candidate.content
@@ -738,7 +738,7 @@ function toPostPayload(candidate) {
738
738
  function resolveLocalePayload(candidate) {
739
739
  const direct = toPostPayload(candidate);
740
740
  if (direct) return direct;
741
- if (!isRecord$2(candidate)) return null;
741
+ if (!isRecord$1(candidate)) return null;
742
742
  for (const value of Object.values(candidate)) {
743
743
  const localePayload = toPostPayload(value);
744
744
  if (localePayload) return localePayload;
@@ -748,7 +748,7 @@ function resolveLocalePayload(candidate) {
748
748
  function resolvePostPayload(parsed) {
749
749
  const direct = toPostPayload(parsed);
750
750
  if (direct) return direct;
751
- if (!isRecord$2(parsed)) return null;
751
+ if (!isRecord$1(parsed)) return null;
752
752
  const wrappedPost = resolveLocalePayload(parsed.post);
753
753
  if (wrappedPost) return wrappedPost;
754
754
  return resolveLocalePayload(parsed);
@@ -821,9 +821,6 @@ function isWithdrawnReplyError(err) {
821
821
  if (typeof response?.data?.code === "number" && WITHDRAWN_REPLY_ERROR_CODES.has(response.data.code)) return true;
822
822
  return false;
823
823
  }
824
- function isRecord$1(value) {
825
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
826
- }
827
824
  /** Send a direct message as a fallback when a reply target is unavailable. */
828
825
  async function sendFallbackDirect(client, params, errorPrefix) {
829
826
  const response = await requestFeishuApi(() => client.im.message.create({
@@ -869,7 +866,7 @@ function normalizeCardTemplateVariable(value) {
869
866
  function readCardTemplateVariables(parsed) {
870
867
  const variables = /* @__PURE__ */ new Map();
871
868
  for (const source of [parsed.template_variable, parsed.template_variables]) {
872
- if (!isRecord$1(source)) continue;
869
+ if (!isRecord(source)) continue;
873
870
  for (const [key, value] of Object.entries(source)) {
874
871
  const normalized = normalizeCardTemplateVariable(value);
875
872
  if (normalized !== void 0) variables.set(key, normalized);
@@ -885,9 +882,9 @@ function applyCardTemplateVariables(text, variables) {
885
882
  });
886
883
  }
887
884
  function extractInteractiveElementText(element, variables) {
888
- if (!isRecord$1(element)) return;
885
+ if (!isRecord(element)) return;
889
886
  const tag = typeof element.tag === "string" ? element.tag : "";
890
- const text = isRecord$1(element.text) ? element.text : void 0;
887
+ const text = isRecord(element.text) ? element.text : void 0;
891
888
  if (tag === "div" && typeof text?.content === "string") return applyCardTemplateVariables(text.content, variables);
892
889
  if ((tag === "markdown" || tag === "lark_md") && typeof element.content === "string") return applyCardTemplateVariables(element.content, variables);
893
890
  if (tag === "plain_text" && typeof element.content === "string") return applyCardTemplateVariables(element.content, variables);
@@ -901,11 +898,11 @@ function extractInteractiveElementsText(elements, variables) {
901
898
  return texts.join("\n").trim();
902
899
  }
903
900
  function readInteractiveElementArrays(parsed) {
904
- const body = isRecord$1(parsed.body) ? parsed.body : void 0;
901
+ const body = isRecord(parsed.body) ? parsed.body : void 0;
905
902
  const elementArrays = [];
906
903
  for (const candidate of [parsed.elements, body?.elements]) if (Array.isArray(candidate)) elementArrays.push(candidate);
907
904
  for (const candidate of [parsed.i18n_elements, body?.i18n_elements]) {
908
- if (!isRecord$1(candidate)) continue;
905
+ if (!isRecord(candidate)) continue;
909
906
  for (const localeElements of Object.values(candidate)) if (Array.isArray(localeElements)) elementArrays.push(localeElements);
910
907
  }
911
908
  return elementArrays;
@@ -915,7 +912,7 @@ function parseInteractivePostFallback(parsed) {
915
912
  return textContent && textContent !== POST_FALLBACK_TEXT ? textContent : void 0;
916
913
  }
917
914
  function parseInteractiveCardContent(parsed) {
918
- if (!isRecord$1(parsed)) return INTERACTIVE_CARD_FALLBACK_TEXT;
915
+ if (!isRecord(parsed)) return INTERACTIVE_CARD_FALLBACK_TEXT;
919
916
  const variables = readCardTemplateVariables(parsed);
920
917
  for (const elements of readInteractiveElementArrays(parsed)) {
921
918
  const text = extractInteractiveElementsText(elements, variables);
@@ -0,0 +1,140 @@
1
+ import { f as isRecord } from "./accounts-CXnY5H8g.js";
2
+ import { createMessageReceiptFromOutboundResults } from "openclaw/plugin-sdk/channel-message";
3
+ //#region extensions/feishu/src/card-interaction.ts
4
+ const FEISHU_CARD_INTERACTION_VERSION = "ocf1";
5
+ function isInteractionKind(value) {
6
+ return value === "button" || value === "quick" || value === "meta";
7
+ }
8
+ function isMetadataValue(value) {
9
+ return value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
10
+ }
11
+ function createFeishuCardInteractionEnvelope(envelope) {
12
+ return {
13
+ oc: FEISHU_CARD_INTERACTION_VERSION,
14
+ ...envelope
15
+ };
16
+ }
17
+ function buildFeishuCardActionTextFallback(event) {
18
+ const actionValue = event.action.value;
19
+ if (isRecord(actionValue)) {
20
+ if (typeof actionValue.text === "string") return actionValue.text;
21
+ if (typeof actionValue.command === "string") return actionValue.command;
22
+ return JSON.stringify(actionValue);
23
+ }
24
+ return String(actionValue);
25
+ }
26
+ function decodeFeishuCardAction(params) {
27
+ const { event, now = Date.now() } = params;
28
+ const actionValue = event.action.value;
29
+ if (!isRecord(actionValue) || actionValue.oc !== "ocf1") return {
30
+ kind: "legacy",
31
+ text: buildFeishuCardActionTextFallback(event)
32
+ };
33
+ if (!isInteractionKind(actionValue.k) || typeof actionValue.a !== "string" || !actionValue.a) return {
34
+ kind: "invalid",
35
+ reason: "malformed"
36
+ };
37
+ if (actionValue.q !== void 0 && typeof actionValue.q !== "string") return {
38
+ kind: "invalid",
39
+ reason: "malformed"
40
+ };
41
+ if (actionValue.m !== void 0) {
42
+ if (!isRecord(actionValue.m)) return {
43
+ kind: "invalid",
44
+ reason: "malformed"
45
+ };
46
+ for (const value of Object.values(actionValue.m)) if (!isMetadataValue(value)) return {
47
+ kind: "invalid",
48
+ reason: "malformed"
49
+ };
50
+ }
51
+ if (actionValue.c !== void 0) {
52
+ if (!isRecord(actionValue.c)) return {
53
+ kind: "invalid",
54
+ reason: "malformed"
55
+ };
56
+ if (actionValue.c.u !== void 0 && typeof actionValue.c.u !== "string") return {
57
+ kind: "invalid",
58
+ reason: "malformed"
59
+ };
60
+ if (actionValue.c.h !== void 0 && typeof actionValue.c.h !== "string") return {
61
+ kind: "invalid",
62
+ reason: "malformed"
63
+ };
64
+ if (actionValue.c.s !== void 0 && typeof actionValue.c.s !== "string") return {
65
+ kind: "invalid",
66
+ reason: "malformed"
67
+ };
68
+ if (actionValue.c.e !== void 0 && !Number.isFinite(actionValue.c.e)) return {
69
+ kind: "invalid",
70
+ reason: "malformed"
71
+ };
72
+ if (actionValue.c.t !== void 0 && actionValue.c.t !== "p2p" && actionValue.c.t !== "group") return {
73
+ kind: "invalid",
74
+ reason: "malformed"
75
+ };
76
+ if (typeof actionValue.c.e === "number" && actionValue.c.e < now) return {
77
+ kind: "invalid",
78
+ reason: "stale"
79
+ };
80
+ const expectedUser = actionValue.c.u?.trim();
81
+ if (expectedUser && expectedUser !== (event.operator.open_id ?? "").trim()) return {
82
+ kind: "invalid",
83
+ reason: "wrong_user"
84
+ };
85
+ const expectedChat = actionValue.c.h?.trim();
86
+ if (expectedChat && expectedChat !== (event.context.chat_id ?? "").trim()) return {
87
+ kind: "invalid",
88
+ reason: "wrong_conversation"
89
+ };
90
+ }
91
+ return {
92
+ kind: "structured",
93
+ envelope: actionValue
94
+ };
95
+ }
96
+ //#endregion
97
+ //#region extensions/feishu/src/send-result.ts
98
+ function resolveFeishuReceiptKind(msgType) {
99
+ switch (msgType) {
100
+ case "audio": return "voice";
101
+ case "image":
102
+ case "media":
103
+ case "file": return "media";
104
+ case "interactive": return "card";
105
+ case "post":
106
+ case "text": return "text";
107
+ default: return "unknown";
108
+ }
109
+ }
110
+ function createFeishuSendReceipt(params) {
111
+ const messageId = params.messageId?.trim();
112
+ const chatId = params.chatId.trim();
113
+ return createMessageReceiptFromOutboundResults({
114
+ results: messageId ? [{
115
+ channel: "feishu",
116
+ messageId,
117
+ chatId,
118
+ conversationId: chatId
119
+ }] : [],
120
+ ...chatId ? { threadId: chatId } : {},
121
+ kind: params.kind ?? "unknown"
122
+ });
123
+ }
124
+ function assertFeishuMessageApiSuccess(response, errorPrefix) {
125
+ if (response.code !== 0) throw new Error(`${errorPrefix}: ${response.msg || `code ${response.code}`}`);
126
+ }
127
+ function toFeishuSendResult(response, chatId, kind) {
128
+ const messageId = response.data?.message_id ?? "unknown";
129
+ return {
130
+ messageId,
131
+ chatId,
132
+ receipt: createFeishuSendReceipt({
133
+ messageId,
134
+ chatId,
135
+ kind
136
+ })
137
+ };
138
+ }
139
+ //#endregion
140
+ export { FEISHU_CARD_INTERACTION_VERSION as a, decodeFeishuCardAction as c, toFeishuSendResult as i, createFeishuSendReceipt as n, buildFeishuCardActionTextFallback as o, resolveFeishuReceiptKind as r, createFeishuCardInteractionEnvelope as s, assertFeishuMessageApiSuccess as t };
package/dist/setup-api.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-Br99IozO.js";
1
+ import { i as feishuSetupAdapter, n as feishuSetupWizard, t as feishuPlugin } from "./channel-DavfT_AA.js";
2
2
  export { feishuPlugin, feishuSetupAdapter, feishuSetupWizard };
@@ -1,3 +1,23 @@
1
+ ## **6.15.2**
2
+ - [Fix] `stringify`: skip null/undefined entries in `arrayFormat: 'comma'` + `encodeValuesOnly` instead of crashing in `encoder`
3
+ - [Fix] `stringify`: use configured `delimiter` after `charsetSentinel` (#555)
4
+ - [Fix] `stringify`: apply `formatter` to encoded key under `strictNullHandling` (#554)
5
+ - [Fix] `stringify`: skip null/undefined filter-array entries instead of crashing in `encoder` (#551)
6
+ - [Fix] `parse`: handle nested bracket groups and add regression tests (#530)
7
+ - [readme] fix grammar (#550)
8
+ - [Dev Deps] update `@ljharb/eslint-config`
9
+ - [Tests] add regression tests for keys containing percent-encoded bracket text
10
+
11
+ ## **6.15.1**
12
+ - [Fix] `parse`: `parameterLimit: Infinity` with `throwOnLimitExceeded: true` silently drops all parameters
13
+ - [Deps] update `@ljharb/eslint-config`
14
+ - [Dev Deps] update `@ljharb/eslint-config`, `iconv-lite`
15
+ - [Tests] increase coverage
16
+
17
+ ## **6.15.0**
18
+ - [New] `parse`: add `strictMerge` option to wrap object/primitive conflicts in an array (#425, #122)
19
+ - [Fix] `duplicates` option should not apply to bracket notation keys (#514)
20
+
1
21
  ## **6.14.2**
2
22
  - [Fix] `parse`: mark overflow objects for indexed notation exceeding `arrayLimit` (#546)
3
23
  - [Fix] `arrayLimit` means max count, not max index, in `combine`/`merge`/`parseArrayValue`
@@ -30,6 +50,17 @@
30
50
  - [Dev Deps] update `es-value-fixtures`, `has-bigints`, `has-proto`, `has-symbols`
31
51
  - [Tests] increase coverage
32
52
 
53
+ ## **6.13.3**
54
+ [Fix] fix regressions from robustness refactor
55
+ [actions] update reusable workflows
56
+
57
+ ## **6.13.2**
58
+ - [Robustness] avoid `.push`, use `void`
59
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
60
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
61
+ - [readme] replace runkit CI badge with shields.io check-runs badge
62
+ - [actions] fix rebase workflow permissions
63
+
33
64
  ## **6.13.1**
34
65
  - [Fix] `stringify`: avoid a crash when a `filter` key is `null`
35
66
  - [Fix] `utils.merge`: functions should not be stringified into keys
@@ -46,6 +77,17 @@
46
77
  - [New] `parse`: add `strictDepth` option (#511)
47
78
  - [Tests] use `npm audit` instead of `aud`
48
79
 
80
+ ## **6.12.5**
81
+ - [Fix] fix regressions from robustness refactor
82
+ - [actions] update reusable workflows
83
+
84
+ ## **6.12.4**
85
+ - [Robustness] avoid `.push`, use `void`
86
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
87
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
88
+ - [readme] replace runkit CI badge with shields.io check-runs badge
89
+ - [actions] fix rebase workflow permissions
90
+
49
91
  ## **6.12.3**
50
92
  - [Fix] `parse`: properly account for `strictNullHandling` when `allowEmptyArrays`
51
93
  - [meta] fix changelog indentation
@@ -83,6 +125,17 @@
83
125
  - [Dev Deps] pin `glob`, since v10.3.8+ requires a broken `jackspeak`
84
126
  - [Dev Deps] pin `jackspeak` since 2.1.2+ depends on npm aliases, which kill the install process in npm < 6
85
127
 
128
+ ## **6.11.4**
129
+ - [Fix] fix regressions from robustness refactor
130
+ - [actions] update reusable workflows
131
+
132
+ ## **6.11.3**
133
+ - [Robustness] avoid `.push`, use `void`
134
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
135
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
136
+ - [readme] replace runkit CI badge with shields.io check-runs badge
137
+ - [actions] fix rebase workflow permissions
138
+
86
139
  ## **6.11.2**
87
140
  - [Fix] `parse`: Fix parsing when the global Object prototype is frozen (#473)
88
141
  - [Tests] add passing test cases with empty keys (#473)
@@ -100,6 +153,17 @@
100
153
  - [New] [Fix] `stringify`: revert 0e903c0; add `commaRoundTrip` option (#442)
101
154
  - [readme] fix version badge
102
155
 
156
+ ## **6.10.7**
157
+ - [Fix] fix regressions from robustness refactor
158
+ - [actions] update reusable workflows
159
+
160
+ ## **6.10.6**
161
+ - [Robustness] avoid `.push`, use `void`
162
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
163
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
164
+ - [readme] replace runkit CI badge with shields.io check-runs badge
165
+ - [actions] fix rebase workflow permissions
166
+
103
167
  ## **6.10.5**
104
168
  - [Fix] `stringify`: with `arrayFormat: comma`, properly include an explicit `[]` on a single-item array (#434)
105
169
 
@@ -137,6 +201,18 @@
137
201
  - [Tests] use `ljharb/actions/node/install` instead of `ljharb/actions/node/run`
138
202
  - [Tests] Revert "[meta] ignore eclint transitive audit warning"
139
203
 
204
+ ## **6.9.9**
205
+ - [Fix] fix regressions from robustness refactor
206
+ - [meta] add `npmignore` to autogenerate an npmignore file
207
+ - [actions] update reusable workflows
208
+
209
+ ## **6.9.8**
210
+ - [Robustness] avoid `.push`, use `void`
211
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
212
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
213
+ - [readme] replace runkit CI badge with shields.io check-runs badge
214
+ - [actions] fix rebase workflow permissions
215
+
140
216
  ## **6.9.7**
141
217
  - [Fix] `parse`: ignore `__proto__` keys (#428)
142
218
  - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
@@ -197,6 +273,18 @@
197
273
  - [Tests] up to `node` `v12.10`, `v11.15`, `v10.16`, `v8.16`
198
274
  - [Tests] `Buffer.from` in node v5.0-v5.9 and v4.0-v4.4 requires a TypedArray
199
275
 
276
+ ## **6.8.5**
277
+ - [Fix] fix regressions from robustness refactor
278
+ - [meta] add `npmignore` to autogenerate an npmignore file
279
+ - [actions] update reusable workflows
280
+
281
+ ## **6.8.4**
282
+ - [Robustness] avoid `.push`, use `void`
283
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
284
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
285
+ - [readme] replace runkit CI badge with shields.io check-runs badge
286
+ - [actions] fix rebase workflow permissions
287
+
200
288
  ## **6.8.3**
201
289
  - [Fix] `parse`: ignore `__proto__` keys (#428)
202
290
  - [Robustness] `stringify`: avoid relying on a global `undefined` (#427)
@@ -241,6 +329,18 @@
241
329
  - [meta] add FUNDING.yml
242
330
  - [meta] Clean up license text so it’s properly detected as BSD-3-Clause
243
331
 
332
+ ## **6.7.5**
333
+ - [Fix] fix regressions from robustness refactor
334
+ - [meta] add `npmignore` to autogenerate an npmignore file
335
+ - [actions] update reusable workflows
336
+
337
+ ## **6.7.4**
338
+ - [Robustness] avoid `.push`, use `void`
339
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
340
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
341
+ - [readme] replace runkit CI badge with shields.io check-runs badge
342
+ - [actions] fix rebase workflow permissions
343
+
244
344
  ## **6.7.3**
245
345
  - [Fix] `parse`: ignore `__proto__` keys (#428)
246
346
  - [Fix] `stringify`: avoid encoding arrayformat comma when `encodeValuesOnly = true` (#424)
@@ -292,6 +392,18 @@
292
392
  - [Tests] fix Buffer tests to work in node < 4.5 and node < 5.10
293
393
  - [Tests] temporarily allow coverage to fail
294
394
 
395
+ ## **6.6.3**
396
+ - [Fix] fix regressions from robustness refactor
397
+ - [meta] add `npmignore` to autogenerate an npmignore file
398
+ - [actions] update reusable workflows
399
+
400
+ ## **6.6.2**
401
+ - [Robustness] avoid `.push`, use `void`
402
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
403
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
404
+ - [readme] replace runkit CI badge with shields.io check-runs badge
405
+ - [actions] fix rebase workflow permissions
406
+
295
407
  ## **6.6.1**
296
408
  - [Fix] `parse`: ignore `__proto__` keys (#428)
297
409
  - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
@@ -334,6 +446,18 @@
334
446
  - [Dev Deps] update `browserify`, `eslint`, `@ljharb/eslint-config`, `iconv-lite`, `safe-publish-latest`, `tape`
335
447
  - [Tests] up to `node` `v10.10`, `v9.11`, `v8.12`, `v6.14`, `v4.9`; pin included builds to LTS
336
448
 
449
+ ## **6.5.5**
450
+ - [Fix] fix regressions from robustness refactor
451
+ - [meta] add `npmignore` to autogenerate an npmignore file
452
+ - [actions] update reusable workflows
453
+
454
+ ## **6.5.4**
455
+ - [Robustness] avoid `.push`, use `void`
456
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
457
+ - [readme] document that `addQueryPrefix` does not add `?` to empty output (#418)
458
+ - [readme] replace runkit CI badge with shields.io check-runs badge
459
+ - [actions] fix rebase workflow permissions
460
+
337
461
  ## **6.5.3**
338
462
  - [Fix] `parse`: ignore `__proto__` keys (#428)
339
463
  - [Fix] `utils.merge`: avoid a crash with a null target and a truthy non-array source
@@ -384,6 +508,18 @@
384
508
  - [Tests] up to `node` `v8.1`, `v7.10`, `v6.11`; npm v4.6 breaks on node < v1; npm v5+ breaks on node < v4
385
509
  - [Tests] add `editorconfig-tools`
386
510
 
511
+ ## **6.4.3**
512
+ - [Fix] fix regressions from robustness refactor
513
+ - [meta] add `npmignore` to autogenerate an npmignore file
514
+ - [actions] update reusable workflows
515
+
516
+ ## **6.4.2**
517
+ - [Robustness] avoid `.push`, use `void`
518
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
519
+ - [readme] replace runkit CI badge with shields.io check-runs badge
520
+ - [readme] replace travis CI badge with shields.io check-runs badge
521
+ - [actions] fix rebase workflow permissions
522
+
387
523
  ## **6.4.1**
388
524
  - [Fix] `parse`: ignore `__proto__` keys (#428)
389
525
  - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
@@ -414,6 +550,17 @@
414
550
  - [Tests] up to `node` `v7.7`, `v6.10`,` v4.8`; disable osx builds since they block linux builds
415
551
  - [eslint] reduce warnings
416
552
 
553
+ ## **6.3.5**
554
+ - [Fix] fix regressions from robustness refactor
555
+ - [meta] add `npmignore` to autogenerate an npmignore file
556
+ - [actions] update reusable workflows
557
+
558
+ ## **6.3.4**
559
+ - [Robustness] avoid `.push`, use `void`
560
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
561
+ - [readme] replace travis CI badge with shields.io check-runs badge
562
+ - [actions] fix rebase workflow permissions
563
+
417
564
  ## **6.3.3**
418
565
  - [Fix] `parse`: ignore `__proto__` keys (#428)
419
566
  - [Fix] fix for an impossible situation: when the formatter is called with a non-string value
@@ -467,6 +614,17 @@
467
614
  - [Tests] skip Object.create tests when null objects are not available
468
615
  - [Tests] Turn on eslint for test files (#175)
469
616
 
617
+ ## **6.2.6**
618
+ - [Fix] fix regression from robustness refactor
619
+ - [meta] add `npmignore` to autogenerate an npmignore file
620
+ - [actions] update reusable workflows
621
+
622
+ ## **6.2.5**
623
+ - [Robustness] avoid `.push`, use `void`
624
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
625
+ - [readme] replace travis CI badge with shields.io check-runs badge
626
+ - [actions] fix rebase workflow permissions
627
+
470
628
  ## **6.2.4**
471
629
  - [Fix] `parse`: ignore `__proto__` keys (#428)
472
630
  - [Fix] `utils.merge`: avoid a crash with a null target and an array source
@@ -505,6 +663,16 @@
505
663
  - [New] add "encoder" and "decoder" options, for custom param encoding/decoding (#160)
506
664
  - [Fix] fix compacting of nested sparse arrays (#150)
507
665
 
666
+ ## **6.1.4**
667
+ - [Fix] fix regression from robustness refactor
668
+ - [meta] add `npmignore` to autogenerate an npmignore file
669
+ - [actions] update reusable workflows
670
+
671
+ ## **6.1.3**
672
+ - [Robustness] avoid `.push`, use `void`
673
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
674
+ - [readme] replace travis CI badge with shields.io check-runs badge
675
+
508
676
  ## **6.1.2**
509
677
  - [Fix] follow `allowPrototypes` option during merge (#201, #200)
510
678
  - [Fix] chmod a-x
@@ -519,6 +687,16 @@
519
687
  - [Fix] "sort" option should work at a depth of 3 or more (#151)
520
688
  - [Fix] Restore `dist` directory; will be removed in v7 (#148)
521
689
 
690
+ ## **6.0.6**
691
+ - [Fix] fix regression from robustness refactor
692
+ - [meta] add `npmignore` to autogenerate an npmignore file
693
+ - [actions] update reusable workflows
694
+
695
+ ## **6.0.5**
696
+ - [Robustness] avoid `.push`, use `void`
697
+ - [readme] clarify `parseArrays` and `arrayLimit` documentation (#543)
698
+ - [readme] replace travis CI badge with shields.io check-runs badge
699
+
522
700
  ## **6.0.4**
523
701
  - [Fix] follow `allowPrototypes` option during merge (#201, #200)
524
702
  - [Fix] chmod a-x
@@ -183,7 +183,7 @@ var withDots = qs.parse('name%252Eobj.first=John&name%252Eobj.last=Doe', { decod
183
183
  assert.deepEqual(withDots, { 'name.obj': { first: 'John', last: 'Doe' }});
184
184
  ```
185
185
 
186
- Option `allowEmptyArrays` can be used to allowing empty array values in object
186
+ Option `allowEmptyArrays` can be used to allow empty array values in an object
187
187
  ```javascript
188
188
  var withEmptyArrays = qs.parse('foo[]&bar=baz', { allowEmptyArrays: true });
189
189
  assert.deepEqual(withEmptyArrays, { foo: [], bar: 'baz' });
@@ -197,6 +197,11 @@ assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'first' }), { foo: 'b
197
197
  assert.deepEqual(qs.parse('foo=bar&foo=baz', { duplicates: 'last' }), { foo: 'baz' });
198
198
  ```
199
199
 
200
+ Note that keys with bracket notation (`[]`) always combine into arrays, regardless of the `duplicates` setting:
201
+ ```javascript
202
+ assert.deepEqual(qs.parse('a=1&a=2&b[]=1&b[]=2', { duplicates: 'last' }), { a: '2', b: ['1', '2'] });
203
+ ```
204
+
200
205
  If you have to deal with legacy browsers or services, there's also support for decoding percent-encoded octets as iso-8859-1:
201
206
 
202
207
  ```javascript
@@ -325,6 +330,19 @@ var mixedNotation = qs.parse('a[0]=b&a[b]=c');
325
330
  assert.deepEqual(mixedNotation, { a: { '0': 'b', b: 'c' } });
326
331
  ```
327
332
 
333
+ When a key appears as both a plain value and an object, **qs** will by default wrap the conflicting values in an array (`strictMerge` defaults to `true`):
334
+
335
+ ```javascript
336
+ assert.deepEqual(qs.parse('a[b]=c&a=d'), { a: [{ b: 'c' }, 'd'] });
337
+ assert.deepEqual(qs.parse('a=d&a[b]=c'), { a: ['d', { b: 'c' }] });
338
+ ```
339
+
340
+ To restore the legacy behavior (where the primitive is used as a key with value `true`), set `strictMerge` to `false`:
341
+
342
+ ```javascript
343
+ assert.deepEqual(qs.parse('a[b]=c&a=d', { strictMerge: false }), { a: { b: 'c', d: true } });
344
+ ```
345
+
328
346
  You can also create arrays of objects:
329
347
 
330
348
  ```javascript