@rei-standard/amsg-client 2.9.0-next.2 → 2.9.0-next.3
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.cjs +89 -0
- package/dist/index.d.cts +98 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.mjs +89 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -597,6 +597,95 @@ var ReiClient = class {
|
|
|
597
597
|
data: decrypted
|
|
598
598
|
};
|
|
599
599
|
}
|
|
600
|
+
// ─── Client state (single-user cloud mirror) ────────────────────
|
|
601
|
+
/**
|
|
602
|
+
* Batch-upsert client-state entries (single-user worker,
|
|
603
|
+
* amsg-server 2.6.0+ `/client-state`).
|
|
604
|
+
*
|
|
605
|
+
* The worker keeps one live copy per (namespace, key); this client is
|
|
606
|
+
* the only writer. Send everything that changed in ONE call — e.g.
|
|
607
|
+
* inside the few-seconds window before iOS backgrounds the page — the
|
|
608
|
+
* server upserts the whole batch in a single DB round trip. Upserts
|
|
609
|
+
* are last-write-wins on `updatedAt`: entries older than the stored
|
|
610
|
+
* row are skipped, so re-sending a stale batch is harmless.
|
|
611
|
+
*
|
|
612
|
+
* The payload is encrypted like every other amsg-server call
|
|
613
|
+
* (requires `init()`); the worker re-encrypts each value at rest
|
|
614
|
+
* under the per-user key.
|
|
615
|
+
*
|
|
616
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
617
|
+
* - `value`: pre-serialized string (the SDK does not stringify it for you).
|
|
618
|
+
* - `updatedAt`: epoch milliseconds.
|
|
619
|
+
* @returns {Promise<Object>} `{ success, data?: { upserted, skipped }, error? }`
|
|
620
|
+
*/
|
|
621
|
+
async putClientState(entries) {
|
|
622
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
623
|
+
throw new TypeError("[rei-standard-amsg-client] entries must be a non-empty array");
|
|
624
|
+
}
|
|
625
|
+
const json = JSON.stringify({ entries });
|
|
626
|
+
this._assertPayloadSize(json, "putClientState");
|
|
627
|
+
const encrypted = await this._encrypt(json);
|
|
628
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
629
|
+
method: "PUT",
|
|
630
|
+
headers: this._withServerToken({
|
|
631
|
+
"Content-Type": "application/json",
|
|
632
|
+
"X-User-Id": this._userId,
|
|
633
|
+
"X-Payload-Encrypted": "true",
|
|
634
|
+
"X-Encryption-Version": "1"
|
|
635
|
+
}),
|
|
636
|
+
body: JSON.stringify(encrypted)
|
|
637
|
+
});
|
|
638
|
+
return res.json();
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Read every entry of one client-state namespace.
|
|
642
|
+
*
|
|
643
|
+
* The response rides the encrypted-response envelope (same as
|
|
644
|
+
* `listMessages`); this method decrypts it and returns plaintext
|
|
645
|
+
* values.
|
|
646
|
+
*
|
|
647
|
+
* @param {string} namespace
|
|
648
|
+
* @returns {Promise<Object>} `{ success, data?: { namespace, entries }, error? }`
|
|
649
|
+
* where `entries` is `Array<{ namespace, key, value, updatedAt }>`.
|
|
650
|
+
*/
|
|
651
|
+
async getClientState(namespace) {
|
|
652
|
+
if (typeof namespace !== "string" || !namespace.trim()) {
|
|
653
|
+
throw new TypeError("[rei-standard-amsg-client] namespace must be a non-empty string");
|
|
654
|
+
}
|
|
655
|
+
const res = await fetch(
|
|
656
|
+
`${this._baseUrl}/client-state?namespace=${encodeURIComponent(namespace)}`,
|
|
657
|
+
{
|
|
658
|
+
method: "GET",
|
|
659
|
+
headers: this._withServerToken({
|
|
660
|
+
"X-User-Id": this._userId,
|
|
661
|
+
"X-Response-Encrypted": "true",
|
|
662
|
+
"X-Encryption-Version": "1"
|
|
663
|
+
})
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
const json = await res.json();
|
|
667
|
+
if (!json?.success || json?.encrypted !== true) return json;
|
|
668
|
+
const decrypted = await this._decrypt(json.data);
|
|
669
|
+
return {
|
|
670
|
+
success: true,
|
|
671
|
+
encrypted: true,
|
|
672
|
+
version: json.version || 1,
|
|
673
|
+
data: decrypted
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Wipe every client-state entry of this user, across all namespaces —
|
|
678
|
+
* e.g. behind a "clear cloud state" settings action.
|
|
679
|
+
*
|
|
680
|
+
* @returns {Promise<Object>} `{ success, data?: { deleted }, error? }`
|
|
681
|
+
*/
|
|
682
|
+
async clearClientState() {
|
|
683
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
684
|
+
method: "DELETE",
|
|
685
|
+
headers: this._withServerToken({ "X-User-Id": this._userId })
|
|
686
|
+
});
|
|
687
|
+
return res.json();
|
|
688
|
+
}
|
|
600
689
|
// ─── Push Subscription ──────────────────────────────────────────
|
|
601
690
|
/**
|
|
602
691
|
* Subscribe to Web Push notifications.
|
package/dist/index.d.cts
CHANGED
|
@@ -992,6 +992,104 @@ class ReiClient {
|
|
|
992
992
|
};
|
|
993
993
|
}
|
|
994
994
|
|
|
995
|
+
// ─── Client state (single-user cloud mirror) ────────────────────
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Batch-upsert client-state entries (single-user worker,
|
|
999
|
+
* amsg-server 2.6.0+ `/client-state`).
|
|
1000
|
+
*
|
|
1001
|
+
* The worker keeps one live copy per (namespace, key); this client is
|
|
1002
|
+
* the only writer. Send everything that changed in ONE call — e.g.
|
|
1003
|
+
* inside the few-seconds window before iOS backgrounds the page — the
|
|
1004
|
+
* server upserts the whole batch in a single DB round trip. Upserts
|
|
1005
|
+
* are last-write-wins on `updatedAt`: entries older than the stored
|
|
1006
|
+
* row are skipped, so re-sending a stale batch is harmless.
|
|
1007
|
+
*
|
|
1008
|
+
* The payload is encrypted like every other amsg-server call
|
|
1009
|
+
* (requires `init()`); the worker re-encrypts each value at rest
|
|
1010
|
+
* under the per-user key.
|
|
1011
|
+
*
|
|
1012
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
1013
|
+
* - `value`: pre-serialized string (the SDK does not stringify it for you).
|
|
1014
|
+
* - `updatedAt`: epoch milliseconds.
|
|
1015
|
+
* @returns {Promise<Object>} `{ success, data?: { upserted, skipped }, error? }`
|
|
1016
|
+
*/
|
|
1017
|
+
async putClientState(entries) {
|
|
1018
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1019
|
+
throw new TypeError('[rei-standard-amsg-client] entries must be a non-empty array');
|
|
1020
|
+
}
|
|
1021
|
+
const json = JSON.stringify({ entries });
|
|
1022
|
+
this._assertPayloadSize(json, 'putClientState');
|
|
1023
|
+
const encrypted = await this._encrypt(json);
|
|
1024
|
+
|
|
1025
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
1026
|
+
method: 'PUT',
|
|
1027
|
+
headers: this._withServerToken({
|
|
1028
|
+
'Content-Type': 'application/json',
|
|
1029
|
+
'X-User-Id': this._userId,
|
|
1030
|
+
'X-Payload-Encrypted': 'true',
|
|
1031
|
+
'X-Encryption-Version': '1'
|
|
1032
|
+
}),
|
|
1033
|
+
body: JSON.stringify(encrypted)
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
return res.json();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Read every entry of one client-state namespace.
|
|
1041
|
+
*
|
|
1042
|
+
* The response rides the encrypted-response envelope (same as
|
|
1043
|
+
* `listMessages`); this method decrypts it and returns plaintext
|
|
1044
|
+
* values.
|
|
1045
|
+
*
|
|
1046
|
+
* @param {string} namespace
|
|
1047
|
+
* @returns {Promise<Object>} `{ success, data?: { namespace, entries }, error? }`
|
|
1048
|
+
* where `entries` is `Array<{ namespace, key, value, updatedAt }>`.
|
|
1049
|
+
*/
|
|
1050
|
+
async getClientState(namespace) {
|
|
1051
|
+
if (typeof namespace !== 'string' || !namespace.trim()) {
|
|
1052
|
+
throw new TypeError('[rei-standard-amsg-client] namespace must be a non-empty string');
|
|
1053
|
+
}
|
|
1054
|
+
const res = await fetch(
|
|
1055
|
+
`${this._baseUrl}/client-state?namespace=${encodeURIComponent(namespace)}`,
|
|
1056
|
+
{
|
|
1057
|
+
method: 'GET',
|
|
1058
|
+
headers: this._withServerToken({
|
|
1059
|
+
'X-User-Id': this._userId,
|
|
1060
|
+
'X-Response-Encrypted': 'true',
|
|
1061
|
+
'X-Encryption-Version': '1'
|
|
1062
|
+
})
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
const json = await res.json();
|
|
1067
|
+
if (!json?.success || json?.encrypted !== true) return json;
|
|
1068
|
+
|
|
1069
|
+
const decrypted = await this._decrypt(json.data);
|
|
1070
|
+
return {
|
|
1071
|
+
success: true,
|
|
1072
|
+
encrypted: true,
|
|
1073
|
+
version: json.version || 1,
|
|
1074
|
+
data: decrypted
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Wipe every client-state entry of this user, across all namespaces —
|
|
1080
|
+
* e.g. behind a "clear cloud state" settings action.
|
|
1081
|
+
*
|
|
1082
|
+
* @returns {Promise<Object>} `{ success, data?: { deleted }, error? }`
|
|
1083
|
+
*/
|
|
1084
|
+
async clearClientState() {
|
|
1085
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
1086
|
+
method: 'DELETE',
|
|
1087
|
+
headers: this._withServerToken({ 'X-User-Id': this._userId })
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
return res.json();
|
|
1091
|
+
}
|
|
1092
|
+
|
|
995
1093
|
// ─── Push Subscription ──────────────────────────────────────────
|
|
996
1094
|
|
|
997
1095
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -992,6 +992,104 @@ class ReiClient {
|
|
|
992
992
|
};
|
|
993
993
|
}
|
|
994
994
|
|
|
995
|
+
// ─── Client state (single-user cloud mirror) ────────────────────
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* Batch-upsert client-state entries (single-user worker,
|
|
999
|
+
* amsg-server 2.6.0+ `/client-state`).
|
|
1000
|
+
*
|
|
1001
|
+
* The worker keeps one live copy per (namespace, key); this client is
|
|
1002
|
+
* the only writer. Send everything that changed in ONE call — e.g.
|
|
1003
|
+
* inside the few-seconds window before iOS backgrounds the page — the
|
|
1004
|
+
* server upserts the whole batch in a single DB round trip. Upserts
|
|
1005
|
+
* are last-write-wins on `updatedAt`: entries older than the stored
|
|
1006
|
+
* row are skipped, so re-sending a stale batch is harmless.
|
|
1007
|
+
*
|
|
1008
|
+
* The payload is encrypted like every other amsg-server call
|
|
1009
|
+
* (requires `init()`); the worker re-encrypts each value at rest
|
|
1010
|
+
* under the per-user key.
|
|
1011
|
+
*
|
|
1012
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
1013
|
+
* - `value`: pre-serialized string (the SDK does not stringify it for you).
|
|
1014
|
+
* - `updatedAt`: epoch milliseconds.
|
|
1015
|
+
* @returns {Promise<Object>} `{ success, data?: { upserted, skipped }, error? }`
|
|
1016
|
+
*/
|
|
1017
|
+
async putClientState(entries) {
|
|
1018
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
1019
|
+
throw new TypeError('[rei-standard-amsg-client] entries must be a non-empty array');
|
|
1020
|
+
}
|
|
1021
|
+
const json = JSON.stringify({ entries });
|
|
1022
|
+
this._assertPayloadSize(json, 'putClientState');
|
|
1023
|
+
const encrypted = await this._encrypt(json);
|
|
1024
|
+
|
|
1025
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
1026
|
+
method: 'PUT',
|
|
1027
|
+
headers: this._withServerToken({
|
|
1028
|
+
'Content-Type': 'application/json',
|
|
1029
|
+
'X-User-Id': this._userId,
|
|
1030
|
+
'X-Payload-Encrypted': 'true',
|
|
1031
|
+
'X-Encryption-Version': '1'
|
|
1032
|
+
}),
|
|
1033
|
+
body: JSON.stringify(encrypted)
|
|
1034
|
+
});
|
|
1035
|
+
|
|
1036
|
+
return res.json();
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* Read every entry of one client-state namespace.
|
|
1041
|
+
*
|
|
1042
|
+
* The response rides the encrypted-response envelope (same as
|
|
1043
|
+
* `listMessages`); this method decrypts it and returns plaintext
|
|
1044
|
+
* values.
|
|
1045
|
+
*
|
|
1046
|
+
* @param {string} namespace
|
|
1047
|
+
* @returns {Promise<Object>} `{ success, data?: { namespace, entries }, error? }`
|
|
1048
|
+
* where `entries` is `Array<{ namespace, key, value, updatedAt }>`.
|
|
1049
|
+
*/
|
|
1050
|
+
async getClientState(namespace) {
|
|
1051
|
+
if (typeof namespace !== 'string' || !namespace.trim()) {
|
|
1052
|
+
throw new TypeError('[rei-standard-amsg-client] namespace must be a non-empty string');
|
|
1053
|
+
}
|
|
1054
|
+
const res = await fetch(
|
|
1055
|
+
`${this._baseUrl}/client-state?namespace=${encodeURIComponent(namespace)}`,
|
|
1056
|
+
{
|
|
1057
|
+
method: 'GET',
|
|
1058
|
+
headers: this._withServerToken({
|
|
1059
|
+
'X-User-Id': this._userId,
|
|
1060
|
+
'X-Response-Encrypted': 'true',
|
|
1061
|
+
'X-Encryption-Version': '1'
|
|
1062
|
+
})
|
|
1063
|
+
}
|
|
1064
|
+
);
|
|
1065
|
+
|
|
1066
|
+
const json = await res.json();
|
|
1067
|
+
if (!json?.success || json?.encrypted !== true) return json;
|
|
1068
|
+
|
|
1069
|
+
const decrypted = await this._decrypt(json.data);
|
|
1070
|
+
return {
|
|
1071
|
+
success: true,
|
|
1072
|
+
encrypted: true,
|
|
1073
|
+
version: json.version || 1,
|
|
1074
|
+
data: decrypted
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/**
|
|
1079
|
+
* Wipe every client-state entry of this user, across all namespaces —
|
|
1080
|
+
* e.g. behind a "clear cloud state" settings action.
|
|
1081
|
+
*
|
|
1082
|
+
* @returns {Promise<Object>} `{ success, data?: { deleted }, error? }`
|
|
1083
|
+
*/
|
|
1084
|
+
async clearClientState() {
|
|
1085
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
1086
|
+
method: 'DELETE',
|
|
1087
|
+
headers: this._withServerToken({ 'X-User-Id': this._userId })
|
|
1088
|
+
});
|
|
1089
|
+
|
|
1090
|
+
return res.json();
|
|
1091
|
+
}
|
|
1092
|
+
|
|
995
1093
|
// ─── Push Subscription ──────────────────────────────────────────
|
|
996
1094
|
|
|
997
1095
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -575,6 +575,95 @@ var ReiClient = class {
|
|
|
575
575
|
data: decrypted
|
|
576
576
|
};
|
|
577
577
|
}
|
|
578
|
+
// ─── Client state (single-user cloud mirror) ────────────────────
|
|
579
|
+
/**
|
|
580
|
+
* Batch-upsert client-state entries (single-user worker,
|
|
581
|
+
* amsg-server 2.6.0+ `/client-state`).
|
|
582
|
+
*
|
|
583
|
+
* The worker keeps one live copy per (namespace, key); this client is
|
|
584
|
+
* the only writer. Send everything that changed in ONE call — e.g.
|
|
585
|
+
* inside the few-seconds window before iOS backgrounds the page — the
|
|
586
|
+
* server upserts the whole batch in a single DB round trip. Upserts
|
|
587
|
+
* are last-write-wins on `updatedAt`: entries older than the stored
|
|
588
|
+
* row are skipped, so re-sending a stale batch is harmless.
|
|
589
|
+
*
|
|
590
|
+
* The payload is encrypted like every other amsg-server call
|
|
591
|
+
* (requires `init()`); the worker re-encrypts each value at rest
|
|
592
|
+
* under the per-user key.
|
|
593
|
+
*
|
|
594
|
+
* @param {Array<{ namespace: string, key: string, value: string, updatedAt: number }>} entries
|
|
595
|
+
* - `value`: pre-serialized string (the SDK does not stringify it for you).
|
|
596
|
+
* - `updatedAt`: epoch milliseconds.
|
|
597
|
+
* @returns {Promise<Object>} `{ success, data?: { upserted, skipped }, error? }`
|
|
598
|
+
*/
|
|
599
|
+
async putClientState(entries) {
|
|
600
|
+
if (!Array.isArray(entries) || entries.length === 0) {
|
|
601
|
+
throw new TypeError("[rei-standard-amsg-client] entries must be a non-empty array");
|
|
602
|
+
}
|
|
603
|
+
const json = JSON.stringify({ entries });
|
|
604
|
+
this._assertPayloadSize(json, "putClientState");
|
|
605
|
+
const encrypted = await this._encrypt(json);
|
|
606
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
607
|
+
method: "PUT",
|
|
608
|
+
headers: this._withServerToken({
|
|
609
|
+
"Content-Type": "application/json",
|
|
610
|
+
"X-User-Id": this._userId,
|
|
611
|
+
"X-Payload-Encrypted": "true",
|
|
612
|
+
"X-Encryption-Version": "1"
|
|
613
|
+
}),
|
|
614
|
+
body: JSON.stringify(encrypted)
|
|
615
|
+
});
|
|
616
|
+
return res.json();
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Read every entry of one client-state namespace.
|
|
620
|
+
*
|
|
621
|
+
* The response rides the encrypted-response envelope (same as
|
|
622
|
+
* `listMessages`); this method decrypts it and returns plaintext
|
|
623
|
+
* values.
|
|
624
|
+
*
|
|
625
|
+
* @param {string} namespace
|
|
626
|
+
* @returns {Promise<Object>} `{ success, data?: { namespace, entries }, error? }`
|
|
627
|
+
* where `entries` is `Array<{ namespace, key, value, updatedAt }>`.
|
|
628
|
+
*/
|
|
629
|
+
async getClientState(namespace) {
|
|
630
|
+
if (typeof namespace !== "string" || !namespace.trim()) {
|
|
631
|
+
throw new TypeError("[rei-standard-amsg-client] namespace must be a non-empty string");
|
|
632
|
+
}
|
|
633
|
+
const res = await fetch(
|
|
634
|
+
`${this._baseUrl}/client-state?namespace=${encodeURIComponent(namespace)}`,
|
|
635
|
+
{
|
|
636
|
+
method: "GET",
|
|
637
|
+
headers: this._withServerToken({
|
|
638
|
+
"X-User-Id": this._userId,
|
|
639
|
+
"X-Response-Encrypted": "true",
|
|
640
|
+
"X-Encryption-Version": "1"
|
|
641
|
+
})
|
|
642
|
+
}
|
|
643
|
+
);
|
|
644
|
+
const json = await res.json();
|
|
645
|
+
if (!json?.success || json?.encrypted !== true) return json;
|
|
646
|
+
const decrypted = await this._decrypt(json.data);
|
|
647
|
+
return {
|
|
648
|
+
success: true,
|
|
649
|
+
encrypted: true,
|
|
650
|
+
version: json.version || 1,
|
|
651
|
+
data: decrypted
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Wipe every client-state entry of this user, across all namespaces —
|
|
656
|
+
* e.g. behind a "clear cloud state" settings action.
|
|
657
|
+
*
|
|
658
|
+
* @returns {Promise<Object>} `{ success, data?: { deleted }, error? }`
|
|
659
|
+
*/
|
|
660
|
+
async clearClientState() {
|
|
661
|
+
const res = await fetch(`${this._baseUrl}/client-state`, {
|
|
662
|
+
method: "DELETE",
|
|
663
|
+
headers: this._withServerToken({ "X-User-Id": this._userId })
|
|
664
|
+
});
|
|
665
|
+
return res.json();
|
|
666
|
+
}
|
|
578
667
|
// ─── Push Subscription ──────────────────────────────────────────
|
|
579
668
|
/**
|
|
580
669
|
* Subscribe to Web Push notifications.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rei-standard/amsg-client",
|
|
3
|
-
"version": "2.9.0-next.
|
|
3
|
+
"version": "2.9.0-next.3",
|
|
4
4
|
"description": "ReiStandard Active Messaging browser client SDK — also re-exports shared push types, builders, and guards from @rei-standard/amsg-shared",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|