@fatcherjs/middleware-json 3.0.0-alpha-10 → 3.0.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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2022 Fansy
3
+ Copyright (c) 2022 fatcherjs
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,8 +1,25 @@
1
1
  # @fatcherjs/middleware-json
2
2
 
3
- <a href="https://npmjs.com/package/@fatcherjs/middleware-json"><img src="https://img.shields.io/npm/v/@fatcherjs/middleware-json.svg" alt="npm package"></a>
4
- [![install size](https://packagephobia.com/badge?p=@fatcherjs/middleware-json)](https://packagephobia.com/result?p=@fatcherjs/middleware-json)
5
- <a href="https://unpkg.com/@fatcherjs/middleware-json"><img alt="Size" src="https://img.badgesize.io/https://unpkg.com/@fatcherjs/middleware-json"></a>
3
+ <div align="center">
4
+ <a href="https://codecov.io/github/fatcherjs/middleware-json" >
5
+ <img src="https://codecov.io/github/fatcherjs/middleware-json/graph/badge.svg?token=TFKUGW6YNI"/>
6
+ </a>
7
+ <a href="https://www.jsdelivr.com/package/npm/@fatcherjs/middleware-json">
8
+ <img src="https://data.jsdelivr.com/v1/package/npm/@fatcherjs/middleware-json/badge?style=rounded" alt="jsDelivr">
9
+ </a>
10
+ <a href="https://packagephobia.com/result?p=@fatcherjs/middleware-json">
11
+ <img src="https://packagephobia.com/badge?p=@fatcherjs/middleware-json" alt="install size">
12
+ </a>
13
+ <a href="https://unpkg.com/@fatcherjs/middleware-json">
14
+ <img src="https://img.badgesize.io/https://unpkg.com/@fatcherjs/middleware-json" alt="Size">
15
+ </a>
16
+ <a href="https://npmjs.com/package/@fatcherjs/middleware-json">
17
+ <img src="https://img.shields.io/npm/v/@fatcherjs/middleware-json.svg" alt="npm package">
18
+ </a>
19
+ <a href="https://github.com/fatcherjs/middleware-json/actions/workflows/ci.yml">
20
+ <img src="https://github.com/fatcherjs/middleware-json/actions/workflows/ci.yml/badge.svg?branch=master" alt="build status">
21
+ </a>
22
+ </div>
6
23
 
7
24
  ## Install
8
25
 
@@ -15,7 +32,20 @@
15
32
  ### CDN
16
33
 
17
34
  ```html
35
+ <script src="https://cdn.jsdelivr.net/npm/fatcher/dist/fatcher.min.js"></script>
18
36
  <script src="https://cdn.jsdelivr.net/npm/@fatcherjs/middleware-json/dist/index.min.js"></script>
37
+
38
+ <script>
39
+ Fatcher.fatcher('url', {
40
+ middlewares: [FatcherMiddlewareJson],
41
+ progressive: {
42
+ isPlaceholder: (value) => value.startsWith('$$');
43
+ }
44
+ }).then(response => {
45
+ console.log(response.skeleton); // Promise Tree
46
+ console.log(response.snapshot); // Progressive Json
47
+ });
48
+ </script>
19
49
  ```
20
50
 
21
51
  ## Usage
@@ -25,14 +55,102 @@ import { fatcher } from 'fatcher';
25
55
  import { json } from '@fatcherjs/middleware-json';
26
56
 
27
57
  const res = await fatcher('https://foo.bar/get', {
28
- middlewares: [json()],
58
+ middlewares: [json],
59
+ progressive: {
60
+ isPlaceholder: (value) => value.startsWith('$$');
61
+ }
29
62
  });
30
63
 
31
- const streamingJson = await res.readStreamAsJson((string: string, buffer: Uint8Array) => {
32
- console.log(string, buffer); // chunks for streaming string
33
- }); // full result
64
+ console.log(response.skeleton); // Promise Tree
65
+ console.log(response.snapshot); // Progressive Json
66
+ ```
67
+
68
+ ## Examples
69
+
70
+ ### React 19
71
+
72
+ ```jsx
73
+ import { useState } from 'react';
74
+ import { json } from '@fatcherjs/middleware-json';
75
+ import { fatcher } from 'fatcher';
76
+ import { use, Suspense } from 'react';
77
+ function delay(ms) {
78
+ return new Promise(resolve => {
79
+ setTimeout(() => {
80
+ resolve();
81
+ }, ms);
82
+ });
83
+ }
84
+
85
+ async function fetchMessage() {
86
+ const response = await fatcher('https://test.com', {
87
+ progressive: {
88
+ isPlaceholder: value => value.startsWith('$$'),
89
+ },
90
+ middlewares: [
91
+ json,
92
+ () => {
93
+ return new Response(
94
+ new ReadableStream({
95
+ async start(controller) {
96
+ controller.enqueue(new TextEncoder().encode(JSON.stringify({ userInfo: '$$1' })));
97
+ await delay(300);
98
+ controller.enqueue(
99
+ new TextEncoder().encode(
100
+ JSON.stringify({
101
+ $$1: { name: 'Alice', age: 18 },
102
+ }),
103
+ ),
104
+ );
105
+ controller.close();
106
+ },
107
+ }),
108
+ );
109
+ },
110
+ ],
111
+ });
112
+
113
+ return response.skeleton;
114
+ }
115
+
116
+ function UserInfo({ userInfoPromise }) {
117
+ const userInfo = use(userInfoPromise);
118
+
119
+ return userInfo ? `${userInfo.name} (${userInfo.age})` : 'loading...';
120
+ }
121
+
122
+ function Message({ skeleton }) {
123
+ const data = use(skeleton);
124
+
125
+ return (
126
+ <p>
127
+ Here is the message:{' '}
128
+ <Suspense fallback={<p>⌛Downloading UserInfo...</p>}>
129
+ <UserInfo userInfoPromise={data.userInfo} />
130
+ </Suspense>
131
+ </p>
132
+ );
133
+ }
134
+
135
+ export default function App() {
136
+ const [skeleton, setSkeleton] = useState(null);
137
+
138
+ async function download() {
139
+ setSkeleton(fetchMessage());
140
+ }
141
+
142
+ if (!skeleton) {
143
+ return <button onClick={download}>Download message</button>;
144
+ }
145
+
146
+ return (
147
+ <Suspense fallback={<p>⌛Downloading message...</p>}>
148
+ <Message skeleton={skeleton} />
149
+ </Suspense>
150
+ );
151
+ }
34
152
  ```
35
153
 
36
154
  ## License
37
155
 
38
- [MIT](https://github.com/fanhaoyuan/fatcher/blob/master/LICENSE)
156
+ [MIT](https://github.com/fatcherjs/middleware-json/blob/master/LICENSE)
@@ -0,0 +1,13 @@
1
+ type ProgressiveJSON = Record<string, any>;
2
+ export declare class Assembler {
3
+ readonly isPlaceholder: (value: string) => boolean;
4
+ skeleton: ProgressiveJSON | null;
5
+ snapshot: ProgressiveJSON;
6
+ private buffer;
7
+ constructor(isPlaceholder: (value: string) => boolean);
8
+ consume(frame: ProgressiveJSON): void;
9
+ replaceFrameValues(frame: ProgressiveJSON, path?: string): {
10
+ [k: string]: any;
11
+ };
12
+ }
13
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,11 +1,2 @@
1
- import { FatcherMiddleware } from 'fatcher';
2
-
3
- declare const json: () => FatcherMiddleware;
4
-
5
- declare module 'fatcher' {
6
- interface FatcherResponse {
7
- readStreamAsJson: <T>(onRead?: (chunk: string, buffer: Uint8Array) => void | Promise<void>) => Promise<T | null>;
8
- }
9
- }
10
-
11
- export { json };
1
+ export * from './middleware';
2
+ export * from './types';
package/dist/index.esm.js CHANGED
@@ -1,30 +1,304 @@
1
- import { readStreamByChunk } from '@fatcherjs/utils-shared';
2
-
3
- const json = () => {
4
- return async (context, next) => {
5
- const response = await next();
6
- if (response.bodyUsed || !response.body) {
7
- response.readStreamAsJson = async () => null;
8
- return response;
1
+ async function A(e, t) {
2
+ async function r(s) {
3
+ const { value: n, done: c } = await s.read();
4
+ c || (await t(n), await r(s));
5
+ }
6
+ return r(e.getReader());
7
+ }
8
+ function x(e) {
9
+ return typeof e == "symbol" || e instanceof Symbol;
10
+ }
11
+ function y(e) {
12
+ if (!e || typeof e != "object")
13
+ return !1;
14
+ const t = Object.getPrototypeOf(e);
15
+ return t === null || t === Object.prototype || Object.getPrototypeOf(t) === null ? Object.prototype.toString.call(e) === "[object Object]" : !1;
16
+ }
17
+ function h(e) {
18
+ return e === "__proto__";
19
+ }
20
+ function m(e, t) {
21
+ const r = {}, s = Object.keys(e);
22
+ for (let n = 0; n < s.length; n++) {
23
+ const c = s[n], i = e[c];
24
+ t(i, c) && (r[c] = i);
25
+ }
26
+ return r;
27
+ }
28
+ function T(e, t) {
29
+ return e === t || Number.isNaN(e) && Number.isNaN(t);
30
+ }
31
+ function b(e) {
32
+ return e instanceof Promise;
33
+ }
34
+ function I(e) {
35
+ return typeof e == "string";
36
+ }
37
+ function R(e) {
38
+ switch (typeof e) {
39
+ case "number":
40
+ case "symbol":
41
+ return !1;
42
+ case "string":
43
+ return e.includes(".") || e.includes("[") || e.includes("]");
44
+ }
45
+ }
46
+ function p(e) {
47
+ return typeof e == "string" || typeof e == "symbol" ? e : Object.is(e?.valueOf?.(), -0) ? "-0" : String(e);
48
+ }
49
+ function w(e) {
50
+ if (e == null)
51
+ return "";
52
+ if (typeof e == "string")
53
+ return e;
54
+ if (Array.isArray(e))
55
+ return e.map(w).join(",");
56
+ const t = String(e);
57
+ return t === "0" && Object.is(Number(e), -0) ? "-0" : t;
58
+ }
59
+ function O(e) {
60
+ if (Array.isArray(e))
61
+ return e.map(p);
62
+ if (typeof e == "symbol")
63
+ return [e];
64
+ e = w(e);
65
+ const t = [], r = e.length;
66
+ if (r === 0)
67
+ return t;
68
+ let s = 0, n = "", c = "", i = !1;
69
+ for (e.charCodeAt(0) === 46 && (t.push(""), s++); s < r; ) {
70
+ const o = e[s];
71
+ c ? o === "\\" && s + 1 < r ? (s++, n += e[s]) : o === c ? c = "" : n += o : i ? o === '"' || o === "'" ? c = o : o === "]" ? (i = !1, t.push(n), n = "") : n += o : o === "[" ? (i = !0, n && (t.push(n), n = "")) : o === "." ? n && (t.push(n), n = "") : n += o, s++;
72
+ }
73
+ return n && t.push(n), t;
74
+ }
75
+ function P(e, t, r) {
76
+ if (e == null)
77
+ return r;
78
+ switch (typeof t) {
79
+ case "string": {
80
+ if (h(t))
81
+ return r;
82
+ const s = e[t];
83
+ return s === void 0 ? R(t) ? P(e, O(t), r) : r : s;
9
84
  }
10
- const clonedResponse = response.clone();
11
- response.readStreamAsJson = async (onRead) => {
12
- const textDecoder = new TextDecoder();
13
- let string = "";
14
- await readStreamByChunk(clonedResponse.body, (chunk) => {
15
- const currentChunkString = textDecoder.decode(chunk, { stream: true });
16
- string += currentChunkString;
17
- onRead?.(currentChunkString, chunk);
18
- });
19
- string += textDecoder.decode(void 0, { stream: false });
20
- try {
21
- return JSON.parse(string);
22
- } catch {
23
- return null;
85
+ case "number":
86
+ case "symbol": {
87
+ typeof t == "number" && (t = p(t));
88
+ const s = e[t];
89
+ return s === void 0 ? r : s;
90
+ }
91
+ default: {
92
+ if (Array.isArray(t))
93
+ return _(e, t, r);
94
+ if (Object.is(t?.valueOf(), -0) ? t = "-0" : t = String(t), h(t))
95
+ return r;
96
+ const s = e[t];
97
+ return s === void 0 ? r : s;
98
+ }
99
+ }
100
+ }
101
+ function _(e, t, r) {
102
+ if (t.length === 0)
103
+ return r;
104
+ let s = e;
105
+ for (let n = 0; n < t.length; n++) {
106
+ if (s == null || h(t[n]))
107
+ return r;
108
+ s = s[t[n]];
109
+ }
110
+ return s === void 0 ? r : s;
111
+ }
112
+ function d(e) {
113
+ return e !== null && (typeof e == "object" || typeof e == "function");
114
+ }
115
+ const D = /^(?:0|[1-9]\d*)$/;
116
+ function F(e, t = Number.MAX_SAFE_INTEGER) {
117
+ switch (typeof e) {
118
+ case "number":
119
+ return Number.isInteger(e) && e >= 0 && e < t;
120
+ case "symbol":
121
+ return !1;
122
+ case "string":
123
+ return D.test(e);
124
+ }
125
+ }
126
+ const q = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/, B = /^\w*$/;
127
+ function C(e, t) {
128
+ return Array.isArray(e) ? !1 : typeof e == "number" || typeof e == "boolean" || e == null || x(e) ? !0 : typeof e == "string" && (B.test(e) || !q.test(e)) || t != null && Object.hasOwn(t, e);
129
+ }
130
+ const G = (e, t, r) => {
131
+ const s = e[t];
132
+ (!(Object.hasOwn(e, t) && T(s, r)) || r === void 0 && !(t in e)) && (e[t] = r);
133
+ };
134
+ function U(e, t, r, s) {
135
+ if (e == null && !d(e))
136
+ return e;
137
+ let n;
138
+ C(t, e) ? n = [t] : Array.isArray(t) ? n = t : n = O(t);
139
+ const c = r(P(e, n));
140
+ let i = e;
141
+ for (let o = 0; o < n.length && i != null; o++) {
142
+ const f = p(n[o]);
143
+ if (h(f))
144
+ continue;
145
+ let u;
146
+ if (o === n.length - 1)
147
+ u = c;
148
+ else {
149
+ const l = i[f], a = s?.(l, f, e);
150
+ u = a !== void 0 ? a : d(l) ? l : F(n[o + 1]) ? [] : {};
151
+ }
152
+ G(i, f, u), i = i[f];
153
+ }
154
+ return e;
155
+ }
156
+ function g(e, t, r) {
157
+ return U(e, t, () => r, () => {
158
+ });
159
+ }
160
+ function k() {
161
+ let e, t;
162
+ return { promise: new Promise((s, n) => {
163
+ e = s, t = n;
164
+ }), resolve: e, reject: t };
165
+ }
166
+ class J {
167
+ constructor(t) {
168
+ this.isPlaceholder = t;
169
+ }
170
+ skeleton = null;
171
+ snapshot = {};
172
+ buffer = /* @__PURE__ */ new Map();
173
+ consume(t) {
174
+ if (!y(t))
175
+ throw new TypeError("Frame must be an object");
176
+ if (!this.skeleton) {
177
+ this.skeleton = this.replaceFrameValues(t), this.snapshot = m(this.skeleton, (s) => !b(s));
178
+ return;
179
+ }
180
+ const r = Object.keys(t);
181
+ if (r.some((s) => !this.isPlaceholder(s)))
182
+ throw new TypeError("Patch has an invalid key");
183
+ for (const s of r) {
184
+ if (!this.buffer.has(s))
185
+ throw new TypeError("It is a unknown patch");
186
+ const { resolve: n, path: c } = this.buffer.get(s), i = t[s];
187
+ if (y(i)) {
188
+ const o = this.replaceFrameValues(i, c);
189
+ g(
190
+ this.snapshot,
191
+ c,
192
+ m(o, (f) => !b(f))
193
+ ), n(o);
194
+ continue;
195
+ }
196
+ g(this.snapshot, c, i), n(i);
197
+ }
198
+ }
199
+ replaceFrameValues(t, r) {
200
+ return Object.fromEntries(
201
+ Object.entries(t).map((s) => {
202
+ const [n, c] = s, i = [r, n].filter(Boolean).join(".");
203
+ if (I(c) && this.isPlaceholder(c)) {
204
+ const o = k();
205
+ return this.buffer.set(c, {
206
+ promise: o.promise,
207
+ resolve: o.resolve,
208
+ path: i
209
+ }), [n, o.promise];
210
+ }
211
+ return s;
212
+ })
213
+ );
214
+ }
215
+ }
216
+ class M {
217
+ buffer = "";
218
+ cursor = 0;
219
+ stack = [];
220
+ inString = !1;
221
+ escaped = !1;
222
+ started = !1;
223
+ frames = [];
224
+ parse(t) {
225
+ for (this.buffer += t; this.cursor < this.buffer.length; ) {
226
+ const r = this.buffer[this.cursor];
227
+ if (!this.started) {
228
+ if (/\s/.test(r)) {
229
+ this.cursor++;
230
+ continue;
231
+ }
232
+ if (r !== "{" && r !== "[")
233
+ throw new TypeError("Parse Error");
234
+ this.started = !0, this.stack.push(r), this.cursor++;
235
+ continue;
24
236
  }
25
- };
26
- return response;
27
- };
237
+ if (this.inString) {
238
+ if (this.escaped) {
239
+ this.escaped = !1, this.cursor++;
240
+ continue;
241
+ }
242
+ if (r === "\\") {
243
+ this.escaped = !0, this.cursor++;
244
+ continue;
245
+ }
246
+ r === '"' && (this.inString = !1), this.cursor++;
247
+ continue;
248
+ }
249
+ if (r === '"') {
250
+ this.inString = !0, this.cursor++;
251
+ continue;
252
+ }
253
+ if (r === "{" || r === "[") {
254
+ this.stack.push(r), this.cursor++;
255
+ continue;
256
+ }
257
+ if (r === "}" || r === "]") {
258
+ const s = this.stack.pop();
259
+ if (r === "}" && s !== "{" || r === "]" && s !== "[")
260
+ throw new TypeError("Parse Error");
261
+ if (this.cursor++, !this.stack.length) {
262
+ const n = this.buffer.slice(0, this.cursor);
263
+ this.buffer = this.buffer.slice(this.cursor), this.cursor = 0, this.started = !1, this.inString = !1, this.escaped = !1;
264
+ const c = JSON.parse(n);
265
+ this.frames.push(c);
266
+ continue;
267
+ }
268
+ continue;
269
+ }
270
+ this.cursor++;
271
+ }
272
+ }
273
+ drain() {
274
+ const t = this.frames;
275
+ return this.frames = [], t;
276
+ }
277
+ }
278
+ const W = {
279
+ name: "fatcher-middleware-json",
280
+ use: async (e, t) => {
281
+ const r = await t();
282
+ if (!e.progressive || r.bodyUsed || !r.body)
283
+ return r;
284
+ let s = !1;
285
+ const { promise: n, resolve: c } = k(), i = new J(e.progressive.isPlaceholder), o = new ReadableStream({
286
+ async start(u) {
287
+ const l = new TextDecoder(), a = new M();
288
+ await A(r.clone().body, (S) => {
289
+ const E = l.decode(S, { stream: !0 });
290
+ a.parse(E);
291
+ const N = a.drain();
292
+ for (const j of N)
293
+ i.consume(j), !s && i.skeleton && (s = !0, c(i.skeleton));
294
+ }), u.enqueue(new TextEncoder().encode(JSON.stringify(i.snapshot))), u.close();
295
+ }
296
+ });
297
+ await n;
298
+ const f = new Response(o, r);
299
+ return f.skeleton = i.skeleton, f.snapshot = i.snapshot, f;
300
+ }
301
+ };
302
+ export {
303
+ W as json
28
304
  };
29
-
30
- export { json };
package/dist/index.js CHANGED
@@ -1,34 +1 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- var utilsShared = require('@fatcherjs/utils-shared');
6
-
7
- const json = () => {
8
- return async (context, next) => {
9
- const response = await next();
10
- if (response.bodyUsed || !response.body) {
11
- response.readStreamAsJson = async () => null;
12
- return response;
13
- }
14
- const clonedResponse = response.clone();
15
- response.readStreamAsJson = async (onRead) => {
16
- const textDecoder = new TextDecoder();
17
- let string = "";
18
- await utilsShared.readStreamByChunk(clonedResponse.body, (chunk) => {
19
- const currentChunkString = textDecoder.decode(chunk, { stream: true });
20
- string += currentChunkString;
21
- onRead?.(currentChunkString, chunk);
22
- });
23
- string += textDecoder.decode(void 0, { stream: false });
24
- try {
25
- return JSON.parse(string);
26
- } catch {
27
- return null;
28
- }
29
- };
30
- return response;
31
- };
32
- };
33
-
34
- exports.json = json;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});async function A(e,t){async function r(s){const{value:n,done:c}=await s.read();c||(await t(n),await r(s))}return r(e.getReader())}function T(e){return typeof e=="symbol"||e instanceof Symbol}function y(e){if(!e||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)==="[object Object]":!1}function h(e){return e==="__proto__"}function m(e,t){const r={},s=Object.keys(e);for(let n=0;n<s.length;n++){const c=s[n],i=e[c];t(i,c)&&(r[c]=i)}return r}function x(e,t){return e===t||Number.isNaN(e)&&Number.isNaN(t)}function b(e){return e instanceof Promise}function I(e){return typeof e=="string"}function R(e){switch(typeof e){case"number":case"symbol":return!1;case"string":return e.includes(".")||e.includes("[")||e.includes("]")}}function p(e){return typeof e=="string"||typeof e=="symbol"?e:Object.is(e?.valueOf?.(),-0)?"-0":String(e)}function w(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(w).join(",");const t=String(e);return t==="0"&&Object.is(Number(e),-0)?"-0":t}function O(e){if(Array.isArray(e))return e.map(p);if(typeof e=="symbol")return[e];e=w(e);const t=[],r=e.length;if(r===0)return t;let s=0,n="",c="",i=!1;for(e.charCodeAt(0)===46&&(t.push(""),s++);s<r;){const o=e[s];c?o==="\\"&&s+1<r?(s++,n+=e[s]):o===c?c="":n+=o:i?o==='"'||o==="'"?c=o:o==="]"?(i=!1,t.push(n),n=""):n+=o:o==="["?(i=!0,n&&(t.push(n),n="")):o==="."?n&&(t.push(n),n=""):n+=o,s++}return n&&t.push(n),t}function P(e,t,r){if(e==null)return r;switch(typeof t){case"string":{if(h(t))return r;const s=e[t];return s===void 0?R(t)?P(e,O(t),r):r:s}case"number":case"symbol":{typeof t=="number"&&(t=p(t));const s=e[t];return s===void 0?r:s}default:{if(Array.isArray(t))return _(e,t,r);if(Object.is(t?.valueOf(),-0)?t="-0":t=String(t),h(t))return r;const s=e[t];return s===void 0?r:s}}}function _(e,t,r){if(t.length===0)return r;let s=e;for(let n=0;n<t.length;n++){if(s==null||h(t[n]))return r;s=s[t[n]]}return s===void 0?r:s}function d(e){return e!==null&&(typeof e=="object"||typeof e=="function")}const D=/^(?:0|[1-9]\d*)$/;function F(e,t=Number.MAX_SAFE_INTEGER){switch(typeof e){case"number":return Number.isInteger(e)&&e>=0&&e<t;case"symbol":return!1;case"string":return D.test(e)}}const q=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,B=/^\w*$/;function C(e,t){return Array.isArray(e)?!1:typeof e=="number"||typeof e=="boolean"||e==null||T(e)?!0:typeof e=="string"&&(B.test(e)||!q.test(e))||t!=null&&Object.hasOwn(t,e)}const G=(e,t,r)=>{const s=e[t];(!(Object.hasOwn(e,t)&&x(s,r))||r===void 0&&!(t in e))&&(e[t]=r)};function M(e,t,r,s){if(e==null&&!d(e))return e;let n;C(t,e)?n=[t]:Array.isArray(t)?n=t:n=O(t);const c=r(P(e,n));let i=e;for(let o=0;o<n.length&&i!=null;o++){const f=p(n[o]);if(h(f))continue;let u;if(o===n.length-1)u=c;else{const l=i[f],a=s?.(l,f,e);u=a!==void 0?a:d(l)?l:F(n[o+1])?[]:{}}G(i,f,u),i=i[f]}return e}function g(e,t,r){return M(e,t,()=>r,()=>{})}function S(){let e,t;return{promise:new Promise((s,n)=>{e=s,t=n}),resolve:e,reject:t}}class U{constructor(t){this.isPlaceholder=t}skeleton=null;snapshot={};buffer=new Map;consume(t){if(!y(t))throw new TypeError("Frame must be an object");if(!this.skeleton){this.skeleton=this.replaceFrameValues(t),this.snapshot=m(this.skeleton,s=>!b(s));return}const r=Object.keys(t);if(r.some(s=>!this.isPlaceholder(s)))throw new TypeError("Patch has an invalid key");for(const s of r){if(!this.buffer.has(s))throw new TypeError("It is a unknown patch");const{resolve:n,path:c}=this.buffer.get(s),i=t[s];if(y(i)){const o=this.replaceFrameValues(i,c);g(this.snapshot,c,m(o,f=>!b(f))),n(o);continue}g(this.snapshot,c,i),n(i)}}replaceFrameValues(t,r){return Object.fromEntries(Object.entries(t).map(s=>{const[n,c]=s,i=[r,n].filter(Boolean).join(".");if(I(c)&&this.isPlaceholder(c)){const o=S();return this.buffer.set(c,{promise:o.promise,resolve:o.resolve,path:i}),[n,o.promise]}return s}))}}class J{buffer="";cursor=0;stack=[];inString=!1;escaped=!1;started=!1;frames=[];parse(t){for(this.buffer+=t;this.cursor<this.buffer.length;){const r=this.buffer[this.cursor];if(!this.started){if(/\s/.test(r)){this.cursor++;continue}if(r!=="{"&&r!=="[")throw new TypeError("Parse Error");this.started=!0,this.stack.push(r),this.cursor++;continue}if(this.inString){if(this.escaped){this.escaped=!1,this.cursor++;continue}if(r==="\\"){this.escaped=!0,this.cursor++;continue}r==='"'&&(this.inString=!1),this.cursor++;continue}if(r==='"'){this.inString=!0,this.cursor++;continue}if(r==="{"||r==="["){this.stack.push(r),this.cursor++;continue}if(r==="}"||r==="]"){const s=this.stack.pop();if(r==="}"&&s!=="{"||r==="]"&&s!=="[")throw new TypeError("Parse Error");if(this.cursor++,!this.stack.length){const n=this.buffer.slice(0,this.cursor);this.buffer=this.buffer.slice(this.cursor),this.cursor=0,this.started=!1,this.inString=!1,this.escaped=!1;const c=JSON.parse(n);this.frames.push(c);continue}continue}this.cursor++}}drain(){const t=this.frames;return this.frames=[],t}}const W={name:"fatcher-middleware-json",use:async(e,t)=>{const r=await t();if(!e.progressive||r.bodyUsed||!r.body)return r;let s=!1;const{promise:n,resolve:c}=S(),i=new U(e.progressive.isPlaceholder),o=new ReadableStream({async start(u){const l=new TextDecoder,a=new J;await A(r.clone().body,k=>{const j=l.decode(k,{stream:!0});a.parse(j);const E=a.drain();for(const N of E)i.consume(N),!s&&i.skeleton&&(s=!0,c(i.skeleton))}),u.enqueue(new TextEncoder().encode(JSON.stringify(i.snapshot))),u.close()}});await n;const f=new Response(o,r);return f.skeleton=i.skeleton,f.snapshot=i.snapshot,f}};exports.json=W;
package/dist/index.min.js CHANGED
@@ -1 +1 @@
1
- (function(t,n){typeof exports=="object"&&typeof module!="undefined"?n(exports):typeof define=="function"&&define.amd?define(["exports"],n):(t=typeof globalThis!="undefined"?globalThis:t||self,n(t.FatcherMiddlewareJson={}))})(this,function(t){"use strict";async function n(i,a){async function e(s){const{value:r,done:o}=await s.read();o||(await a(r),await e(s))}return e(i.getReader())}const f=()=>async(i,a)=>{const e=await a();if(e.bodyUsed||!e.body)return e.readStreamAsJson=async()=>null,e;const s=e.clone();return e.readStreamAsJson=async r=>{const o=new TextDecoder;let c="";await n(s.body,d=>{const u=o.decode(d,{stream:!0});c+=u,r==null||r(u,d)}),c+=o.decode(void 0,{stream:!1});try{return JSON.parse(c)}catch(d){return null}},e};t.json=f,Object.defineProperty(t,"__esModule",{value:!0})});
1
+ (function(u,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(u=typeof globalThis<"u"?globalThis:u||self,a(u.FatcherMiddlewareJson={}))})(this,(function(u){"use strict";async function a(e,t){async function s(r){const{value:n,done:c}=await r.read();c||(await t(n),await s(r))}return s(e.getReader())}function E(e){return typeof e=="symbol"||e instanceof Symbol}function m(e){if(!e||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype||Object.getPrototypeOf(t)===null?Object.prototype.toString.call(e)==="[object Object]":!1}function y(e){return e==="__proto__"}function b(e,t){const s={},r=Object.keys(e);for(let n=0;n<r.length;n++){const c=r[n],i=e[c];t(i,c)&&(s[c]=i)}return s}function N(e,t){return e===t||Number.isNaN(e)&&Number.isNaN(t)}function g(e){return e instanceof Promise}function A(e){return typeof e=="string"}function T(e){switch(typeof e){case"number":case"symbol":return!1;case"string":return e.includes(".")||e.includes("[")||e.includes("]")}}function d(e){return typeof e=="string"||typeof e=="symbol"?e:Object.is(e?.valueOf?.(),-0)?"-0":String(e)}function w(e){if(e==null)return"";if(typeof e=="string")return e;if(Array.isArray(e))return e.map(w).join(",");const t=String(e);return t==="0"&&Object.is(Number(e),-0)?"-0":t}function O(e){if(Array.isArray(e))return e.map(d);if(typeof e=="symbol")return[e];e=w(e);const t=[],s=e.length;if(s===0)return t;let r=0,n="",c="",i=!1;for(e.charCodeAt(0)===46&&(t.push(""),r++);r<s;){const o=e[r];c?o==="\\"&&r+1<s?(r++,n+=e[r]):o===c?c="":n+=o:i?o==='"'||o==="'"?c=o:o==="]"?(i=!1,t.push(n),n=""):n+=o:o==="["?(i=!0,n&&(t.push(n),n="")):o==="."?n&&(t.push(n),n=""):n+=o,r++}return n&&t.push(n),t}function P(e,t,s){if(e==null)return s;switch(typeof t){case"string":{if(y(t))return s;const r=e[t];return r===void 0?T(t)?P(e,O(t),s):s:r}case"number":case"symbol":{typeof t=="number"&&(t=d(t));const r=e[t];return r===void 0?s:r}default:{if(Array.isArray(t))return x(e,t,s);if(Object.is(t?.valueOf(),-0)?t="-0":t=String(t),y(t))return s;const r=e[t];return r===void 0?s:r}}}function x(e,t,s){if(t.length===0)return s;let r=e;for(let n=0;n<t.length;n++){if(r==null||y(t[n]))return s;r=r[t[n]]}return r===void 0?s:r}function S(e){return e!==null&&(typeof e=="object"||typeof e=="function")}const I=/^(?:0|[1-9]\d*)$/;function R(e,t=Number.MAX_SAFE_INTEGER){switch(typeof e){case"number":return Number.isInteger(e)&&e>=0&&e<t;case"symbol":return!1;case"string":return I.test(e)}}const _=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,F=/^\w*$/;function D(e,t){return Array.isArray(e)?!1:typeof e=="number"||typeof e=="boolean"||e==null||E(e)?!0:typeof e=="string"&&(F.test(e)||!_.test(e))||t!=null&&Object.hasOwn(t,e)}const M=(e,t,s)=>{const r=e[t];(!(Object.hasOwn(e,t)&&N(r,s))||s===void 0&&!(t in e))&&(e[t]=s)};function q(e,t,s,r){if(e==null&&!S(e))return e;let n;D(t,e)?n=[t]:Array.isArray(t)?n=t:n=O(t);const c=s(P(e,n));let i=e;for(let o=0;o<n.length&&i!=null;o++){const f=d(n[o]);if(y(f))continue;let l;if(o===n.length-1)l=c;else{const h=i[f],p=r?.(h,f,e);l=p!==void 0?p:S(h)?h:R(n[o+1])?[]:{}}M(i,f,l),i=i[f]}return e}function k(e,t,s){return q(e,t,()=>s,()=>{})}function j(){let e,t;return{promise:new Promise((r,n)=>{e=r,t=n}),resolve:e,reject:t}}class B{constructor(t){this.isPlaceholder=t}skeleton=null;snapshot={};buffer=new Map;consume(t){if(!m(t))throw new TypeError("Frame must be an object");if(!this.skeleton){this.skeleton=this.replaceFrameValues(t),this.snapshot=b(this.skeleton,r=>!g(r));return}const s=Object.keys(t);if(s.some(r=>!this.isPlaceholder(r)))throw new TypeError("Patch has an invalid key");for(const r of s){if(!this.buffer.has(r))throw new TypeError("It is a unknown patch");const{resolve:n,path:c}=this.buffer.get(r),i=t[r];if(m(i)){const o=this.replaceFrameValues(i,c);k(this.snapshot,c,b(o,f=>!g(f))),n(o);continue}k(this.snapshot,c,i),n(i)}}replaceFrameValues(t,s){return Object.fromEntries(Object.entries(t).map(r=>{const[n,c]=r,i=[s,n].filter(Boolean).join(".");if(A(c)&&this.isPlaceholder(c)){const o=j();return this.buffer.set(c,{promise:o.promise,resolve:o.resolve,path:i}),[n,o.promise]}return r}))}}class C{buffer="";cursor=0;stack=[];inString=!1;escaped=!1;started=!1;frames=[];parse(t){for(this.buffer+=t;this.cursor<this.buffer.length;){const s=this.buffer[this.cursor];if(!this.started){if(/\s/.test(s)){this.cursor++;continue}if(s!=="{"&&s!=="[")throw new TypeError("Parse Error");this.started=!0,this.stack.push(s),this.cursor++;continue}if(this.inString){if(this.escaped){this.escaped=!1,this.cursor++;continue}if(s==="\\"){this.escaped=!0,this.cursor++;continue}s==='"'&&(this.inString=!1),this.cursor++;continue}if(s==='"'){this.inString=!0,this.cursor++;continue}if(s==="{"||s==="["){this.stack.push(s),this.cursor++;continue}if(s==="}"||s==="]"){const r=this.stack.pop();if(s==="}"&&r!=="{"||s==="]"&&r!=="[")throw new TypeError("Parse Error");if(this.cursor++,!this.stack.length){const n=this.buffer.slice(0,this.cursor);this.buffer=this.buffer.slice(this.cursor),this.cursor=0,this.started=!1,this.inString=!1,this.escaped=!1;const c=JSON.parse(n);this.frames.push(c);continue}continue}this.cursor++}}drain(){const t=this.frames;return this.frames=[],t}}const G={name:"fatcher-middleware-json",use:async(e,t)=>{const s=await t();if(!e.progressive||s.bodyUsed||!s.body)return s;let r=!1;const{promise:n,resolve:c}=j(),i=new B(e.progressive.isPlaceholder),o=new ReadableStream({async start(l){const h=new TextDecoder,p=new C;await a(s.clone().body,J=>{const U=h.decode(J,{stream:!0});p.parse(U);const W=p.drain();for(const $ of W)i.consume($),!r&&i.skeleton&&(r=!0,c(i.skeleton))}),l.enqueue(new TextEncoder().encode(JSON.stringify(i.snapshot))),l.close()}});await n;const f=new Response(o,s);return f.skeleton=i.skeleton,f.snapshot=i.snapshot,f}};u.json=G,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
@@ -0,0 +1,2 @@
1
+ import { FatcherMiddleware } from 'fatcher';
2
+ export declare const json: FatcherMiddleware;
@@ -0,0 +1,11 @@
1
+ export declare class Parser {
2
+ private buffer;
3
+ private cursor;
4
+ private stack;
5
+ private inString;
6
+ private escaped;
7
+ private started;
8
+ private frames;
9
+ parse(value: string): void;
10
+ drain(): any[];
11
+ }
@@ -0,0 +1,12 @@
1
+ declare module 'fatcher' {
2
+ interface FatcherOptions {
3
+ progressive?: {
4
+ isPlaceholder: (value: string) => boolean;
5
+ };
6
+ }
7
+ interface FatcherResponse {
8
+ skeleton: Record<string, any>;
9
+ snapshot: Record<string, any>;
10
+ }
11
+ }
12
+ export {};
@@ -0,0 +1,5 @@
1
+ export declare function withResolvers<T>(): {
2
+ promise: Promise<T>;
3
+ resolve: (v: T) => void;
4
+ reject: () => void;
5
+ };
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@fatcherjs/middleware-json",
3
- "homepage": "https://github.com/fanhaoyuan/fatcher",
3
+ "version": "3.0.0",
4
+ "homepage": "https://github.com/fatcherjs/middleware-json",
4
5
  "repository": {
5
6
  "type": "git",
6
- "url": "git+https://github.com/fanhaoyuan/fatcher.git"
7
+ "url": "git+https://github.com/fatcherjs/middleware-json.git"
7
8
  },
8
9
  "main": "dist/index.js",
9
10
  "module": "dist/index.esm.js",
@@ -13,19 +14,36 @@
13
14
  "dist"
14
15
  ],
15
16
  "peerDependencies": {
16
- "fatcher": "3.0.0-alpha-10"
17
+ "fatcher": "^3.0.0"
17
18
  },
18
19
  "dependencies": {
19
- "@fatcherjs/utils-shared": "3.0.0-alpha-10"
20
+ "@fatcherjs/utils-shared": "^3.0.0-alpha-11",
21
+ "es-toolkit": "^1.43.0"
20
22
  },
21
23
  "devDependencies": {
22
- "fatcher": "3.0.0-alpha-10"
24
+ "@fansy/eslint-config": "^1.4.2",
25
+ "@fansy/prettier-config": "^1.1.0",
26
+ "@types/node": "^24.3.1",
27
+ "@typescript-eslint/eslint-plugin": "^8.11.0",
28
+ "@typescript-eslint/parser": "^8.11.0",
29
+ "@vitest/coverage-v8": "3.2.4",
30
+ "eslint": "^8.57.1",
31
+ "eslint-plugin-prettier": "^5.2.1",
32
+ "fatcher": "^3.0.0",
33
+ "husky": "^9.1.7",
34
+ "lint-staged": "^16.1.6",
35
+ "msw": "^2.11.1",
36
+ "prettier": "^3.3.3",
37
+ "prettier-plugin-organize-imports": "^4.1.0",
38
+ "typescript": "^5.9.2",
39
+ "vite": "^7.1.5",
40
+ "vite-plugin-dts": "^4.5.4",
41
+ "vitest": "^3.2.4"
23
42
  },
24
- "version": "3.0.0-alpha-10",
25
43
  "scripts": {
26
- "clean": "rimraf dist",
27
- "build": "npm run clean && rollup -c rollup.config.ts",
28
- "test": "jest",
29
- "test:cov": "jest --coverage"
44
+ "build": "vite build",
45
+ "test": "vitest run",
46
+ "test:cov": "vitest run --coverage",
47
+ "eslint": "eslint ."
30
48
  }
31
49
  }