@linqapp/sdk 0.28.0 → 0.28.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/CHANGELOG.md +14 -0
- package/client.d.mts +59 -9
- package/client.d.mts.map +1 -1
- package/client.d.ts +59 -9
- package/client.d.ts.map +1 -1
- package/client.js +63 -5
- package/client.js.map +1 -1
- package/client.mjs +64 -6
- package/client.mjs.map +1 -1
- package/internal/qs/formats.d.mts +7 -0
- package/internal/qs/formats.d.mts.map +1 -0
- package/internal/qs/formats.d.ts +7 -0
- package/internal/qs/formats.d.ts.map +1 -0
- package/internal/qs/formats.js +13 -0
- package/internal/qs/formats.js.map +1 -0
- package/internal/qs/formats.mjs +9 -0
- package/internal/qs/formats.mjs.map +1 -0
- package/internal/qs/index.d.mts +10 -0
- package/internal/qs/index.d.mts.map +1 -0
- package/internal/qs/index.d.ts +10 -0
- package/internal/qs/index.d.ts.map +1 -0
- package/internal/qs/index.js +14 -0
- package/internal/qs/index.js.map +1 -0
- package/internal/qs/index.mjs +10 -0
- package/internal/qs/index.mjs.map +1 -0
- package/internal/qs/stringify.d.mts +3 -0
- package/internal/qs/stringify.d.mts.map +1 -0
- package/internal/qs/stringify.d.ts +3 -0
- package/internal/qs/stringify.d.ts.map +1 -0
- package/internal/qs/stringify.js +277 -0
- package/internal/qs/stringify.js.map +1 -0
- package/internal/qs/stringify.mjs +274 -0
- package/internal/qs/stringify.mjs.map +1 -0
- package/internal/qs/types.d.mts +57 -0
- package/internal/qs/types.d.mts.map +1 -0
- package/internal/qs/types.d.ts +57 -0
- package/internal/qs/types.d.ts.map +1 -0
- package/internal/qs/types.js +3 -0
- package/internal/qs/types.js.map +1 -0
- package/internal/qs/types.mjs +2 -0
- package/internal/qs/types.mjs.map +1 -0
- package/internal/qs/utils.d.mts +15 -0
- package/internal/qs/utils.d.mts.map +1 -0
- package/internal/qs/utils.d.ts +15 -0
- package/internal/qs/utils.d.ts.map +1 -0
- package/internal/qs/utils.js +230 -0
- package/internal/qs/utils.js.map +1 -0
- package/internal/qs/utils.mjs +217 -0
- package/internal/qs/utils.mjs.map +1 -0
- package/internal/tslib.js +17 -17
- package/internal/utils/query.d.mts +0 -3
- package/internal/utils/query.d.mts.map +1 -1
- package/internal/utils/query.d.ts +0 -3
- package/internal/utils/query.d.ts.map +1 -1
- package/internal/utils/query.js +3 -16
- package/internal/utils/query.js.map +1 -1
- package/internal/utils/query.mjs +2 -16
- package/internal/utils/query.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/attachments.d.mts +1 -1
- package/resources/attachments.d.ts +1 -1
- package/resources/attachments.js +1 -1
- package/resources/attachments.mjs +1 -1
- package/resources/available-number.d.mts +58 -0
- package/resources/available-number.d.mts.map +1 -0
- package/resources/available-number.d.ts +58 -0
- package/resources/available-number.d.ts.map +1 -0
- package/resources/available-number.js +36 -0
- package/resources/available-number.js.map +1 -0
- package/resources/available-number.mjs +32 -0
- package/resources/available-number.mjs.map +1 -0
- package/resources/chats/chats.d.mts +59 -11
- package/resources/chats/chats.d.mts.map +1 -1
- package/resources/chats/chats.d.ts +59 -11
- package/resources/chats/chats.d.ts.map +1 -1
- package/resources/chats/chats.js.map +1 -1
- package/resources/chats/chats.mjs.map +1 -1
- package/resources/chats/location.d.mts +9 -8
- package/resources/chats/location.d.mts.map +1 -1
- package/resources/chats/location.d.ts +9 -8
- package/resources/chats/location.d.ts.map +1 -1
- package/resources/chats/location.js +9 -8
- package/resources/chats/location.js.map +1 -1
- package/resources/chats/location.mjs +9 -8
- package/resources/chats/location.mjs.map +1 -1
- package/resources/chats/messages.d.mts +84 -6
- package/resources/chats/messages.d.mts.map +1 -1
- package/resources/chats/messages.d.ts +84 -6
- package/resources/chats/messages.d.ts.map +1 -1
- package/resources/chats/messages.js +40 -0
- package/resources/chats/messages.js.map +1 -1
- package/resources/chats/messages.mjs +40 -0
- package/resources/chats/messages.mjs.map +1 -1
- package/resources/contact-card.d.mts +1 -2
- package/resources/contact-card.d.mts.map +1 -1
- package/resources/contact-card.d.ts +1 -2
- package/resources/contact-card.d.ts.map +1 -1
- package/resources/index.d.mts +3 -2
- package/resources/index.d.mts.map +1 -1
- package/resources/index.d.ts +3 -2
- package/resources/index.d.ts.map +1 -1
- package/resources/index.js +3 -1
- package/resources/index.js.map +1 -1
- package/resources/index.mjs +2 -1
- package/resources/index.mjs.map +1 -1
- package/resources/messages.d.mts +400 -7
- package/resources/messages.d.mts.map +1 -1
- package/resources/messages.d.ts +400 -7
- package/resources/messages.d.ts.map +1 -1
- package/resources/messages.js +148 -0
- package/resources/messages.js.map +1 -1
- package/resources/messages.mjs +148 -0
- package/resources/messages.mjs.map +1 -1
- package/resources/phone-numbers.d.mts +68 -23
- package/resources/phone-numbers.d.mts.map +1 -1
- package/resources/phone-numbers.d.ts +68 -23
- package/resources/phone-numbers.d.ts.map +1 -1
- package/resources/phone-numbers.js +23 -0
- package/resources/phone-numbers.js.map +1 -1
- package/resources/phone-numbers.mjs +23 -0
- package/resources/phone-numbers.mjs.map +1 -1
- package/resources/webhooks.d.mts +14 -14
- package/resources/webhooks.d.ts +14 -14
- package/src/client.ts +94 -8
- package/src/internal/qs/LICENSE.md +13 -0
- package/src/internal/qs/README.md +3 -0
- package/src/internal/qs/formats.ts +10 -0
- package/src/internal/qs/index.ts +13 -0
- package/src/internal/qs/stringify.ts +385 -0
- package/src/internal/qs/types.ts +71 -0
- package/src/internal/qs/utils.ts +265 -0
- package/src/internal/utils/query.ts +2 -18
- package/src/resources/attachments.ts +1 -1
- package/src/resources/available-number.ts +72 -0
- package/src/resources/chats/chats.ts +64 -12
- package/src/resources/chats/location.ts +9 -8
- package/src/resources/chats/messages.ts +87 -6
- package/src/resources/contact-card.ts +1 -2
- package/src/resources/index.ts +15 -1
- package/src/resources/messages.ts +453 -6
- package/src/resources/phone-numbers.ts +85 -23
- package/src/resources/webhooks.ts +14 -14
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/resources/webhooks.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ export declare namespace MessageEventV2 {
|
|
|
175
175
|
*/
|
|
176
176
|
type: 'imessage_app';
|
|
177
177
|
/**
|
|
178
|
-
* The URL
|
|
178
|
+
* The URL the recipient's app opens when the user taps the card.
|
|
179
179
|
*/
|
|
180
180
|
url: string;
|
|
181
181
|
/**
|
|
@@ -321,7 +321,7 @@ export declare namespace MessagePayload {
|
|
|
321
321
|
*/
|
|
322
322
|
type: 'imessage_app';
|
|
323
323
|
/**
|
|
324
|
-
* The URL
|
|
324
|
+
* The URL the recipient's app opens when the user taps the card.
|
|
325
325
|
*/
|
|
326
326
|
url: string;
|
|
327
327
|
/**
|
|
@@ -1777,13 +1777,13 @@ export declare namespace PhoneNumberStatusUpdatedWebhookEvent {
|
|
|
1777
1777
|
*/
|
|
1778
1778
|
changed_at: string;
|
|
1779
1779
|
/**
|
|
1780
|
-
* @deprecated Current reputation of this phone line
|
|
1780
|
+
* @deprecated Current reputation of this phone line.
|
|
1781
1781
|
*
|
|
1782
|
-
* - `HEALTHY` —
|
|
1783
|
-
* - `AT_RISK` —
|
|
1784
|
-
*
|
|
1785
|
-
* - `CRITICAL` —
|
|
1786
|
-
*
|
|
1782
|
+
* - `HEALTHY` — The line is in good standing. Send normally.
|
|
1783
|
+
* - `AT_RISK` — The line's overall engagement is trending down. Slow the line's
|
|
1784
|
+
* send pace and review your messaging patterns.
|
|
1785
|
+
* - `CRITICAL` — Strong signals that messages from this line aren't landing well.
|
|
1786
|
+
* Pause outbound on the line until it recovers.
|
|
1787
1787
|
*
|
|
1788
1788
|
* Defaults to `HEALTHY` for lines that have not yet been scored.
|
|
1789
1789
|
*/
|
|
@@ -1801,13 +1801,13 @@ export declare namespace PhoneNumberStatusUpdatedWebhookEvent {
|
|
|
1801
1801
|
*/
|
|
1802
1802
|
phone_number: string;
|
|
1803
1803
|
/**
|
|
1804
|
-
* @deprecated Current reputation of this phone line
|
|
1804
|
+
* @deprecated Current reputation of this phone line.
|
|
1805
1805
|
*
|
|
1806
|
-
* - `HEALTHY` —
|
|
1807
|
-
* - `AT_RISK` —
|
|
1808
|
-
*
|
|
1809
|
-
* - `CRITICAL` —
|
|
1810
|
-
*
|
|
1806
|
+
* - `HEALTHY` — The line is in good standing. Send normally.
|
|
1807
|
+
* - `AT_RISK` — The line's overall engagement is trending down. Slow the line's
|
|
1808
|
+
* send pace and review your messaging patterns.
|
|
1809
|
+
* - `CRITICAL` — Strong signals that messages from this line aren't landing well.
|
|
1810
|
+
* Pause outbound on the line until it recovers.
|
|
1811
1811
|
*
|
|
1812
1812
|
* Defaults to `HEALTHY` for lines that have not yet been scored.
|
|
1813
1813
|
*/
|
package/src/client.ts
CHANGED
|
@@ -32,6 +32,11 @@ import {
|
|
|
32
32
|
Attachments,
|
|
33
33
|
SupportedContentType,
|
|
34
34
|
} from './resources/attachments';
|
|
35
|
+
import {
|
|
36
|
+
AvailableNumber,
|
|
37
|
+
AvailableNumberRetrieveParams,
|
|
38
|
+
AvailableNumberRetrieveResponse,
|
|
39
|
+
} from './resources/available-number';
|
|
35
40
|
import {
|
|
36
41
|
Capability,
|
|
37
42
|
CapabilityCheckIMessageParams,
|
|
@@ -51,14 +56,23 @@ import {
|
|
|
51
56
|
Message,
|
|
52
57
|
MessageAddReactionParams,
|
|
53
58
|
MessageAddReactionResponse,
|
|
59
|
+
MessageCreateParams,
|
|
60
|
+
MessageCreateResponse,
|
|
54
61
|
MessageEffect,
|
|
55
62
|
MessageListMessagesThreadParams,
|
|
63
|
+
MessageUpdateAppCardParams,
|
|
64
|
+
MessageUpdateAppCardResponse,
|
|
56
65
|
MessageUpdateParams,
|
|
57
66
|
Messages,
|
|
58
67
|
MessagesListMessagesPagination,
|
|
59
68
|
ReplyTo,
|
|
60
69
|
} from './resources/messages';
|
|
61
|
-
import {
|
|
70
|
+
import {
|
|
71
|
+
PhoneNumberListResponse,
|
|
72
|
+
PhoneNumberUpdateParams,
|
|
73
|
+
PhoneNumberUpdateResponse,
|
|
74
|
+
PhoneNumbers,
|
|
75
|
+
} from './resources/phone-numbers';
|
|
62
76
|
import { PhonenumberListResponse, Phonenumbers } from './resources/phonenumbers';
|
|
63
77
|
import { WebhookEventListResponse, WebhookEventType, WebhookEvents } from './resources/webhook-events';
|
|
64
78
|
import {
|
|
@@ -141,7 +155,7 @@ export interface ClientOptions {
|
|
|
141
155
|
* Standard Webhooks signature on incoming webhook requests.
|
|
142
156
|
*
|
|
143
157
|
* Format: a base64-encoded key, optionally with a `whsec_` prefix
|
|
144
|
-
* (e.g. `
|
|
158
|
+
* (e.g. `whsec_<your-webhook-signing-secret>`).
|
|
145
159
|
*
|
|
146
160
|
*/
|
|
147
161
|
webhookSecret?: string | null | undefined;
|
|
@@ -338,9 +352,6 @@ export class LinqAPIV3 {
|
|
|
338
352
|
return buildHeaders([{ Authorization: `Bearer ${this.apiKey}` }]);
|
|
339
353
|
}
|
|
340
354
|
|
|
341
|
-
/**
|
|
342
|
-
* Basic re-implementation of `qs.stringify` for primitive types.
|
|
343
|
-
*/
|
|
344
355
|
protected stringifyQuery(query: object | Record<string, unknown>): string {
|
|
345
356
|
return stringifyQuery(query);
|
|
346
357
|
}
|
|
@@ -805,11 +816,19 @@ export class LinqAPIV3 {
|
|
|
805
816
|
return () => controller.abort();
|
|
806
817
|
}
|
|
807
818
|
|
|
808
|
-
private buildBody({ options
|
|
819
|
+
private buildBody({ options }: { options: FinalRequestOptions }): {
|
|
809
820
|
bodyHeaders: HeadersLike;
|
|
810
821
|
body: BodyInit | undefined;
|
|
811
822
|
} {
|
|
823
|
+
const { body, headers: rawHeaders } = options;
|
|
812
824
|
if (!body) {
|
|
825
|
+
// A resource method always passes a `body` key when its operation defines a
|
|
826
|
+
// request body, even if the caller omitted an optional body param. Keep the
|
|
827
|
+
// content-type for those, and only elide it for operations with no body at
|
|
828
|
+
// all (e.g. GET/DELETE).
|
|
829
|
+
if (body == null && 'body' in options) {
|
|
830
|
+
return this.#encoder({ body, headers: buildHeaders([rawHeaders]) });
|
|
831
|
+
}
|
|
813
832
|
return { bodyHeaders: undefined, body: undefined };
|
|
814
833
|
}
|
|
815
834
|
const headers = buildHeaders([rawHeaders]);
|
|
@@ -890,6 +909,46 @@ export class LinqAPIV3 {
|
|
|
890
909
|
* - A `link` part cannot be combined with other parts in the same message.
|
|
891
910
|
* - Maximum URL length: 2,048 characters.
|
|
892
911
|
*
|
|
912
|
+
* ## Ephemeral Messages (Privacy Tier)
|
|
913
|
+
*
|
|
914
|
+
* For regulated or sensitive conversations, opt in to the **ephemeral messages** tier by contacting your Linq support contact. When enabled, every message on the covered phone numbers is automatically given a fixed **24-hour retention window** — after that window the platform permanently deletes the message from Linq storage. There is no per-message flag; ephemerality is applied automatically based on your configuration.
|
|
915
|
+
*
|
|
916
|
+
* You can request it at two scopes:
|
|
917
|
+
*
|
|
918
|
+
* | Scope | Effect |
|
|
919
|
+
* |---|---|
|
|
920
|
+
* | **Partner-wide** | Every outbound and inbound message on every phone number under your account is retained for 24 hours, then deleted. |
|
|
921
|
+
* | **Per phone number** | Only the specified phone numbers have their messages auto-deleted. The rest follow the standard message-retention policy. |
|
|
922
|
+
*
|
|
923
|
+
* **Behavioral differences vs the standard default:**
|
|
924
|
+
*
|
|
925
|
+
* | Aspect | Standard | Ephemeral |
|
|
926
|
+
* |---|---|---|
|
|
927
|
+
* | Retention | Retained per the standard message-retention policy | **Hard backstop: 24 hours** from when the message is created |
|
|
928
|
+
* | After expiry | Message stays retrievable | Message is permanently deleted — `GET /v3/messages/{messageId}` returns `404` and it no longer appears in `GET /v3/chats/{chatId}/messages` |
|
|
929
|
+
* | Content on expiry | N/A | Text, formatting, and attachment references are scrubbed; the message is gone, not blanked out |
|
|
930
|
+
* | Cross-partner isolation | Enforced | Enforced |
|
|
931
|
+
*
|
|
932
|
+
* **How the 24-hour window works:**
|
|
933
|
+
*
|
|
934
|
+
* - The window is fixed at **24 hours from message creation** (`created_at`) and cannot be configured per message.
|
|
935
|
+
* - It mirrors the ephemeral *attachments* 1-day backstop, so a message and any media it carries expire together.
|
|
936
|
+
* - Expiry is delivery-independent — the clock starts when the message is created, not when it is delivered or read.
|
|
937
|
+
*
|
|
938
|
+
* **What you observe:**
|
|
939
|
+
*
|
|
940
|
+
* - **No expiry timestamp is exposed.** API responses and webhook payloads do not include the deletion time. If you need it, compute `created_at + 24h` yourself.
|
|
941
|
+
* - **No deletion webhook is sent.** There is no `message.deleted` event — a message simply stops being retrievable once its window passes.
|
|
942
|
+
* - **Delivery is unaffected.** Ephemeral messages send, deliver, and fire the usual `message.sent` / `message.received` and status webhooks exactly like standard messages. Only retention changes.
|
|
943
|
+
*
|
|
944
|
+
* **When to choose ephemeral:**
|
|
945
|
+
*
|
|
946
|
+
* - You have a compliance requirement that the platform must not retain message content beyond a short window.
|
|
947
|
+
* - The conversation is high-sensitivity (PHI, financial, identity verification) and you do not want it sitting in storage long-term.
|
|
948
|
+
* - Your application is the system of record — you capture what you need from the delivery webhook in real time and do not rely on reading message history back from Linq later.
|
|
949
|
+
*
|
|
950
|
+
* **Important:** ephemeral applies in *both directions* — messages you send **and** messages received by the phone numbers in that scope. Because Linq can no longer return the message after 24 hours, persist anything you need to keep from the webhook payload at the time it is delivered.
|
|
951
|
+
*
|
|
893
952
|
*/
|
|
894
953
|
messages: API.Messages = new API.Messages(this);
|
|
895
954
|
/**
|
|
@@ -1056,7 +1115,7 @@ export class LinqAPIV3 {
|
|
|
1056
1115
|
* |---|---|---|
|
|
1057
1116
|
* | Attachment bytes | Retained until you `DELETE` | **Auto-removed after 1 day**, also removable via `DELETE` |
|
|
1058
1117
|
* | Attachment metadata (id, filename, mime type, size) | Retained until you `DELETE` | Removed alongside the bytes |
|
|
1059
|
-
* | Message body & parts | Retained per message-retention policy | Retained per message-retention policy |
|
|
1118
|
+
* | Message body & parts | Retained per message-retention policy | Retained per message-retention policy — unless the line also has **ephemeral messages** enabled (see the Messages page), in which case the message and its parts are deleted 24 hours after creation |
|
|
1060
1119
|
* | Audit log of deletions | Retained per platform retention policy | Retained per platform retention policy |
|
|
1061
1120
|
*
|
|
1062
1121
|
* **In transit:** TLS 1.2+ everywhere. **At rest:** AES-256 (server-side encryption).
|
|
@@ -1096,6 +1155,17 @@ export class LinqAPIV3 {
|
|
|
1096
1155
|
*
|
|
1097
1156
|
*/
|
|
1098
1157
|
phoneNumbers: API.PhoneNumbers = new API.PhoneNumbers(this);
|
|
1158
|
+
/**
|
|
1159
|
+
* Phone Numbers represent the phone numbers assigned to your partner account.
|
|
1160
|
+
*
|
|
1161
|
+
* Use the list phone numbers endpoint to discover which phone numbers are available
|
|
1162
|
+
* for sending messages.
|
|
1163
|
+
*
|
|
1164
|
+
* When creating chats, listing chats, or sending a voice memo, use one of your assigned phone numbers
|
|
1165
|
+
* in the `from` field.
|
|
1166
|
+
*
|
|
1167
|
+
*/
|
|
1168
|
+
availableNumber: API.AvailableNumber = new API.AvailableNumber(this);
|
|
1099
1169
|
/**
|
|
1100
1170
|
* Webhook Subscriptions allow you to receive real-time notifications when events
|
|
1101
1171
|
* occur on your account.
|
|
@@ -1380,6 +1450,7 @@ LinqAPIV3.Messages = Messages;
|
|
|
1380
1450
|
LinqAPIV3.Attachments = Attachments;
|
|
1381
1451
|
LinqAPIV3.Phonenumbers = Phonenumbers;
|
|
1382
1452
|
LinqAPIV3.PhoneNumbers = PhoneNumbers;
|
|
1453
|
+
LinqAPIV3.AvailableNumber = AvailableNumber;
|
|
1383
1454
|
LinqAPIV3.WebhookEvents = WebhookEvents;
|
|
1384
1455
|
LinqAPIV3.WebhookSubscriptions = WebhookSubscriptions;
|
|
1385
1456
|
LinqAPIV3.Capability = Capability;
|
|
@@ -1424,11 +1495,15 @@ export declare namespace LinqAPIV3 {
|
|
|
1424
1495
|
type Message as Message,
|
|
1425
1496
|
type MessageEffect as MessageEffect,
|
|
1426
1497
|
type ReplyTo as ReplyTo,
|
|
1498
|
+
type MessageCreateResponse as MessageCreateResponse,
|
|
1427
1499
|
type MessageAddReactionResponse as MessageAddReactionResponse,
|
|
1500
|
+
type MessageUpdateAppCardResponse as MessageUpdateAppCardResponse,
|
|
1428
1501
|
type MessagesListMessagesPagination as MessagesListMessagesPagination,
|
|
1502
|
+
type MessageCreateParams as MessageCreateParams,
|
|
1429
1503
|
type MessageListMessagesThreadParams as MessageListMessagesThreadParams,
|
|
1430
1504
|
type MessageAddReactionParams as MessageAddReactionParams,
|
|
1431
1505
|
type MessageUpdateParams as MessageUpdateParams,
|
|
1506
|
+
type MessageUpdateAppCardParams as MessageUpdateAppCardParams,
|
|
1432
1507
|
};
|
|
1433
1508
|
|
|
1434
1509
|
export {
|
|
@@ -1441,7 +1516,18 @@ export declare namespace LinqAPIV3 {
|
|
|
1441
1516
|
|
|
1442
1517
|
export { Phonenumbers as Phonenumbers, type PhonenumberListResponse as PhonenumberListResponse };
|
|
1443
1518
|
|
|
1444
|
-
export {
|
|
1519
|
+
export {
|
|
1520
|
+
PhoneNumbers as PhoneNumbers,
|
|
1521
|
+
type PhoneNumberUpdateResponse as PhoneNumberUpdateResponse,
|
|
1522
|
+
type PhoneNumberListResponse as PhoneNumberListResponse,
|
|
1523
|
+
type PhoneNumberUpdateParams as PhoneNumberUpdateParams,
|
|
1524
|
+
};
|
|
1525
|
+
|
|
1526
|
+
export {
|
|
1527
|
+
AvailableNumber as AvailableNumber,
|
|
1528
|
+
type AvailableNumberRetrieveResponse as AvailableNumberRetrieveResponse,
|
|
1529
|
+
type AvailableNumberRetrieveParams as AvailableNumberRetrieveParams,
|
|
1530
|
+
};
|
|
1445
1531
|
|
|
1446
1532
|
export {
|
|
1447
1533
|
WebhookEvents as WebhookEvents,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/puruvj/neoqs/graphs/contributors) All rights reserved.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
10
|
+
|
|
11
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Format } from './types';
|
|
2
|
+
|
|
3
|
+
export const default_format: Format = 'RFC3986';
|
|
4
|
+
export const default_formatter = (v: PropertyKey) => String(v);
|
|
5
|
+
export const formatters: Record<Format, (str: PropertyKey) => string> = {
|
|
6
|
+
RFC1738: (v: PropertyKey) => String(v).replace(/%20/g, '+'),
|
|
7
|
+
RFC3986: default_formatter,
|
|
8
|
+
};
|
|
9
|
+
export const RFC1738 = 'RFC1738';
|
|
10
|
+
export const RFC3986 = 'RFC3986';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { default_format, formatters, RFC1738, RFC3986 } from './formats';
|
|
2
|
+
|
|
3
|
+
const formats = {
|
|
4
|
+
formatters,
|
|
5
|
+
RFC1738,
|
|
6
|
+
RFC3986,
|
|
7
|
+
default: default_format,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { stringify } from './stringify';
|
|
11
|
+
export { formats };
|
|
12
|
+
|
|
13
|
+
export type { DefaultDecoder, DefaultEncoder, Format, ParseOptions, StringifyOptions } from './types';
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { encode, is_buffer, maybe_map, has } from './utils';
|
|
2
|
+
import { default_format, default_formatter, formatters } from './formats';
|
|
3
|
+
import type { NonNullableProperties, StringifyOptions } from './types';
|
|
4
|
+
import { isArray } from '../utils/values';
|
|
5
|
+
|
|
6
|
+
const array_prefix_generators = {
|
|
7
|
+
brackets(prefix: PropertyKey) {
|
|
8
|
+
return String(prefix) + '[]';
|
|
9
|
+
},
|
|
10
|
+
comma: 'comma',
|
|
11
|
+
indices(prefix: PropertyKey, key: string) {
|
|
12
|
+
return String(prefix) + '[' + key + ']';
|
|
13
|
+
},
|
|
14
|
+
repeat(prefix: PropertyKey) {
|
|
15
|
+
return String(prefix);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const push_to_array = function (arr: any[], value_or_array: any) {
|
|
20
|
+
Array.prototype.push.apply(arr, isArray(value_or_array) ? value_or_array : [value_or_array]);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
let toISOString;
|
|
24
|
+
|
|
25
|
+
const defaults = {
|
|
26
|
+
addQueryPrefix: false,
|
|
27
|
+
allowDots: false,
|
|
28
|
+
allowEmptyArrays: false,
|
|
29
|
+
arrayFormat: 'indices',
|
|
30
|
+
charset: 'utf-8',
|
|
31
|
+
charsetSentinel: false,
|
|
32
|
+
delimiter: '&',
|
|
33
|
+
encode: true,
|
|
34
|
+
encodeDotInKeys: false,
|
|
35
|
+
encoder: encode,
|
|
36
|
+
encodeValuesOnly: false,
|
|
37
|
+
format: default_format,
|
|
38
|
+
formatter: default_formatter,
|
|
39
|
+
/** @deprecated */
|
|
40
|
+
indices: false,
|
|
41
|
+
serializeDate(date) {
|
|
42
|
+
return (toISOString ??= Function.prototype.call.bind(Date.prototype.toISOString))(date);
|
|
43
|
+
},
|
|
44
|
+
skipNulls: false,
|
|
45
|
+
strictNullHandling: false,
|
|
46
|
+
} as NonNullableProperties<StringifyOptions & { formatter: (typeof formatters)['RFC1738'] }>;
|
|
47
|
+
|
|
48
|
+
function is_non_nullish_primitive(v: unknown): v is string | number | boolean | symbol | bigint {
|
|
49
|
+
return (
|
|
50
|
+
typeof v === 'string' ||
|
|
51
|
+
typeof v === 'number' ||
|
|
52
|
+
typeof v === 'boolean' ||
|
|
53
|
+
typeof v === 'symbol' ||
|
|
54
|
+
typeof v === 'bigint'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sentinel = {};
|
|
59
|
+
|
|
60
|
+
function inner_stringify(
|
|
61
|
+
object: any,
|
|
62
|
+
prefix: PropertyKey,
|
|
63
|
+
generateArrayPrefix: StringifyOptions['arrayFormat'] | ((prefix: string, key: string) => string),
|
|
64
|
+
commaRoundTrip: boolean,
|
|
65
|
+
allowEmptyArrays: boolean,
|
|
66
|
+
strictNullHandling: boolean,
|
|
67
|
+
skipNulls: boolean,
|
|
68
|
+
encodeDotInKeys: boolean,
|
|
69
|
+
encoder: StringifyOptions['encoder'],
|
|
70
|
+
filter: StringifyOptions['filter'],
|
|
71
|
+
sort: StringifyOptions['sort'],
|
|
72
|
+
allowDots: StringifyOptions['allowDots'],
|
|
73
|
+
serializeDate: StringifyOptions['serializeDate'],
|
|
74
|
+
format: StringifyOptions['format'],
|
|
75
|
+
formatter: StringifyOptions['formatter'],
|
|
76
|
+
encodeValuesOnly: boolean,
|
|
77
|
+
charset: StringifyOptions['charset'],
|
|
78
|
+
sideChannel: WeakMap<any, any>,
|
|
79
|
+
) {
|
|
80
|
+
let obj = object;
|
|
81
|
+
|
|
82
|
+
let tmp_sc = sideChannel;
|
|
83
|
+
let step = 0;
|
|
84
|
+
let find_flag = false;
|
|
85
|
+
while ((tmp_sc = tmp_sc.get(sentinel)) !== void undefined && !find_flag) {
|
|
86
|
+
// Where object last appeared in the ref tree
|
|
87
|
+
const pos = tmp_sc.get(object);
|
|
88
|
+
step += 1;
|
|
89
|
+
if (typeof pos !== 'undefined') {
|
|
90
|
+
if (pos === step) {
|
|
91
|
+
throw new RangeError('Cyclic object value');
|
|
92
|
+
} else {
|
|
93
|
+
find_flag = true; // Break while
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
if (typeof tmp_sc.get(sentinel) === 'undefined') {
|
|
97
|
+
step = 0;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (typeof filter === 'function') {
|
|
102
|
+
obj = filter(prefix, obj);
|
|
103
|
+
} else if (obj instanceof Date) {
|
|
104
|
+
obj = serializeDate?.(obj);
|
|
105
|
+
} else if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
|
106
|
+
obj = maybe_map(obj, function (value) {
|
|
107
|
+
if (value instanceof Date) {
|
|
108
|
+
return serializeDate?.(value);
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (obj === null) {
|
|
115
|
+
if (strictNullHandling) {
|
|
116
|
+
return encoder && !encodeValuesOnly ?
|
|
117
|
+
// @ts-expect-error
|
|
118
|
+
encoder(prefix, defaults.encoder, charset, 'key', format)
|
|
119
|
+
: prefix;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
obj = '';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (is_non_nullish_primitive(obj) || is_buffer(obj)) {
|
|
126
|
+
if (encoder) {
|
|
127
|
+
const key_value =
|
|
128
|
+
encodeValuesOnly ? prefix
|
|
129
|
+
// @ts-expect-error
|
|
130
|
+
: encoder(prefix, defaults.encoder, charset, 'key', format);
|
|
131
|
+
return [
|
|
132
|
+
formatter?.(key_value) +
|
|
133
|
+
'=' +
|
|
134
|
+
// @ts-expect-error
|
|
135
|
+
formatter?.(encoder(obj, defaults.encoder, charset, 'value', format)),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
return [formatter?.(prefix) + '=' + formatter?.(String(obj))];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const values: string[] = [];
|
|
142
|
+
|
|
143
|
+
if (typeof obj === 'undefined') {
|
|
144
|
+
return values;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let obj_keys;
|
|
148
|
+
if (generateArrayPrefix === 'comma' && isArray(obj)) {
|
|
149
|
+
// we need to join elements in
|
|
150
|
+
if (encodeValuesOnly && encoder) {
|
|
151
|
+
// @ts-expect-error values only
|
|
152
|
+
obj = maybe_map(obj, encoder);
|
|
153
|
+
}
|
|
154
|
+
obj_keys = [{ value: obj.length > 0 ? obj.join(',') || null : void undefined }];
|
|
155
|
+
} else if (isArray(filter)) {
|
|
156
|
+
obj_keys = filter;
|
|
157
|
+
} else {
|
|
158
|
+
const keys = Object.keys(obj);
|
|
159
|
+
obj_keys = sort ? keys.sort(sort) : keys;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const encoded_prefix = encodeDotInKeys ? String(prefix).replace(/\./g, '%2E') : String(prefix);
|
|
163
|
+
|
|
164
|
+
const adjusted_prefix =
|
|
165
|
+
commaRoundTrip && isArray(obj) && obj.length === 1 ? encoded_prefix + '[]' : encoded_prefix;
|
|
166
|
+
|
|
167
|
+
if (allowEmptyArrays && isArray(obj) && obj.length === 0) {
|
|
168
|
+
return adjusted_prefix + '[]';
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
for (let j = 0; j < obj_keys.length; ++j) {
|
|
172
|
+
const key = obj_keys[j];
|
|
173
|
+
const value =
|
|
174
|
+
// @ts-ignore
|
|
175
|
+
typeof key === 'object' && typeof key.value !== 'undefined' ? key.value : obj[key as any];
|
|
176
|
+
|
|
177
|
+
if (skipNulls && value === null) {
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// @ts-ignore
|
|
182
|
+
const encoded_key = allowDots && encodeDotInKeys ? (key as any).replace(/\./g, '%2E') : key;
|
|
183
|
+
const key_prefix =
|
|
184
|
+
isArray(obj) ?
|
|
185
|
+
typeof generateArrayPrefix === 'function' ?
|
|
186
|
+
generateArrayPrefix(adjusted_prefix, encoded_key)
|
|
187
|
+
: adjusted_prefix
|
|
188
|
+
: adjusted_prefix + (allowDots ? '.' + encoded_key : '[' + encoded_key + ']');
|
|
189
|
+
|
|
190
|
+
sideChannel.set(object, step);
|
|
191
|
+
const valueSideChannel = new WeakMap();
|
|
192
|
+
valueSideChannel.set(sentinel, sideChannel);
|
|
193
|
+
push_to_array(
|
|
194
|
+
values,
|
|
195
|
+
inner_stringify(
|
|
196
|
+
value,
|
|
197
|
+
key_prefix,
|
|
198
|
+
generateArrayPrefix,
|
|
199
|
+
commaRoundTrip,
|
|
200
|
+
allowEmptyArrays,
|
|
201
|
+
strictNullHandling,
|
|
202
|
+
skipNulls,
|
|
203
|
+
encodeDotInKeys,
|
|
204
|
+
// @ts-ignore
|
|
205
|
+
generateArrayPrefix === 'comma' && encodeValuesOnly && isArray(obj) ? null : encoder,
|
|
206
|
+
filter,
|
|
207
|
+
sort,
|
|
208
|
+
allowDots,
|
|
209
|
+
serializeDate,
|
|
210
|
+
format,
|
|
211
|
+
formatter,
|
|
212
|
+
encodeValuesOnly,
|
|
213
|
+
charset,
|
|
214
|
+
valueSideChannel,
|
|
215
|
+
),
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return values;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function normalize_stringify_options(
|
|
223
|
+
opts: StringifyOptions = defaults,
|
|
224
|
+
): NonNullableProperties<Omit<StringifyOptions, 'indices'>> & { indices?: boolean } {
|
|
225
|
+
if (typeof opts.allowEmptyArrays !== 'undefined' && typeof opts.allowEmptyArrays !== 'boolean') {
|
|
226
|
+
throw new TypeError('`allowEmptyArrays` option can only be `true` or `false`, when provided');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (typeof opts.encodeDotInKeys !== 'undefined' && typeof opts.encodeDotInKeys !== 'boolean') {
|
|
230
|
+
throw new TypeError('`encodeDotInKeys` option can only be `true` or `false`, when provided');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (opts.encoder !== null && typeof opts.encoder !== 'undefined' && typeof opts.encoder !== 'function') {
|
|
234
|
+
throw new TypeError('Encoder has to be a function.');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const charset = opts.charset || defaults.charset;
|
|
238
|
+
if (typeof opts.charset !== 'undefined' && opts.charset !== 'utf-8' && opts.charset !== 'iso-8859-1') {
|
|
239
|
+
throw new TypeError('The charset option must be either utf-8, iso-8859-1, or undefined');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let format = default_format;
|
|
243
|
+
if (typeof opts.format !== 'undefined') {
|
|
244
|
+
if (!has(formatters, opts.format)) {
|
|
245
|
+
throw new TypeError('Unknown format option provided.');
|
|
246
|
+
}
|
|
247
|
+
format = opts.format;
|
|
248
|
+
}
|
|
249
|
+
const formatter = formatters[format];
|
|
250
|
+
|
|
251
|
+
let filter = defaults.filter;
|
|
252
|
+
if (typeof opts.filter === 'function' || isArray(opts.filter)) {
|
|
253
|
+
filter = opts.filter;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let arrayFormat: StringifyOptions['arrayFormat'];
|
|
257
|
+
if (opts.arrayFormat && opts.arrayFormat in array_prefix_generators) {
|
|
258
|
+
arrayFormat = opts.arrayFormat;
|
|
259
|
+
} else if ('indices' in opts) {
|
|
260
|
+
arrayFormat = opts.indices ? 'indices' : 'repeat';
|
|
261
|
+
} else {
|
|
262
|
+
arrayFormat = defaults.arrayFormat;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if ('commaRoundTrip' in opts && typeof opts.commaRoundTrip !== 'boolean') {
|
|
266
|
+
throw new TypeError('`commaRoundTrip` must be a boolean, or absent');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const allowDots =
|
|
270
|
+
typeof opts.allowDots === 'undefined' ?
|
|
271
|
+
!!opts.encodeDotInKeys === true ?
|
|
272
|
+
true
|
|
273
|
+
: defaults.allowDots
|
|
274
|
+
: !!opts.allowDots;
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
addQueryPrefix: typeof opts.addQueryPrefix === 'boolean' ? opts.addQueryPrefix : defaults.addQueryPrefix,
|
|
278
|
+
// @ts-ignore
|
|
279
|
+
allowDots: allowDots,
|
|
280
|
+
allowEmptyArrays:
|
|
281
|
+
typeof opts.allowEmptyArrays === 'boolean' ? !!opts.allowEmptyArrays : defaults.allowEmptyArrays,
|
|
282
|
+
arrayFormat: arrayFormat,
|
|
283
|
+
charset: charset,
|
|
284
|
+
charsetSentinel:
|
|
285
|
+
typeof opts.charsetSentinel === 'boolean' ? opts.charsetSentinel : defaults.charsetSentinel,
|
|
286
|
+
commaRoundTrip: !!opts.commaRoundTrip,
|
|
287
|
+
delimiter: typeof opts.delimiter === 'undefined' ? defaults.delimiter : opts.delimiter,
|
|
288
|
+
encode: typeof opts.encode === 'boolean' ? opts.encode : defaults.encode,
|
|
289
|
+
encodeDotInKeys:
|
|
290
|
+
typeof opts.encodeDotInKeys === 'boolean' ? opts.encodeDotInKeys : defaults.encodeDotInKeys,
|
|
291
|
+
encoder: typeof opts.encoder === 'function' ? opts.encoder : defaults.encoder,
|
|
292
|
+
encodeValuesOnly:
|
|
293
|
+
typeof opts.encodeValuesOnly === 'boolean' ? opts.encodeValuesOnly : defaults.encodeValuesOnly,
|
|
294
|
+
filter: filter,
|
|
295
|
+
format: format,
|
|
296
|
+
formatter: formatter,
|
|
297
|
+
serializeDate: typeof opts.serializeDate === 'function' ? opts.serializeDate : defaults.serializeDate,
|
|
298
|
+
skipNulls: typeof opts.skipNulls === 'boolean' ? opts.skipNulls : defaults.skipNulls,
|
|
299
|
+
// @ts-ignore
|
|
300
|
+
sort: typeof opts.sort === 'function' ? opts.sort : null,
|
|
301
|
+
strictNullHandling:
|
|
302
|
+
typeof opts.strictNullHandling === 'boolean' ? opts.strictNullHandling : defaults.strictNullHandling,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function stringify(object: any, opts: StringifyOptions = {}) {
|
|
307
|
+
let obj = object;
|
|
308
|
+
const options = normalize_stringify_options(opts);
|
|
309
|
+
|
|
310
|
+
let obj_keys: PropertyKey[] | undefined;
|
|
311
|
+
let filter;
|
|
312
|
+
|
|
313
|
+
if (typeof options.filter === 'function') {
|
|
314
|
+
filter = options.filter;
|
|
315
|
+
obj = filter('', obj);
|
|
316
|
+
} else if (isArray(options.filter)) {
|
|
317
|
+
filter = options.filter;
|
|
318
|
+
obj_keys = filter;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const keys: string[] = [];
|
|
322
|
+
|
|
323
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
324
|
+
return '';
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const generateArrayPrefix = array_prefix_generators[options.arrayFormat];
|
|
328
|
+
const commaRoundTrip = generateArrayPrefix === 'comma' && options.commaRoundTrip;
|
|
329
|
+
|
|
330
|
+
if (!obj_keys) {
|
|
331
|
+
obj_keys = Object.keys(obj);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (options.sort) {
|
|
335
|
+
obj_keys.sort(options.sort);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const sideChannel = new WeakMap();
|
|
339
|
+
for (let i = 0; i < obj_keys.length; ++i) {
|
|
340
|
+
const key = obj_keys[i]!;
|
|
341
|
+
|
|
342
|
+
if (options.skipNulls && obj[key] === null) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
push_to_array(
|
|
346
|
+
keys,
|
|
347
|
+
inner_stringify(
|
|
348
|
+
obj[key],
|
|
349
|
+
key,
|
|
350
|
+
// @ts-expect-error
|
|
351
|
+
generateArrayPrefix,
|
|
352
|
+
commaRoundTrip,
|
|
353
|
+
options.allowEmptyArrays,
|
|
354
|
+
options.strictNullHandling,
|
|
355
|
+
options.skipNulls,
|
|
356
|
+
options.encodeDotInKeys,
|
|
357
|
+
options.encode ? options.encoder : null,
|
|
358
|
+
options.filter,
|
|
359
|
+
options.sort,
|
|
360
|
+
options.allowDots,
|
|
361
|
+
options.serializeDate,
|
|
362
|
+
options.format,
|
|
363
|
+
options.formatter,
|
|
364
|
+
options.encodeValuesOnly,
|
|
365
|
+
options.charset,
|
|
366
|
+
sideChannel,
|
|
367
|
+
),
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const joined = keys.join(options.delimiter);
|
|
372
|
+
let prefix = options.addQueryPrefix === true ? '?' : '';
|
|
373
|
+
|
|
374
|
+
if (options.charsetSentinel) {
|
|
375
|
+
if (options.charset === 'iso-8859-1') {
|
|
376
|
+
// encodeURIComponent('✓'), the "numeric entity" representation of a checkmark
|
|
377
|
+
prefix += 'utf8=%26%2310003%3B&';
|
|
378
|
+
} else {
|
|
379
|
+
// encodeURIComponent('✓')
|
|
380
|
+
prefix += 'utf8=%E2%9C%93&';
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return joined.length > 0 ? prefix + joined : '';
|
|
385
|
+
}
|