acrylblends 0.6.3 → 0.7.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/README.md +4 -3
- package/docs/framework/README.md +2 -2
- package/docs/framework/UPSTREAM.md +1 -1
- package/docs/framework/guides/jsx.md +51 -1
- package/docs/framework/helpers/ssg.md +3 -6
- package/docs/framework/middleware/builtin/jsx-renderer.md +0 -3
- package/docs/framework/upstream.json +2 -2
- package/lib/accepts.d.ts +1 -1
- package/lib/acryl-base.d.ts +1 -1
- package/lib/adapter.d.ts +1 -1
- package/lib/{app-wFwpg-bt.d.ts → app-D8riatvv.d.ts} +1 -1
- package/lib/{base-ddiVmiE7.js → base-NSN_SNv-.js} +1 -0
- package/lib/basic-auth.d.ts +1 -1
- package/lib/bearer-auth.d.ts +1 -1
- package/lib/body-limit.d.ts +1 -1
- package/lib/cache.d.ts +1 -1
- package/lib/cache.js +4 -4
- package/lib/{client-BHTr318g.js → client-DDLXwhBo.js} +2 -125
- package/lib/client.d.ts +2 -2
- package/lib/client.js +2 -1
- package/lib/combine.d.ts +1 -1
- package/lib/{components-DYZdxJIJ.js → components-JXVm45kK.js} +1 -1
- package/lib/{components-Ce5FRyHU.js → components-cEW3ovK0.js} +3 -3
- package/lib/compress.d.ts +1 -1
- package/lib/conninfo.d.ts +2 -2
- package/lib/{context-DVQBjuns.d.ts → context-DS3fYyG-.d.ts} +1 -1
- package/lib/context-storage.d.ts +1 -1
- package/lib/cookie.d.ts +1 -1
- package/lib/cors.d.ts +1 -1
- package/lib/csrf.d.ts +1 -1
- package/lib/dev.d.ts +2 -2
- package/lib/etag.d.ts +1 -1
- package/lib/factory.d.ts +2 -2
- package/lib/{hooks-Bq5q9IMl.js → hooks-w72iLeFR.js} +6 -6
- package/lib/{index-CUTfU69R.d.ts → index-Bb7YJKCq.d.ts} +2 -2
- package/lib/{index-CsvB-F9j.d.ts → index-BvFh1Gfx.d.ts} +1 -1
- package/lib/{index-Cqcdluyn.d.ts → index-CTy4N7ng.d.ts} +2 -2
- package/lib/index.d.ts +4 -4
- package/lib/ip-restriction.d.ts +2 -2
- package/lib/jsx/dom/client.js +1 -1
- package/lib/jsx/dom/jsx-dev-runtime.js +1 -1
- package/lib/jsx/dom/jsx-runtime.js +1 -1
- package/lib/jsx/dom/server.js +2 -2
- package/lib/jsx/dom.js +4 -4
- package/lib/jsx/jsx-dev-runtime.js +1 -1
- package/lib/jsx/jsx-runtime.js +1 -1
- package/lib/jsx/streaming.js +1 -1
- package/lib/{jsx-dev-runtime-kAhR_4C7.js → jsx-dev-runtime-CWLRA2F3.js} +2 -2
- package/lib/jsx-renderer.d.ts +75 -0
- package/lib/jsx-renderer.js +99 -0
- package/lib/jsx.js +4 -4
- package/lib/jwk.d.ts +1 -1
- package/lib/jwt.d.ts +1 -1
- package/lib/language.d.ts +1 -1
- package/lib/logger.d.ts +1 -1
- package/lib/loopback-only.d.ts +1 -1
- package/lib/method-not-allowed.d.ts +2 -2
- package/lib/method-override.d.ts +2 -2
- package/lib/mount.d.ts +1 -1
- package/lib/node-server/conninfo.d.ts +1 -1
- package/lib/node-server/register.d.ts +1 -0
- package/lib/node-server/register.js +73 -0
- package/lib/node-server/serve-static.d.ts +1 -1
- package/lib/node-server/serve-static.js +1 -1
- package/lib/node-server.d.ts +1 -1
- package/lib/node-server.js +1 -1
- package/lib/powered-by.d.ts +1 -1
- package/lib/pretty-json.d.ts +1 -1
- package/lib/quick.d.ts +1 -1
- package/lib/request-id.d.ts +1 -1
- package/lib/request.d.ts +1 -1
- package/lib/route.d.ts +1 -1
- package/lib/schema-validator.d.ts +1 -1
- package/lib/secure-headers.d.ts +1 -1
- package/lib/{serve-static-Cw23shXv.js → serve-static-B14MF1Va.js} +1 -0
- package/lib/serve-static.d.ts +1 -1
- package/lib/ssg-BwjcA-Ge.js +389 -0
- package/lib/ssg.d.ts +156 -0
- package/lib/ssg.js +2 -0
- package/lib/streaming.d.ts +1 -1
- package/lib/testing.d.ts +3 -3
- package/lib/testing.js +1 -1
- package/lib/timeout.d.ts +1 -1
- package/lib/timing.d.ts +1 -1
- package/lib/tiny.d.ts +1 -1
- package/lib/trailing-slash.d.ts +1 -1
- package/lib/{types-nEL8VPDC.d.ts → types-Dv_nV2cv.d.ts} +1 -1
- package/lib/{types-CQiGmsWW.d.ts → types-zNxyEpG2.d.ts} +2 -2
- package/lib/types.d.ts +1 -1
- package/lib/utils/body.d.ts +1 -1
- package/lib/utils/ipaddr.d.ts +1 -1
- package/lib/utils-Cx61GFMC.js +125 -0
- package/lib/validator.d.ts +1 -1
- package/lib/ws.d.ts +1 -1
- package/package.json +13 -1
- package/plugins/acryl-inspect/upstream.json +1 -1
- package/plugins/acryl-node-server/src/register.ts +86 -0
- package/plugins/acryl-node-server/upstream.json +5 -3
- package/plugins/acryl-router/src/core/helper/ssg/index.ts +14 -0
- package/plugins/acryl-router/src/core/helper/ssg/middleware.ts +83 -0
- package/plugins/acryl-router/src/core/helper/ssg/plugins.ts +72 -0
- package/plugins/acryl-router/src/core/helper/ssg/ssg.ts +471 -0
- package/plugins/acryl-router/src/core/helper/ssg/utils.ts +118 -0
- package/plugins/acryl-router/src/core/jsx/dom/render.ts +6 -6
- package/plugins/acryl-router/src/core/middleware/cache/index.ts +4 -4
- package/plugins/acryl-router/src/core/middleware/jsx-renderer/index.ts +162 -0
- package/plugins/acryl-router/src/helpers/index.ts +1 -0
- package/plugins/acryl-router/src/middleware/index.ts +1 -0
- package/plugins/acryl-router/upstream.json +2 -2
- package/plugins/acryl-router-http/upstream.json +1 -1
- package/plugins/acryl-state/upstream.json +1 -1
- package/plugins/desktop/main.ts +4 -1
- package/plugins/desktop/upstream.json +1 -1
- package/plugins/desktop-web/main.ts +3 -1
- package/plugins/desktop-web/upstream.json +1 -1
- package/plugins/extensions/resolve.ts +4 -0
- package/shared/AGENTS.core.md +3 -0
- package/templates/agent/package.json +7 -3
- package/templates/agent/tsconfig.json +10 -2
- package/templates/blank/package.json +7 -3
- package/templates/blank/src/about.tsx +14 -0
- package/templates/blank/src/routes.ts +2 -0
- package/templates/blank/test/app.test.ts +15 -0
- package/templates/blank/tsconfig.json +3 -1
- package/templates/todo/package.json +7 -3
- package/templates/todo/tsconfig.json +3 -1
- package/templates/todo/upstream.json +1 -1
- package/plugins/acryl-inspect/tests/report.spec.ts +0 -69
- package/plugins/acryl-inspect/tests/service.spec.ts +0 -121
- package/plugins/acryl-node-server/tests/adapter/app.ts +0 -76
- package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +0 -5
- package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
- package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +0 -1
- package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +0 -1
- package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +0 -34
- package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +0 -248
- package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +0 -457
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +0 -23
- package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +0 -27
- package/plugins/acryl-node-server/tests/adapter/headers.test.ts +0 -252
- package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +0 -190
- package/plugins/acryl-node-server/tests/adapter/listener.test.ts +0 -727
- package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +0 -189
- package/plugins/acryl-node-server/tests/adapter/request.test.ts +0 -1230
- package/plugins/acryl-node-server/tests/adapter/response.test.ts +0 -285
- package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +0 -849
- package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +0 -790
- package/plugins/acryl-node-server/tests/adapter/server.test.ts +0 -1312
- package/plugins/acryl-node-server/tests/adapter/setup.ts +0 -16
- package/plugins/acryl-node-server/tests/adapter/url.test.ts +0 -76
- package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +0 -23
- package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +0 -75
- package/plugins/acryl-node-server/tests/adapter/utils.test.ts +0 -235
- package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +0 -260
- package/plugins/acryl-node-server/tests/server.spec.ts +0 -141
- package/plugins/acryl-router/src/core/app.test.ts +0 -3833
- package/plugins/acryl-router/src/core/client/client.test.ts +0 -2313
- package/plugins/acryl-router/src/core/client/types.test.ts +0 -149
- package/plugins/acryl-router/src/core/client/utils.test.ts +0 -405
- package/plugins/acryl-router/src/core/compose.test.ts +0 -751
- package/plugins/acryl-router/src/core/context.test.ts +0 -629
- package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +0 -344
- package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +0 -55
- package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +0 -510
- package/plugins/acryl-router/src/core/helper/css/common.case.test.tsx +0 -506
- package/plugins/acryl-router/src/core/helper/css/index.test.tsx +0 -678
- package/plugins/acryl-router/src/core/helper/dev/index.test.ts +0 -181
- package/plugins/acryl-router/src/core/helper/factory/index.test.ts +0 -434
- package/plugins/acryl-router/src/core/helper/html/index.test.ts +0 -78
- package/plugins/acryl-router/src/core/helper/proxy/index.test.ts +0 -356
- package/plugins/acryl-router/src/core/helper/route/index.test.ts +0 -259
- package/plugins/acryl-router/src/core/helper/streaming/sse.test.tsx +0 -557
- package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +0 -128
- package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +0 -34
- package/plugins/acryl-router/src/core/helper/testing/index.test.ts +0 -42
- package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +0 -124
- package/plugins/acryl-router/src/core/http-exception.test.ts +0 -45
- package/plugins/acryl-router/src/core/jsx/base.test.tsx +0 -97
- package/plugins/acryl-router/src/core/jsx/children.test.ts +0 -52
- package/plugins/acryl-router/src/core/jsx/components.test.tsx +0 -936
- package/plugins/acryl-router/src/core/jsx/context-isolation.test.ts +0 -655
- package/plugins/acryl-router/src/core/jsx/dom/client.test.tsx +0 -128
- package/plugins/acryl-router/src/core/jsx/dom/components.test.tsx +0 -330
- package/plugins/acryl-router/src/core/jsx/dom/context.test.tsx +0 -184
- package/plugins/acryl-router/src/core/jsx/dom/css.test.tsx +0 -149
- package/plugins/acryl-router/src/core/jsx/dom/hooks/index.test.tsx +0 -172
- package/plugins/acryl-router/src/core/jsx/dom/index.test.tsx +0 -3681
- package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.test.tsx +0 -1344
- package/plugins/acryl-router/src/core/jsx/dom/server.test.tsx +0 -199
- package/plugins/acryl-router/src/core/jsx/hooks/dom.test.tsx +0 -859
- package/plugins/acryl-router/src/core/jsx/hooks/string.test.tsx +0 -40
- package/plugins/acryl-router/src/core/jsx/index.test.tsx +0 -1744
- package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.test.tsx +0 -666
- package/plugins/acryl-router/src/core/jsx/jsx-runtime.test.tsx +0 -85
- package/plugins/acryl-router/src/core/jsx/streaming.test.tsx +0 -1281
- package/plugins/acryl-router/src/core/jsx/utils.test.ts +0 -366
- package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +0 -434
- package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +0 -1018
- package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +0 -273
- package/plugins/acryl-router/src/core/middleware/cache/index.test.ts +0 -1211
- package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +0 -355
- package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +0 -535
- package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +0 -43
- package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +0 -532
- package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +0 -534
- package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +0 -21
- package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +0 -503
- package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +0 -217
- package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +0 -1135
- package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +0 -48
- package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +0 -802
- package/plugins/acryl-router/src/core/middleware/language/index.test.ts +0 -552
- package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +0 -204
- package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +0 -292
- package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +0 -221
- package/plugins/acryl-router/src/core/middleware/mount/index.test.ts +0 -334
- package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +0 -37
- package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +0 -218
- package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +0 -168
- package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +0 -671
- package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +0 -450
- package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +0 -50
- package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +0 -70
- package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +0 -310
- package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +0 -381
- package/plugins/acryl-router/src/core/preset/quick.test.ts +0 -42
- package/plugins/acryl-router/src/core/preset/tiny.test.ts +0 -42
- package/plugins/acryl-router/src/core/request.test.ts +0 -751
- package/plugins/acryl-router/src/core/router/common.case.test.ts +0 -1132
- package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +0 -80
- package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +0 -46
- package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +0 -154
- package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +0 -265
- package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +0 -13
- package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +0 -896
- package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +0 -8
- package/plugins/acryl-router/src/core/types.test.ts +0 -4134
- package/plugins/acryl-router/src/core/utils/accept.test.ts +0 -299
- package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +0 -56
- package/plugins/acryl-router/src/core/utils/body.test.ts +0 -496
- package/plugins/acryl-router/src/core/utils/buffer.test.ts +0 -147
- package/plugins/acryl-router/src/core/utils/color.test.ts +0 -23
- package/plugins/acryl-router/src/core/utils/concurrent.test.ts +0 -69
- package/plugins/acryl-router/src/core/utils/cookie.test.ts +0 -460
- package/plugins/acryl-router/src/core/utils/crypto.test.ts +0 -43
- package/plugins/acryl-router/src/core/utils/encode.test.ts +0 -61
- package/plugins/acryl-router/src/core/utils/filepath.test.ts +0 -78
- package/plugins/acryl-router/src/core/utils/html.test.ts +0 -16
- package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +0 -149
- package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +0 -86
- package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +0 -1788
- package/plugins/acryl-router/src/core/utils/mime.test.ts +0 -64
- package/plugins/acryl-router/src/core/utils/stream.test.ts +0 -212
- package/plugins/acryl-router/src/core/utils/types.test.ts +0 -295
- package/plugins/acryl-router/src/core/utils/url.test.ts +0 -384
- package/plugins/acryl-router/src/core/validator/utils.test.ts +0 -82
- package/plugins/acryl-router/src/core/validator/validator.test.ts +0 -1488
- package/plugins/acryl-router/tests/docs-examples.spec.ts +0 -152
- package/plugins/acryl-router/tests/jsx.spec.tsx +0 -63
- package/plugins/acryl-router/tests/live-install.spec.ts +0 -64
- package/plugins/acryl-router/tests/loader.spec.ts +0 -72
- package/plugins/acryl-router/tests/loopback-only.spec.ts +0 -80
- package/plugins/acryl-router/tests/no-dom/consumer.ts +0 -11
- package/plugins/acryl-router/tests/no-dom/tsconfig.json +0 -13
- package/plugins/acryl-router/tests/service.spec.ts +0 -426
- package/plugins/acryl-router/tests/shadow-state.spec.ts +0 -79
- package/plugins/acryl-router-http/tests/adapter.spec.ts +0 -174
- package/plugins/acryl-router-http/tests/stream.spec.ts +0 -133
- package/plugins/acryl-router-http/tests/surfaces.spec.ts +0 -101
- package/plugins/acryl-state/src/react.ts +0 -11
- package/plugins/acryl-state/tests/cell.spec.ts +0 -71
- package/plugins/acryl-state/tests/react.spec.ts +0 -39
- package/plugins/acryl-state/tests/service.spec.ts +0 -132
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
-
|
|
3
|
-
const importIndex = () => import('./index')
|
|
4
|
-
const importStreaming = () => import('./streaming')
|
|
5
|
-
const importHooks = () => import('./hooks')
|
|
6
|
-
const importAsyncHooks = () => import('node:async_hooks')
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* These tests exercise the non-`AsyncLocalStorage` fallback path. They force
|
|
10
|
-
* every `AsyncLocalStorage` acquisition route to fail (and keep it disabled
|
|
11
|
-
* while the module lazily probes for it), then verify the React-style
|
|
12
|
-
* fail-safe: reading context after `await` returns the default value and never
|
|
13
|
-
* another concurrent request's value.
|
|
14
|
-
*/
|
|
15
|
-
describe('JSX context isolation without AsyncLocalStorage', () => {
|
|
16
|
-
let originalGetBuiltinModule: unknown
|
|
17
|
-
let originalMainModule: unknown
|
|
18
|
-
let warnSpy: ReturnType<typeof vi.spyOn>
|
|
19
|
-
|
|
20
|
-
beforeEach(() => {
|
|
21
|
-
// Disable every route `loadAsyncLocalStorage()` tries. These stay in effect
|
|
22
|
-
// through the first render so the lazy probe caches "unavailable".
|
|
23
|
-
vi.resetModules()
|
|
24
|
-
originalGetBuiltinModule = (process as { getBuiltinModule?: unknown }).getBuiltinModule
|
|
25
|
-
;(process as { getBuiltinModule?: unknown }).getBuiltinModule = undefined
|
|
26
|
-
originalMainModule = (process as { mainModule?: unknown }).mainModule
|
|
27
|
-
;(process as { mainModule?: unknown }).mainModule = undefined
|
|
28
|
-
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
afterEach(() => {
|
|
32
|
-
;(process as { getBuiltinModule?: unknown }).getBuiltinModule = originalGetBuiltinModule
|
|
33
|
-
;(process as { mainModule?: unknown }).mainModule = originalMainModule
|
|
34
|
-
vi.unstubAllGlobals()
|
|
35
|
-
vi.resetModules()
|
|
36
|
-
warnSpy.mockRestore()
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('falls back to the default value after await and never leaks across concurrent renders', async () => {
|
|
40
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
41
|
-
|
|
42
|
-
const SessionContext = createContext<{ username: string; role: string }>({
|
|
43
|
-
username: 'nobody',
|
|
44
|
-
role: 'none',
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const waits = new Map<string, Promise<void>>()
|
|
48
|
-
const entered = new Map<string, () => void>()
|
|
49
|
-
|
|
50
|
-
const AdminDashboard = async () => {
|
|
51
|
-
const initialSession = useContext(SessionContext)
|
|
52
|
-
entered.get(initialSession.username)?.()
|
|
53
|
-
await waits.get(initialSession.username)
|
|
54
|
-
const session = useContext(SessionContext)
|
|
55
|
-
if (session.role !== 'admin') {
|
|
56
|
-
return jsx('div', {}, `Access Denied for ${session.username}`)
|
|
57
|
-
}
|
|
58
|
-
return jsx('div', {}, `Welcome Admin ${session.username}. Secret Data: 42`)
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const render = async (username: string, role: string): Promise<string> =>
|
|
62
|
-
`${await jsx(
|
|
63
|
-
SessionContext.Provider,
|
|
64
|
-
{ value: { username, role } },
|
|
65
|
-
jsx(AdminDashboard, {})
|
|
66
|
-
).toString()}`
|
|
67
|
-
|
|
68
|
-
let resolveAdmin!: () => void
|
|
69
|
-
let resolveAttacker!: () => void
|
|
70
|
-
waits.set(
|
|
71
|
-
'admin',
|
|
72
|
-
new Promise<void>((resolve) => {
|
|
73
|
-
resolveAdmin = resolve
|
|
74
|
-
})
|
|
75
|
-
)
|
|
76
|
-
waits.set(
|
|
77
|
-
'attacker',
|
|
78
|
-
new Promise<void>((resolve) => {
|
|
79
|
-
resolveAttacker = resolve
|
|
80
|
-
})
|
|
81
|
-
)
|
|
82
|
-
const adminEntered = new Promise<void>((resolve) => {
|
|
83
|
-
entered.set('admin', resolve)
|
|
84
|
-
})
|
|
85
|
-
const attackerEntered = new Promise<void>((resolve) => {
|
|
86
|
-
entered.set('attacker', resolve)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
const adminReq = render('admin', 'admin')
|
|
90
|
-
const attackerReq = render('attacker', 'guest')
|
|
91
|
-
|
|
92
|
-
await Promise.all([adminEntered, attackerEntered])
|
|
93
|
-
resolveAttacker()
|
|
94
|
-
await Promise.resolve()
|
|
95
|
-
resolveAdmin()
|
|
96
|
-
|
|
97
|
-
const [adminHtml, attackerHtml] = await Promise.all([adminReq, attackerReq])
|
|
98
|
-
|
|
99
|
-
// Without AsyncLocalStorage the post-await read sees the default value —
|
|
100
|
-
// the same fail-safe React's request storage degrades to. The critical
|
|
101
|
-
// guarantee is that neither render observes the other request's value.
|
|
102
|
-
expect(adminHtml).toBe('<div>Access Denied for nobody</div>')
|
|
103
|
-
expect(attackerHtml).toBe('<div>Access Denied for nobody</div>')
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it('warns once when a post-await read falls back to the default value', async () => {
|
|
107
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
108
|
-
|
|
109
|
-
const ThemeContext = createContext('default')
|
|
110
|
-
const Reader = async () => {
|
|
111
|
-
await new Promise((r) => setTimeout(r, 10))
|
|
112
|
-
return jsx('div', {}, useContext(ThemeContext))
|
|
113
|
-
}
|
|
114
|
-
const render = async () =>
|
|
115
|
-
`${await jsx(ThemeContext.Provider, { value: 'provided' }, jsx(Reader, {})).toString()}`
|
|
116
|
-
|
|
117
|
-
expect(await render()).toBe('<div>default</div>')
|
|
118
|
-
expect(await render()).toBe('<div>default</div>')
|
|
119
|
-
|
|
120
|
-
// Degrading to the default value is reported once, not per read.
|
|
121
|
-
expect(warnSpy).toHaveBeenCalledTimes(1)
|
|
122
|
-
expect(warnSpy.mock.calls[0][0]).toContain('AsyncLocalStorage')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('clears the fallback warning window after an async render rejects', async () => {
|
|
126
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
127
|
-
|
|
128
|
-
const ThemeContext = createContext('default')
|
|
129
|
-
const Broken = async () => {
|
|
130
|
-
await Promise.resolve()
|
|
131
|
-
throw new Error('boom')
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
await expect(jsx(Broken, {}).toString()).rejects.toThrow('boom')
|
|
135
|
-
|
|
136
|
-
useContext(ThemeContext)
|
|
137
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('finds AsyncLocalStorage via process.mainModule.require (Node < 20.16 CJS)', async () => {
|
|
141
|
-
// Re-enable only the legacy route: a CJS entrypoint exposes the main
|
|
142
|
-
// module's `require` even where `process.getBuiltinModule` doesn't exist.
|
|
143
|
-
// Context must then survive `await` with no warning.
|
|
144
|
-
const { AsyncLocalStorage } = await importAsyncHooks()
|
|
145
|
-
;(process as { mainModule?: unknown }).mainModule = {
|
|
146
|
-
require: (specifier: string) =>
|
|
147
|
-
specifier === 'node:async_hooks' ? { AsyncLocalStorage } : {},
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
151
|
-
|
|
152
|
-
const ThemeContext = createContext('default')
|
|
153
|
-
const Reader = async () => {
|
|
154
|
-
await new Promise((r) => setTimeout(r, 10))
|
|
155
|
-
return jsx('div', {}, useContext(ThemeContext))
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
expect(
|
|
159
|
-
`${await jsx(ThemeContext.Provider, { value: 'provided' }, jsx(Reader, {})).toString()}`
|
|
160
|
-
).toBe('<div>provided</div>')
|
|
161
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('keeps a synchronous render synchronous and does not warn', async () => {
|
|
165
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
166
|
-
|
|
167
|
-
const ThemeContext = createContext('light')
|
|
168
|
-
const Consumer = () => jsx('span', {}, useContext(ThemeContext))
|
|
169
|
-
|
|
170
|
-
const result = jsx(ThemeContext.Provider, { value: 'dark' }, jsx(Consumer, {})).toString()
|
|
171
|
-
|
|
172
|
-
// A purely synchronous render must not be forced async by the serializer.
|
|
173
|
-
expect(result).toBe('<span>dark</span>')
|
|
174
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
const drainStream = async (stream: unknown): Promise<string> => {
|
|
178
|
-
const textDecoder = new TextDecoder()
|
|
179
|
-
let html = ''
|
|
180
|
-
for await (const chunk of stream as AsyncIterable<Uint8Array>) {
|
|
181
|
-
html += textDecoder.decode(chunk)
|
|
182
|
-
}
|
|
183
|
-
return html
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
it('restores provider values for the subtree returned by a resumed async component', async () => {
|
|
187
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
188
|
-
|
|
189
|
-
const ThemeContext = createContext('default')
|
|
190
|
-
const Inner = () => jsx('span', {}, useContext(ThemeContext))
|
|
191
|
-
const Outer = async () => {
|
|
192
|
-
await new Promise((r) => setTimeout(r, 10))
|
|
193
|
-
// Unlike a useContext() call here in the async body (which degrades to
|
|
194
|
-
// the default), the returned subtree renders with the context values
|
|
195
|
-
// snapshotted at suspension and restored by the renderer.
|
|
196
|
-
return jsx(Inner, {})
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
expect(
|
|
200
|
-
`${await jsx(ThemeContext.Provider, { value: 'provided' }, jsx(Outer, {})).toString()}`
|
|
201
|
-
).toBe('<span>provided</span>')
|
|
202
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
203
|
-
})
|
|
204
|
-
|
|
205
|
-
it('resumes a Suspense-deferred subtree in the captured request store', async () => {
|
|
206
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
207
|
-
const { Suspense, renderToReadableStream } = await importStreaming()
|
|
208
|
-
const { use } = await importHooks()
|
|
209
|
-
|
|
210
|
-
// A `use()`-suspended child takes the deferred re-render path, which on the
|
|
211
|
-
// fallback path re-establishes the captured render store synchronously.
|
|
212
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
213
|
-
const ThemeContext = createContext('default')
|
|
214
|
-
const Inner = () => jsx('span', {}, useContext(ThemeContext))
|
|
215
|
-
const Content = () => {
|
|
216
|
-
const value = use(deferred)
|
|
217
|
-
return jsx(ThemeContext.Provider, { value }, jsx(Inner, {}))
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const html = await drainStream(
|
|
221
|
-
renderToReadableStream(jsx(Suspense, { fallback: jsx('p', {}, 'Loading') }, jsx(Content, {})))
|
|
222
|
-
)
|
|
223
|
-
|
|
224
|
-
expect(html).toContain('Loading')
|
|
225
|
-
expect(html).toContain('<span>done</span>')
|
|
226
|
-
// Providers inside the resumed subtree render in the request store, not in
|
|
227
|
-
// the module-global fallback — so no degradation warning fires.
|
|
228
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
229
|
-
})
|
|
230
|
-
|
|
231
|
-
it('resumes a Suspense-deferred subtree with outer provider values', async () => {
|
|
232
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
233
|
-
const { Suspense, renderToReadableStream } = await importStreaming()
|
|
234
|
-
const { use } = await importHooks()
|
|
235
|
-
|
|
236
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
237
|
-
const ThemeContext = createContext('default')
|
|
238
|
-
const Content = () => {
|
|
239
|
-
use(deferred)
|
|
240
|
-
return jsx('span', {}, useContext(ThemeContext))
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const html = await drainStream(
|
|
244
|
-
renderToReadableStream(
|
|
245
|
-
jsx(
|
|
246
|
-
ThemeContext.Provider,
|
|
247
|
-
{ value: 'outer' },
|
|
248
|
-
jsx(Suspense, { fallback: jsx('p', {}, 'Loading') }, jsx(Content, {}))
|
|
249
|
-
)
|
|
250
|
-
)
|
|
251
|
-
)
|
|
252
|
-
|
|
253
|
-
expect(html).toContain('Loading')
|
|
254
|
-
expect(html).toContain('<span>outer</span>')
|
|
255
|
-
expect(html).not.toContain('<span>default</span>')
|
|
256
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
it('renders Suspense fallback with outer provider values', async () => {
|
|
260
|
-
const { createContext, useContext, jsx } = await importIndex()
|
|
261
|
-
const { Suspense, renderToReadableStream } = await importStreaming()
|
|
262
|
-
const { use } = await importHooks()
|
|
263
|
-
|
|
264
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
265
|
-
const ThemeContext = createContext('default')
|
|
266
|
-
const Fallback = () => jsx('p', {}, `Loading ${useContext(ThemeContext)}`)
|
|
267
|
-
const Content = () => {
|
|
268
|
-
const value = use(deferred)
|
|
269
|
-
return jsx('span', {}, value)
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const html = await drainStream(
|
|
273
|
-
renderToReadableStream(
|
|
274
|
-
jsx(
|
|
275
|
-
ThemeContext.Provider,
|
|
276
|
-
{ value: 'outer' },
|
|
277
|
-
jsx(Suspense, { fallback: jsx(Fallback, {}) }, jsx(Content, {}))
|
|
278
|
-
)
|
|
279
|
-
)
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
expect(html).toContain('<p>Loading outer</p>')
|
|
283
|
-
expect(html).toContain('<span>done</span>')
|
|
284
|
-
expect(html).not.toContain('default')
|
|
285
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('restores nested provider values in a resumed Suspense subtree', async () => {
|
|
289
|
-
const { createContext, useContext, jsx, Fragment } = await importIndex()
|
|
290
|
-
const { Suspense, renderToReadableStream } = await importStreaming()
|
|
291
|
-
const { use } = await importHooks()
|
|
292
|
-
|
|
293
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
294
|
-
const ThemeContext = createContext('default')
|
|
295
|
-
const Inner = () => jsx('span', {}, useContext(ThemeContext))
|
|
296
|
-
const OuterSibling = () => jsx('em', {}, useContext(ThemeContext))
|
|
297
|
-
const Content = () => {
|
|
298
|
-
use(deferred)
|
|
299
|
-
return jsx(
|
|
300
|
-
Fragment,
|
|
301
|
-
{},
|
|
302
|
-
jsx(ThemeContext.Provider, { value: 'inner' }, jsx(Inner, {})),
|
|
303
|
-
jsx(OuterSibling, {})
|
|
304
|
-
)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const html = await drainStream(
|
|
308
|
-
renderToReadableStream(
|
|
309
|
-
jsx(
|
|
310
|
-
ThemeContext.Provider,
|
|
311
|
-
{ value: 'outer' },
|
|
312
|
-
jsx(Suspense, { fallback: jsx('p', {}, 'Loading') }, jsx(Content, {}))
|
|
313
|
-
)
|
|
314
|
-
)
|
|
315
|
-
)
|
|
316
|
-
|
|
317
|
-
expect(html).toContain('<span>inner</span><em>outer</em>')
|
|
318
|
-
expect(html).not.toContain('default')
|
|
319
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
it('keeps sibling Suspense resume contexts isolated when they resolve out of order', async () => {
|
|
323
|
-
const { createContext, useContext, jsx, Fragment } = await importIndex()
|
|
324
|
-
const { Suspense, renderToReadableStream } = await importStreaming()
|
|
325
|
-
const { use } = await importHooks()
|
|
326
|
-
|
|
327
|
-
const ThemeContext = createContext('default')
|
|
328
|
-
const waits = new Map<string, Promise<void>>()
|
|
329
|
-
const entered = new Map<string, () => void>()
|
|
330
|
-
let resolveFirst!: () => void
|
|
331
|
-
let resolveSecond!: () => void
|
|
332
|
-
waits.set(
|
|
333
|
-
'first',
|
|
334
|
-
new Promise<void>((resolve) => {
|
|
335
|
-
resolveFirst = resolve
|
|
336
|
-
})
|
|
337
|
-
)
|
|
338
|
-
waits.set(
|
|
339
|
-
'second',
|
|
340
|
-
new Promise<void>((resolve) => {
|
|
341
|
-
resolveSecond = resolve
|
|
342
|
-
})
|
|
343
|
-
)
|
|
344
|
-
const firstEntered = new Promise<void>((resolve) => {
|
|
345
|
-
entered.set('first', resolve)
|
|
346
|
-
})
|
|
347
|
-
const secondEntered = new Promise<void>((resolve) => {
|
|
348
|
-
entered.set('second', resolve)
|
|
349
|
-
})
|
|
350
|
-
const Content = ({ name }: { name: string }) => {
|
|
351
|
-
entered.get(name)?.()
|
|
352
|
-
use(waits.get(name)!)
|
|
353
|
-
return jsx('span', {}, `${name}:${useContext(ThemeContext)}`)
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const htmlPromise = drainStream(
|
|
357
|
-
renderToReadableStream(
|
|
358
|
-
jsx(
|
|
359
|
-
Fragment,
|
|
360
|
-
{},
|
|
361
|
-
jsx(
|
|
362
|
-
ThemeContext.Provider,
|
|
363
|
-
{ value: 'one' },
|
|
364
|
-
jsx(
|
|
365
|
-
Suspense,
|
|
366
|
-
{ fallback: jsx('p', {}, 'Loading one') },
|
|
367
|
-
jsx(Content, { name: 'first' })
|
|
368
|
-
)
|
|
369
|
-
),
|
|
370
|
-
jsx(
|
|
371
|
-
ThemeContext.Provider,
|
|
372
|
-
{ value: 'two' },
|
|
373
|
-
jsx(
|
|
374
|
-
Suspense,
|
|
375
|
-
{ fallback: jsx('p', {}, 'Loading two') },
|
|
376
|
-
jsx(Content, { name: 'second' })
|
|
377
|
-
)
|
|
378
|
-
)
|
|
379
|
-
)
|
|
380
|
-
)
|
|
381
|
-
)
|
|
382
|
-
|
|
383
|
-
await Promise.all([firstEntered, secondEntered])
|
|
384
|
-
resolveSecond()
|
|
385
|
-
await Promise.resolve()
|
|
386
|
-
resolveFirst()
|
|
387
|
-
|
|
388
|
-
const html = await htmlPromise
|
|
389
|
-
|
|
390
|
-
expect(html).toContain('<span>first:one</span>')
|
|
391
|
-
expect(html).toContain('<span>second:two</span>')
|
|
392
|
-
expect(html).not.toContain('default')
|
|
393
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
it('resumes ErrorBoundary-deferred children in the captured request store', async () => {
|
|
397
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
398
|
-
const { renderToReadableStream } = await importStreaming()
|
|
399
|
-
|
|
400
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
401
|
-
let resolved: string | undefined
|
|
402
|
-
deferred.then((value) => {
|
|
403
|
-
resolved = value
|
|
404
|
-
})
|
|
405
|
-
const ThemeContext = createContext('default')
|
|
406
|
-
const Inner = () => jsx('span', {}, useContext(ThemeContext))
|
|
407
|
-
const Content = () => {
|
|
408
|
-
if (!resolved) {
|
|
409
|
-
// Suspend like use() does so ErrorBoundary defers the re-render.
|
|
410
|
-
throw deferred
|
|
411
|
-
}
|
|
412
|
-
return jsx(ThemeContext.Provider, { value: resolved }, jsx(Inner, {}))
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
const html = await drainStream(
|
|
416
|
-
renderToReadableStream(
|
|
417
|
-
jsx(ErrorBoundary, { fallback: jsx('p', {}, 'Failed') }, jsx(Content, {}))
|
|
418
|
-
)
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
expect(html).toContain('<span>done</span>')
|
|
422
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
423
|
-
})
|
|
424
|
-
|
|
425
|
-
it('resumes ErrorBoundary-deferred children with outer provider values', async () => {
|
|
426
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
427
|
-
const { renderToReadableStream } = await importStreaming()
|
|
428
|
-
|
|
429
|
-
const deferred = new Promise<string>((resolve) => setTimeout(() => resolve('done'), 10))
|
|
430
|
-
let resolved: string | undefined
|
|
431
|
-
deferred.then((value) => {
|
|
432
|
-
resolved = value
|
|
433
|
-
})
|
|
434
|
-
const ThemeContext = createContext('default')
|
|
435
|
-
const Content = () => {
|
|
436
|
-
if (!resolved) {
|
|
437
|
-
// Suspend like use() does so ErrorBoundary defers the re-render.
|
|
438
|
-
throw deferred
|
|
439
|
-
}
|
|
440
|
-
return jsx('span', {}, useContext(ThemeContext))
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
const html = await drainStream(
|
|
444
|
-
renderToReadableStream(
|
|
445
|
-
jsx(
|
|
446
|
-
ThemeContext.Provider,
|
|
447
|
-
{ value: 'outer' },
|
|
448
|
-
jsx(ErrorBoundary, { fallback: jsx('p', {}, 'Failed') }, jsx(Content, {}))
|
|
449
|
-
)
|
|
450
|
-
)
|
|
451
|
-
)
|
|
452
|
-
|
|
453
|
-
expect(html).toContain('<span>outer</span>')
|
|
454
|
-
expect(html).not.toContain('<span>default</span>')
|
|
455
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
456
|
-
})
|
|
457
|
-
|
|
458
|
-
it('resumes ErrorBoundary fallback rendering with outer provider values', async () => {
|
|
459
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
460
|
-
const { renderToReadableStream } = await importStreaming()
|
|
461
|
-
|
|
462
|
-
const deferred = new Promise((_resolve, reject) =>
|
|
463
|
-
setTimeout(() => reject(new Error('boom')), 10)
|
|
464
|
-
)
|
|
465
|
-
const ThemeContext = createContext('default')
|
|
466
|
-
const Content = () => {
|
|
467
|
-
throw deferred
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const html = await drainStream(
|
|
471
|
-
renderToReadableStream(
|
|
472
|
-
jsx(
|
|
473
|
-
ThemeContext.Provider,
|
|
474
|
-
{ value: 'outer' },
|
|
475
|
-
jsx(
|
|
476
|
-
ErrorBoundary,
|
|
477
|
-
{
|
|
478
|
-
fallbackRender: (error: Error) =>
|
|
479
|
-
jsx('span', {}, `fallback:${useContext(ThemeContext)}:${error.message}`),
|
|
480
|
-
},
|
|
481
|
-
jsx(Content, {})
|
|
482
|
-
)
|
|
483
|
-
)
|
|
484
|
-
)
|
|
485
|
-
)
|
|
486
|
-
|
|
487
|
-
expect(html).toContain('<span>fallback:outer:boom</span>')
|
|
488
|
-
expect(html).not.toContain('fallback:default')
|
|
489
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
490
|
-
})
|
|
491
|
-
|
|
492
|
-
it('resumes ErrorBoundary fallbackRender components with outer provider values', async () => {
|
|
493
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
494
|
-
const { renderToReadableStream } = await importStreaming()
|
|
495
|
-
|
|
496
|
-
const deferred = new Promise((_resolve, reject) =>
|
|
497
|
-
setTimeout(() => reject(new Error('boom')), 10)
|
|
498
|
-
)
|
|
499
|
-
const ThemeContext = createContext('default')
|
|
500
|
-
const Content = () => {
|
|
501
|
-
throw deferred
|
|
502
|
-
}
|
|
503
|
-
const Fallback = () => jsx('span', {}, `fallback:${useContext(ThemeContext)}`)
|
|
504
|
-
|
|
505
|
-
const html = await drainStream(
|
|
506
|
-
renderToReadableStream(
|
|
507
|
-
jsx(
|
|
508
|
-
ThemeContext.Provider,
|
|
509
|
-
{ value: 'outer' },
|
|
510
|
-
jsx(
|
|
511
|
-
ErrorBoundary,
|
|
512
|
-
{
|
|
513
|
-
fallbackRender: () => jsx(Fallback, {}),
|
|
514
|
-
},
|
|
515
|
-
jsx(Content, {})
|
|
516
|
-
)
|
|
517
|
-
)
|
|
518
|
-
)
|
|
519
|
-
)
|
|
520
|
-
|
|
521
|
-
expect(html).toContain('<span>fallback:outer</span>')
|
|
522
|
-
expect(html).not.toContain('fallback:default')
|
|
523
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
524
|
-
})
|
|
525
|
-
|
|
526
|
-
it('resumes ErrorBoundary fallback prop rendering with outer provider values', async () => {
|
|
527
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
528
|
-
const { renderToReadableStream } = await importStreaming()
|
|
529
|
-
|
|
530
|
-
const deferred = new Promise((_resolve, reject) =>
|
|
531
|
-
setTimeout(() => reject(new Error('boom')), 10)
|
|
532
|
-
)
|
|
533
|
-
const ThemeContext = createContext('default')
|
|
534
|
-
const Content = () => {
|
|
535
|
-
throw deferred
|
|
536
|
-
}
|
|
537
|
-
const Fallback = () => jsx('span', {}, `fallback:${useContext(ThemeContext)}`)
|
|
538
|
-
|
|
539
|
-
const html = await drainStream(
|
|
540
|
-
renderToReadableStream(
|
|
541
|
-
jsx(
|
|
542
|
-
ThemeContext.Provider,
|
|
543
|
-
{ value: 'outer' },
|
|
544
|
-
jsx(ErrorBoundary, { fallback: jsx(Fallback, {}) }, jsx(Content, {}))
|
|
545
|
-
)
|
|
546
|
-
)
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
expect(html).toContain('<span>fallback:outer</span>')
|
|
550
|
-
expect(html).not.toContain('fallback:default')
|
|
551
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
552
|
-
})
|
|
553
|
-
|
|
554
|
-
it('resumes thenable ErrorBoundary fallback prop rendering with outer provider values', async () => {
|
|
555
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
556
|
-
const { renderToReadableStream } = await importStreaming()
|
|
557
|
-
|
|
558
|
-
const deferred = new Promise((_resolve, reject) =>
|
|
559
|
-
setTimeout(() => reject(new Error('boom')), 10)
|
|
560
|
-
)
|
|
561
|
-
const ThemeContext = createContext('default')
|
|
562
|
-
const Content = () => {
|
|
563
|
-
throw deferred
|
|
564
|
-
}
|
|
565
|
-
const Fallback = () => jsx('span', {}, `fallback:${useContext(ThemeContext)}`)
|
|
566
|
-
const fallback = {
|
|
567
|
-
then(resolve: (value: unknown) => void) {
|
|
568
|
-
setTimeout(() => resolve(jsx(Fallback, {})), 0)
|
|
569
|
-
},
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
const html = await drainStream(
|
|
573
|
-
renderToReadableStream(
|
|
574
|
-
jsx(
|
|
575
|
-
ThemeContext.Provider,
|
|
576
|
-
{ value: 'outer' },
|
|
577
|
-
jsx(ErrorBoundary, { fallback }, jsx(Content, {}))
|
|
578
|
-
)
|
|
579
|
-
)
|
|
580
|
-
)
|
|
581
|
-
|
|
582
|
-
expect(html).toContain('<span>fallback:outer</span>')
|
|
583
|
-
expect(html).not.toContain('fallback:default')
|
|
584
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
it('resumes synchronous ErrorBoundary fallback rendering with outer provider values', async () => {
|
|
588
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
589
|
-
|
|
590
|
-
const ThemeContext = createContext('default')
|
|
591
|
-
const Content = () => {
|
|
592
|
-
throw new Error('boom')
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
expect(
|
|
596
|
-
`${await jsx(
|
|
597
|
-
ThemeContext.Provider,
|
|
598
|
-
{ value: 'outer' },
|
|
599
|
-
jsx(
|
|
600
|
-
ErrorBoundary,
|
|
601
|
-
{
|
|
602
|
-
fallbackRender: (error: Error) =>
|
|
603
|
-
jsx('span', {}, `fallback:${useContext(ThemeContext)}:${error.message}`),
|
|
604
|
-
},
|
|
605
|
-
jsx(Content, {})
|
|
606
|
-
)
|
|
607
|
-
).toString()}`
|
|
608
|
-
).toBe('<span>fallback:outer:boom</span>')
|
|
609
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
610
|
-
})
|
|
611
|
-
|
|
612
|
-
it('resumes synchronous ErrorBoundary fallbackRender components with outer provider values', async () => {
|
|
613
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
614
|
-
|
|
615
|
-
const ThemeContext = createContext('default')
|
|
616
|
-
const Content = () => {
|
|
617
|
-
throw new Error('boom')
|
|
618
|
-
}
|
|
619
|
-
const Fallback = () => jsx('span', {}, `fallback:${useContext(ThemeContext)}`)
|
|
620
|
-
|
|
621
|
-
expect(
|
|
622
|
-
`${await jsx(
|
|
623
|
-
ThemeContext.Provider,
|
|
624
|
-
{ value: 'outer' },
|
|
625
|
-
jsx(
|
|
626
|
-
ErrorBoundary,
|
|
627
|
-
{
|
|
628
|
-
fallbackRender: () => jsx(Fallback, {}),
|
|
629
|
-
},
|
|
630
|
-
jsx(Content, {})
|
|
631
|
-
)
|
|
632
|
-
).toString()}`
|
|
633
|
-
).toBe('<span>fallback:outer</span>')
|
|
634
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
635
|
-
})
|
|
636
|
-
|
|
637
|
-
it('resumes synchronous ErrorBoundary fallback prop rendering with outer provider values', async () => {
|
|
638
|
-
const { createContext, useContext, jsx, ErrorBoundary } = await importIndex()
|
|
639
|
-
|
|
640
|
-
const ThemeContext = createContext('default')
|
|
641
|
-
const Content = () => {
|
|
642
|
-
throw new Error('boom')
|
|
643
|
-
}
|
|
644
|
-
const Fallback = () => jsx('span', {}, `fallback:${useContext(ThemeContext)}`)
|
|
645
|
-
|
|
646
|
-
expect(
|
|
647
|
-
`${await jsx(
|
|
648
|
-
ThemeContext.Provider,
|
|
649
|
-
{ value: 'outer' },
|
|
650
|
-
jsx(ErrorBoundary, { fallback: jsx(Fallback, {}) }, jsx(Content, {}))
|
|
651
|
-
).toString()}`
|
|
652
|
-
).toBe('<span>fallback:outer</span>')
|
|
653
|
-
expect(warnSpy).not.toHaveBeenCalled()
|
|
654
|
-
})
|
|
655
|
-
})
|