api-ape 3.0.1 → 4.1.0

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.
Files changed (186) hide show
  1. package/README.md +58 -570
  2. package/client/README.md +73 -14
  3. package/client/auth/crypto/aead.js +214 -0
  4. package/client/auth/crypto/constants.js +32 -0
  5. package/client/auth/crypto/encoding.js +104 -0
  6. package/client/auth/crypto/files.md +27 -0
  7. package/client/auth/crypto/kdf.js +217 -0
  8. package/client/auth/crypto-utils.js +118 -0
  9. package/client/auth/files.md +52 -0
  10. package/client/auth/key-recovery.js +288 -0
  11. package/client/auth/recovery/constants.js +37 -0
  12. package/client/auth/recovery/files.md +23 -0
  13. package/client/auth/recovery/key-derivation.js +61 -0
  14. package/client/auth/recovery/sss-browser.js +189 -0
  15. package/client/auth/share-storage.js +205 -0
  16. package/client/auth/storage/constants.js +18 -0
  17. package/client/auth/storage/db.js +132 -0
  18. package/client/auth/storage/files.md +27 -0
  19. package/client/auth/storage/keys.js +173 -0
  20. package/client/auth/storage/shares.js +200 -0
  21. package/client/browser.js +190 -23
  22. package/client/connectSocket.js +418 -988
  23. package/client/connection/README.md +23 -0
  24. package/client/connection/fileDownload.js +256 -0
  25. package/client/connection/fileHandling.js +450 -0
  26. package/client/connection/fileUtils.js +346 -0
  27. package/client/connection/files.md +71 -0
  28. package/client/connection/messageHandler.js +105 -0
  29. package/client/connection/network.js +350 -0
  30. package/client/connection/proxy.js +233 -0
  31. package/client/connection/sender.js +333 -0
  32. package/client/connection/state.js +321 -0
  33. package/client/connection/subscriptions.js +151 -0
  34. package/client/files.md +53 -0
  35. package/client/index.js +298 -142
  36. package/client/transports/README.md +50 -0
  37. package/client/transports/files.md +41 -0
  38. package/client/transports/streamParser.js +195 -0
  39. package/client/transports/streaming.js +555 -202
  40. package/dist/ape.js +6 -1
  41. package/dist/ape.js.map +4 -4
  42. package/index.d.ts +38 -16
  43. package/package.json +32 -7
  44. package/server/README.md +287 -53
  45. package/server/adapters/README.md +28 -19
  46. package/server/adapters/files.md +68 -0
  47. package/server/adapters/firebase.js +543 -160
  48. package/server/adapters/index.js +362 -112
  49. package/server/adapters/mongo.js +530 -140
  50. package/server/adapters/postgres.js +534 -155
  51. package/server/adapters/redis.js +508 -143
  52. package/server/adapters/supabase.js +555 -186
  53. package/server/client/README.md +43 -0
  54. package/server/client/connection.js +586 -0
  55. package/server/client/files.md +40 -0
  56. package/server/client/index.js +342 -0
  57. package/server/files.md +54 -0
  58. package/server/index.js +332 -27
  59. package/server/lib/README.md +26 -0
  60. package/server/lib/broadcast/clients.js +219 -0
  61. package/server/lib/broadcast/files.md +58 -0
  62. package/server/lib/broadcast/index.js +57 -0
  63. package/server/lib/broadcast/publishProxy.js +110 -0
  64. package/server/lib/broadcast/pubsub.js +137 -0
  65. package/server/lib/broadcast/sendProxy.js +103 -0
  66. package/server/lib/bun.js +315 -99
  67. package/server/lib/fileTransfer/README.md +63 -0
  68. package/server/lib/fileTransfer/files.md +30 -0
  69. package/server/lib/fileTransfer/streaming.js +435 -0
  70. package/server/lib/fileTransfer.js +710 -326
  71. package/server/lib/files.md +111 -0
  72. package/server/lib/httpUtils.js +283 -0
  73. package/server/lib/loader.js +208 -7
  74. package/server/lib/longPolling/README.md +63 -0
  75. package/server/lib/longPolling/files.md +44 -0
  76. package/server/lib/longPolling/getHandler.js +365 -0
  77. package/server/lib/longPolling/postHandler.js +327 -0
  78. package/server/lib/longPolling.js +174 -221
  79. package/server/lib/main.js +369 -532
  80. package/server/lib/runtimes/README.md +42 -0
  81. package/server/lib/runtimes/bun.js +586 -0
  82. package/server/lib/runtimes/files.md +56 -0
  83. package/server/lib/runtimes/node.js +511 -0
  84. package/server/lib/wiring.js +539 -98
  85. package/server/lib/ws/README.md +35 -0
  86. package/server/lib/ws/adapters/README.md +54 -0
  87. package/server/lib/ws/adapters/bun.js +538 -170
  88. package/server/lib/ws/adapters/deno.js +623 -149
  89. package/server/lib/ws/adapters/files.md +42 -0
  90. package/server/lib/ws/files.md +74 -0
  91. package/server/lib/ws/frames.js +532 -154
  92. package/server/lib/ws/index.js +207 -10
  93. package/server/lib/ws/server.js +385 -92
  94. package/server/lib/ws/socket.js +549 -181
  95. package/server/lib/wsProvider.js +363 -89
  96. package/server/plugins/binary.js +282 -0
  97. package/server/security/README.md +92 -0
  98. package/server/security/auth/README.md +319 -0
  99. package/server/security/auth/adapters/files.md +95 -0
  100. package/server/security/auth/adapters/ldap/constants.js +37 -0
  101. package/server/security/auth/adapters/ldap/files.md +19 -0
  102. package/server/security/auth/adapters/ldap/helpers.js +111 -0
  103. package/server/security/auth/adapters/ldap.js +353 -0
  104. package/server/security/auth/adapters/oauth2/constants.js +41 -0
  105. package/server/security/auth/adapters/oauth2/files.md +19 -0
  106. package/server/security/auth/adapters/oauth2/helpers.js +123 -0
  107. package/server/security/auth/adapters/oauth2.js +273 -0
  108. package/server/security/auth/adapters/opaque-handlers.js +314 -0
  109. package/server/security/auth/adapters/opaque.js +205 -0
  110. package/server/security/auth/adapters/saml/constants.js +52 -0
  111. package/server/security/auth/adapters/saml/files.md +19 -0
  112. package/server/security/auth/adapters/saml/helpers.js +74 -0
  113. package/server/security/auth/adapters/saml.js +173 -0
  114. package/server/security/auth/adapters/totp.js +703 -0
  115. package/server/security/auth/adapters/webauthn.js +625 -0
  116. package/server/security/auth/files.md +61 -0
  117. package/server/security/auth/framework/constants.js +27 -0
  118. package/server/security/auth/framework/files.md +23 -0
  119. package/server/security/auth/framework/handlers.js +272 -0
  120. package/server/security/auth/framework/socket-auth.js +177 -0
  121. package/server/security/auth/handlers/auth-messages.js +143 -0
  122. package/server/security/auth/handlers/files.md +28 -0
  123. package/server/security/auth/index.js +290 -0
  124. package/server/security/auth/mfa/crypto/aead.js +148 -0
  125. package/server/security/auth/mfa/crypto/constants.js +35 -0
  126. package/server/security/auth/mfa/crypto/files.md +27 -0
  127. package/server/security/auth/mfa/crypto/kdf.js +120 -0
  128. package/server/security/auth/mfa/crypto/utils.js +68 -0
  129. package/server/security/auth/mfa/crypto-utils.js +80 -0
  130. package/server/security/auth/mfa/files.md +77 -0
  131. package/server/security/auth/mfa/ledger/constants.js +75 -0
  132. package/server/security/auth/mfa/ledger/errors.js +73 -0
  133. package/server/security/auth/mfa/ledger/files.md +23 -0
  134. package/server/security/auth/mfa/ledger/share-record.js +32 -0
  135. package/server/security/auth/mfa/ledger.js +255 -0
  136. package/server/security/auth/mfa/recovery/constants.js +67 -0
  137. package/server/security/auth/mfa/recovery/files.md +19 -0
  138. package/server/security/auth/mfa/recovery/handlers.js +216 -0
  139. package/server/security/auth/mfa/recovery.js +191 -0
  140. package/server/security/auth/mfa/sss/constants.js +21 -0
  141. package/server/security/auth/mfa/sss/files.md +23 -0
  142. package/server/security/auth/mfa/sss/gf256.js +103 -0
  143. package/server/security/auth/mfa/sss/serialization.js +82 -0
  144. package/server/security/auth/mfa/sss.js +161 -0
  145. package/server/security/auth/mfa/two-of-three/constants.js +58 -0
  146. package/server/security/auth/mfa/two-of-three/files.md +23 -0
  147. package/server/security/auth/mfa/two-of-three/handlers.js +241 -0
  148. package/server/security/auth/mfa/two-of-three/helpers.js +71 -0
  149. package/server/security/auth/mfa/two-of-three.js +136 -0
  150. package/server/security/auth/nonce-manager.js +89 -0
  151. package/server/security/auth/state-machine-mfa.js +269 -0
  152. package/server/security/auth/state-machine.js +257 -0
  153. package/server/security/extractRootDomain.js +144 -16
  154. package/server/security/files.md +51 -0
  155. package/server/security/origin.js +197 -15
  156. package/server/security/reply.js +274 -16
  157. package/server/socket/README.md +119 -0
  158. package/server/socket/authMiddleware.js +299 -0
  159. package/server/socket/files.md +86 -0
  160. package/server/socket/open.js +154 -8
  161. package/server/socket/pluginHooks.js +334 -0
  162. package/server/socket/receive.js +184 -225
  163. package/server/socket/receiveContext.js +117 -0
  164. package/server/socket/send.js +416 -78
  165. package/server/socket/tagUtils.js +402 -0
  166. package/server/utils/README.md +19 -0
  167. package/server/utils/deepRequire.js +255 -30
  168. package/server/utils/files.md +57 -0
  169. package/server/utils/genId.js +182 -20
  170. package/server/utils/parseUserAgent.js +313 -251
  171. package/server/utils/userAgent/README.md +65 -0
  172. package/server/utils/userAgent/files.md +46 -0
  173. package/server/utils/userAgent/patterns.js +545 -0
  174. package/utils/README.md +21 -0
  175. package/utils/files.md +66 -0
  176. package/utils/jss/README.md +21 -0
  177. package/utils/jss/decode.js +471 -0
  178. package/utils/jss/encode.js +312 -0
  179. package/utils/jss/files.md +68 -0
  180. package/utils/jss/plugins.js +210 -0
  181. package/utils/jss.js +219 -273
  182. package/utils/messageHash.js +238 -35
  183. package/dist/api-ape.min.js +0 -2
  184. package/dist/api-ape.min.js.map +0 -7
  185. package/server/client.js +0 -308
  186. package/server/lib/broadcast.js +0 -146
package/utils/jss.js CHANGED
@@ -1,273 +1,219 @@
1
- //JsonSuperSet
2
-
3
- // TODO: add tests
4
- // check for any repeated ref not just cyclical references
5
- // support nasted array a<![,,[D]]>:["a","b",[Date]]
6
- // support array for the same type a<![*D]>:[Date,Date,Date]
7
-
8
- function encode(obj) {
9
- const tagLookup = {
10
- '[object RegExp]': 'R',
11
- '[object Date]': 'D',
12
- '[object Error]': 'E',
13
- "[object Undefined]": 'U',
14
- "[object Map]": 'M',
15
- "[object Set]": 'S',
16
- };
17
- const visited = new WeakMap();
18
-
19
- function encodeValue(value, path = '') {
20
- const type = typeof value;
21
- const tag = tagLookup[Object.prototype.toString.call(value)];
22
- // console.log({tag,value,path})
23
- if (tag !== undefined) {
24
- if ('D' === tag) return [tag, value.valueOf()];
25
- if ('E' === tag) return [tag, [value.name, value.message, value.stack]];
26
- if ('R' === tag) return [tag, value.toString()];
27
- if ('U' === tag) return [tag, null];
28
- if ('S' === tag) return [tag, Array.from(value)];
29
- if ('M' === tag) return [tag, Object.fromEntries(value)];
30
-
31
- return [tag, JSON.stringify(value)];
32
- } else if (type === 'object' && value !== null) {
33
- /*if (value.$ID) {
34
- return ['', value.$ID];
35
- }*/
36
- if (visitedEncode.has(value)) {
37
- return ['P', visitedEncode.get(value)];
38
- }
39
- visitedEncode.set(value, path);
40
- const isArray = Array.isArray(value);
41
- // keep index with undefined in Array
42
- const keys = isArray ? Array.from(Array(value.length).keys()) : Object.keys(value);
43
- const result = isArray ? [] : {};
44
- const typesFound = [];
45
- for (let i = 0; i < keys.length; i++) {
46
- const key = keys[i];
47
- const [t, v] = encodeValue(value[key], key);
48
- // console.log([t, v])
49
- if (isArray) {
50
- typesFound.push(t);
51
- result.push(v);
52
- // remove key with undefined from Objects
53
- } else if (value[key] !== undefined) {
54
- result[key + (t ? `<!${t}>` : '')] = v;
55
- }
56
- }
57
-
58
- visited.delete(value);
59
- if (isArray && typesFound.find((t) => !!t)) {
60
- return [`[${typesFound.join()}]`, result];
61
- }
62
- return ['', result];
63
- } else {
64
- return ['', value];
65
- }
66
- } // END encodeValue
67
-
68
- let keys = [];
69
- // console.log(obj)
70
- if (Array.isArray(obj)) {
71
- keys = Array.from(Array(obj.length).keys())
72
- } else {
73
- keys = Object.keys(obj);
74
- }
75
-
76
- // Track root object to handle self-references
77
- const visitedEncode = new WeakMap();
78
- visitedEncode.set(obj, []); // Root path is empty array
79
-
80
- function encodeValueWithVisited(value, path = []) {
81
- const type = typeof value;
82
- const tag = tagLookup[Object.prototype.toString.call(value)];
83
- if (tag !== undefined) {
84
- if ('D' === tag) return [tag, value.valueOf()];
85
- if ('E' === tag) return [tag, [value.name, value.message, value.stack]];
86
- if ('R' === tag) return [tag, value.toString()];
87
- if ('U' === tag) return [tag, null];
88
- if ('S' === tag) return [tag, Array.from(value)];
89
- if ('M' === tag) return [tag, Object.fromEntries(value)];
90
- return [tag, JSON.stringify(value)];
91
- } else if (type === 'object' && value !== null) {
92
- if (visitedEncode.has(value)) {
93
- return ['P', visitedEncode.get(value)]; // Return array path
94
- }
95
- visitedEncode.set(value, path);
96
- const isArray = Array.isArray(value);
97
- const objKeys = isArray ? Array.from(Array(value.length).keys()) : Object.keys(value);
98
- const result = isArray ? [] : {};
99
- const typesFound = [];
100
- for (let i = 0; i < objKeys.length; i++) {
101
- const key = objKeys[i];
102
- const [t, v] = encodeValueWithVisited(value[key], [...path, key]); // Append key to path array
103
- if (isArray) {
104
- typesFound.push(t);
105
- result.push(v);
106
- } else if (value[key] !== undefined) {
107
- result[key + (t ? `<!${t}>` : '')] = v;
108
- }
109
- }
110
- if (isArray && typesFound.find((t) => !!t)) {
111
- return [`[${typesFound.join()}]`, result];
112
- }
113
- return ['', result];
114
- } else {
115
- return ['', value];
116
- }
117
- }
118
-
119
- const result = {};
120
- for (let i = 0; i < keys.length; i++) {
121
- const key = keys[i];
122
- // remove key with undefined from Objects
123
- if (obj[key] !== undefined) {
124
- const [t, v] = encodeValueWithVisited(obj[key], [key]); // Start path with single key
125
- result[key + (t ? `<!${t}>` : '')] = v;
126
- }
127
- }
128
- return result;
129
- } // END encode
130
-
131
- function stringify(obj) {
132
- return JSON.stringify(encode(obj))
133
- }
134
-
135
-
136
- function parse(encoded) {
137
- return decode(JSON.parse(encoded))
138
- }
139
-
140
- function decode(data) {
141
- const result = {};
142
- const pointers2Res = [];
143
- const tagLookup = {
144
- R: (s) => new RegExp(s),
145
- D: (n) => new Date(n),
146
- P: function (sourceToPointAt, replaceAtThisPlace) {
147
- // Both paths are now arrays
148
- pointers2Res.push([sourceToPointAt, replaceAtThisPlace]);
149
- return null; // Placeholder, will be replaced by changeAttributeReference
150
- },
151
- E: ([name, message, stack]) => {
152
- let err;
153
- try {
154
- err = new global[name](message);
155
- if (err instanceof Error) err.stack = stack;
156
- else throw {};
157
- } catch (e) {
158
- err = new Error(message);
159
- err.name = name;
160
- err.stack = stack;
161
- }
162
- return err;
163
- },
164
- U: () => undefined,
165
- S: (a) => new Set(a),
166
- M: (o) => new Map(Object.entries(o))
167
- };
168
- const visited = new Map();
169
-
170
- function decodeValue(name, tag, val) {
171
- // this is now an array path
172
- const currentPath = Array.isArray(this) ? this : [];
173
-
174
- if (tag in tagLookup) {
175
- return tagLookup[tag](val, currentPath);
176
- } else if (Array.isArray(val)) {
177
- if (tag && tag.startsWith('[')) {
178
- const typeTags = tag.slice(1, -1).split(',');
179
- const res = [];
180
- for (let i = 0; i < val.length; i++) {
181
- // Pass path with array index appended
182
- const itemPath = [...currentPath, i];
183
- const decodedValue = decodeValue.call(
184
- itemPath,
185
- i.toString(),
186
- typeTags[i],
187
- val[i]
188
- );
189
- res.push(decodedValue);
190
- }
191
- return res;
192
- } else {
193
- const res = [];
194
- for (let i = 0; i < val.length; i++) {
195
- const decodedValue = decodeValue.call([...currentPath, i], '', '', val[i]);
196
- res.push(decodedValue);
197
- }
198
- return res;
199
- }
200
- } else if ('object' === typeof val && val !== null) {
201
- if (visited.has(val)) {
202
- return visited.get(val);
203
- }
204
- visited.set(val, {});
205
- const res = {};
206
- for (const key in val) {
207
- const [nam, t] = parseKeyWithTags(key);
208
- const decodedValue = decodeValue.call(
209
- [...currentPath, nam],
210
- nam,
211
- t,
212
- val[key]
213
- );
214
- res[nam] = decodedValue;
215
- }
216
- visited.set(val, res);
217
- return res;
218
- } else {
219
- return val;
220
- }
221
- } // END decodeValue
222
-
223
- function parseKeyWithTags(key) {
224
- const match = key.match(/(.+)(<!(.)>)/);
225
- if (match) {
226
- return [match[1], match[3]];
227
- }
228
- // Try multi-character tags like array types [,D,]
229
- const multiMatch = key.match(/(.+)(<!!(.+)>)/);
230
- if (multiMatch) {
231
- return [multiMatch[1], multiMatch[3]];
232
- }
233
- // Also handle array type tags that start with [
234
- const arrayMatch = key.match(/(.+)(<!\[(.*)>)/);
235
- if (arrayMatch) {
236
- return [arrayMatch[1], '[' + arrayMatch[3]];
237
- }
238
- return [key, undefined];
239
- } // END parseKeyWithTags
240
-
241
- for (const key in data) {
242
- const [name, tag] = parseKeyWithTags(key);
243
- // Start with path containing just the key name
244
- result[name] = decodeValue.call([name], name, tag, data[key]);
245
- }
246
- pointers2Res.forEach(changeAttributeReference.bind(null, result));
247
- return result;
248
- } // END decode
249
-
250
- function changeAttributeReference(obj, [refPath, attrPath]) {
251
- // refPath and attrPath are now arrays, no splitting needed
252
- const refKeys = refPath || [];
253
- const attrKeys = attrPath || [];
254
-
255
- // Get the reference target by traversing refPath
256
- let ref = obj;
257
- for (let i = 0; i < refKeys.length; i++) {
258
- ref = ref[refKeys[i]];
259
- }
260
-
261
- // Get the parent of the attribute to set
262
- let attr = obj;
263
- for (let i = 0; i < attrKeys.length - 1; i++) {
264
- attr = attr[attrKeys[i]];
265
- }
266
-
267
- // Set the attribute to point to the reference
268
- attr[attrKeys[attrKeys.length - 1]] = ref;
269
- return obj;
270
- } // END changeAttributeReference
271
-
272
-
273
- module.exports = { parse, stringify, encode, decode };
1
+ /**
2
+ * @fileoverview JSON Super Set (JSS) - Extended JSON Serialization
3
+ *
4
+ * JSS extends standard JSON to support additional JavaScript types that
5
+ * JSON.stringify/parse cannot handle. This enables api-ape to transparently
6
+ * serialize and deserialize rich data types over WebSocket connections.
7
+ *
8
+ * ## Supported Types
9
+ *
10
+ * | Type | Tag | Description |
11
+ * |----------------|-----|---------------------------------------|
12
+ * | Date | `D` | Serialized as timestamp |
13
+ * | RegExp | `R` | Serialized as string pattern |
14
+ * | Error | `E` | Preserves name, message, and stack |
15
+ * | undefined | `U` | Explicitly represents undefined |
16
+ * | Map | `M` | Converted to/from object entries |
17
+ * | Set | `S` | Converted to/from array |
18
+ * | Circular Refs | `P` | Preserved via path pointers |
19
+ *
20
+ * ## Wire Format
21
+ *
22
+ * JSS encodes type information into object keys using a tag suffix:
23
+ *
24
+ * ```javascript
25
+ * // Original object
26
+ * { createdAt: new Date('2024-01-01'), pattern: /hello/i }
27
+ *
28
+ * // JSS encoded
29
+ * { "createdAt<!D>": 1704067200000, "pattern<!R>": "/hello/i" }
30
+ * ```
31
+ *
32
+ * ## Usage
33
+ *
34
+ * JSS provides a drop-in replacement for JSON.stringify/parse:
35
+ *
36
+ * ```javascript
37
+ * const jss = require('./jss')
38
+ *
39
+ * // Stringify (like JSON.stringify but handles extended types)
40
+ * const str = jss.stringify({ date: new Date(), regex: /test/ })
41
+ *
42
+ * // Parse (like JSON.parse but restores extended types)
43
+ * const obj = jss.parse(str)
44
+ * // obj.date is a Date instance
45
+ * // obj.regex is a RegExp instance
46
+ * ```
47
+ *
48
+ * ## API Methods
49
+ *
50
+ * - `stringify(obj)` - Convert object to JSS string (high-level)
51
+ * - `parse(str)` - Parse JSS string back to object (high-level)
52
+ * - `encode(obj)` - Convert object to JSS-encoded plain object
53
+ * - `decode(obj)` - Convert JSS-encoded object back to original
54
+ *
55
+ * ## Circular Reference Handling
56
+ *
57
+ * JSS can handle circular references using path pointers:
58
+ *
59
+ * ```javascript
60
+ * const obj = { name: 'root' }
61
+ * obj.self = obj // Circular reference
62
+ *
63
+ * const str = jss.stringify(obj) // No error!
64
+ * const restored = jss.parse(str)
65
+ * console.log(restored.self === restored) // true
66
+ * ```
67
+ *
68
+ * @module utils/jss
69
+ * @see {@link module:utils/jss/encode} for encoding implementation
70
+ * @see {@link module:utils/jss/decode} for decoding implementation
71
+ *
72
+ * @example
73
+ * // Basic usage with dates
74
+ * const jss = require('./jss')
75
+ *
76
+ * const data = {
77
+ * user: 'Alice',
78
+ * loginAt: new Date(),
79
+ * settings: new Map([['theme', 'dark'], ['lang', 'en']])
80
+ * }
81
+ *
82
+ * const serialized = jss.stringify(data)
83
+ * // Can be sent over WebSocket
84
+ *
85
+ * const restored = jss.parse(serialized)
86
+ * console.log(restored.loginAt instanceof Date) // true
87
+ * console.log(restored.settings instanceof Map) // true
88
+ *
89
+ * @example
90
+ * // Error serialization
91
+ * const jss = require('./jss')
92
+ *
93
+ * try {
94
+ * throw new TypeError('Invalid input')
95
+ * } catch (err) {
96
+ * const serialized = jss.stringify({ error: err })
97
+ * const restored = jss.parse(serialized)
98
+ *
99
+ * console.log(restored.error instanceof TypeError) // true
100
+ * console.log(restored.error.message) // 'Invalid input'
101
+ * console.log(restored.error.stack) // Original stack trace
102
+ * }
103
+ *
104
+ * @example
105
+ * // Low-level encode/decode for inspection
106
+ * const jss = require('./jss')
107
+ *
108
+ * const encoded = jss.encode({
109
+ * date: new Date('2024-01-01'),
110
+ * items: new Set([1, 2, 3])
111
+ * })
112
+ *
113
+ * console.log(encoded)
114
+ * // {
115
+ * // "date<!D>": 1704067200000,
116
+ * // "items<!S>": [1, 2, 3]
117
+ * // }
118
+ *
119
+ * const decoded = jss.decode(encoded)
120
+ * // Original types restored
121
+ */
122
+
123
+ const { encode, stringify } = require("./jss/encode");
124
+ const { decode, parse } = require("./jss/decode");
125
+ const { register: custom, clearPlugins } = require("./jss/plugins");
126
+
127
+ /**
128
+ * Parse a JSS-encoded string back into an object with restored types
129
+ *
130
+ * This is the primary method for deserializing JSS data. It combines
131
+ * JSON.parse with type restoration for Date, RegExp, Error, Map, Set,
132
+ * undefined, and circular references.
133
+ *
134
+ * @function parse
135
+ * @param {string} encoded - JSS-encoded JSON string
136
+ * @returns {any} Decoded object with original types restored
137
+ * @throws {SyntaxError} If the string is not valid JSON
138
+ *
139
+ * @example
140
+ * const obj = jss.parse('{"date<!D>":1704067200000}')
141
+ * console.log(obj.date instanceof Date) // true
142
+ */
143
+
144
+ /**
145
+ * Convert an object to a JSS-encoded JSON string
146
+ *
147
+ * This is the primary method for serializing objects with extended types.
148
+ * It handles Date, RegExp, Error, Map, Set, undefined, and circular
149
+ * references that would cause JSON.stringify to fail or lose information.
150
+ *
151
+ * @function stringify
152
+ * @param {any} obj - Object to serialize
153
+ * @returns {string} JSS-encoded JSON string
154
+ *
155
+ * @example
156
+ * const str = jss.stringify({
157
+ * when: new Date(),
158
+ * pattern: /\d+/g,
159
+ * items: new Set([1, 2, 3])
160
+ * })
161
+ */
162
+
163
+ /**
164
+ * Encode an object to JSS format (without stringifying)
165
+ *
166
+ * Low-level method that converts extended types to their tagged
167
+ * representations. Useful for inspection or custom serialization.
168
+ *
169
+ * @function encode
170
+ * @param {any} obj - Object to encode
171
+ * @returns {Object} Plain object with tagged keys for extended types
172
+ *
173
+ * @example
174
+ * const encoded = jss.encode({ date: new Date() })
175
+ * // { "date<!D>": 1704067200000 }
176
+ */
177
+
178
+ /**
179
+ * Decode a JSS-encoded object (without parsing from string)
180
+ *
181
+ * Low-level method that restores extended types from their tagged
182
+ * representations. Useful when working with already-parsed data.
183
+ *
184
+ * @function decode
185
+ * @param {Object} data - JSS-encoded plain object
186
+ * @returns {any} Object with original types restored
187
+ *
188
+ * @example
189
+ * const decoded = jss.decode({ "date<!D>": 1704067200000 })
190
+ * console.log(decoded.date instanceof Date) // true
191
+ */
192
+
193
+ /**
194
+ * Register a custom type handler plugin
195
+ *
196
+ * Plugins extend JSS to handle custom types beyond the built-in set.
197
+ * Each plugin is identified by a single-character tag that appears in
198
+ * the serialized format (e.g., `"key<!X>": value`).
199
+ *
200
+ * @function custom
201
+ * @param {string} tag - Single character tag identifier (e.g., 'X', 'Z')
202
+ * @param {Object} config - Plugin configuration object
203
+ * @param {function(string|number, any): boolean} config.check - Determines if plugin handles value
204
+ * @param {function(string[], string|number, any, Object): any} config.encode - Transform for serialization
205
+ * @param {function(any, string[], Object): any} config.decode - Restore from serialization
206
+ * @param {function=} config.onSend - Optional send lifecycle hook
207
+ * @param {function=} config.onReceive - Optional receive lifecycle hook
208
+ * @throws {Error} If tag conflicts with built-in or existing custom type
209
+ *
210
+ * @example
211
+ * // Register a custom BigInt handler
212
+ * jss.custom('B', {
213
+ * check: (key, value) => typeof value === 'bigint',
214
+ * encode: (path, key, value) => value.toString(),
215
+ * decode: (value) => BigInt(value)
216
+ * })
217
+ */
218
+
219
+ module.exports = { parse, stringify, encode, decode, custom, clearPlugins };