@mandujs/core 0.54.31 → 0.55.0-beta.1
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 +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +39 -20
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
|
@@ -41,11 +41,16 @@ import {
|
|
|
41
41
|
import { createHMRServer, type HMRServer } from "../dev";
|
|
42
42
|
import { PORTS } from "../../constants";
|
|
43
43
|
|
|
44
|
+
const describeCoreIsolated =
|
|
45
|
+
process.env.MANDU_SKIP_CORE_ISOLATED_TESTS === "1"
|
|
46
|
+
? describe.skip
|
|
47
|
+
: describe;
|
|
48
|
+
|
|
44
49
|
// ═══════════════════════════════════════════════════════════════════
|
|
45
50
|
// Section A — `ManduHot` runtime
|
|
46
51
|
// ═══════════════════════════════════════════════════════════════════
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
describeCoreIsolated("createManduHot — Vite-compat import.meta.hot runtime", () => {
|
|
49
54
|
beforeEach(() => {
|
|
50
55
|
_resetRegistryForTests();
|
|
51
56
|
});
|
|
@@ -172,22 +177,22 @@ describe("createManduHot — Vite-compat import.meta.hot runtime", () => {
|
|
|
172
177
|
* Utility: spin up an HMR server and return it plus the public port the
|
|
173
178
|
* client should dial. The caller owns teardown via `afterEach`.
|
|
174
179
|
*
|
|
175
|
-
* We pass `port: 0` ... almost. `createHMRServer` computes
|
|
176
|
-
* `port + PORTS.HMR_OFFSET` internally, so if we want an ephemeral
|
|
177
|
-
* listener we'd need to bind ahead of time. These tests instead use a
|
|
178
|
-
* process-local monotonic port allocator to avoid random collisions
|
|
179
|
-
* during the full parallel core suite.
|
|
180
|
-
*/
|
|
181
|
-
const HMR_TEST_PORT_STATE = "__MANDU_HMR_TEST_PORT_STATE__";
|
|
182
|
-
|
|
183
|
-
function pickPort(): number {
|
|
184
|
-
const stateGlobal = globalThis as typeof globalThis & {
|
|
185
|
-
__MANDU_HMR_TEST_PORT_STATE__?: { next: number };
|
|
186
|
-
};
|
|
187
|
-
stateGlobal.__MANDU_HMR_TEST_PORT_STATE__ ??= { next: 0 };
|
|
188
|
-
const index = stateGlobal.__MANDU_HMR_TEST_PORT_STATE__.next++;
|
|
189
|
-
return 41000 + (((process.pid % 3500) * 2 + index * 2) % 7000);
|
|
190
|
-
}
|
|
180
|
+
* We pass `port: 0` ... almost. `createHMRServer` computes
|
|
181
|
+
* `port + PORTS.HMR_OFFSET` internally, so if we want an ephemeral
|
|
182
|
+
* listener we'd need to bind ahead of time. These tests instead use a
|
|
183
|
+
* process-local monotonic port allocator to avoid random collisions
|
|
184
|
+
* during the full parallel core suite.
|
|
185
|
+
*/
|
|
186
|
+
const HMR_TEST_PORT_STATE = "__MANDU_HMR_TEST_PORT_STATE__";
|
|
187
|
+
|
|
188
|
+
function pickPort(): number {
|
|
189
|
+
const stateGlobal = globalThis as typeof globalThis & {
|
|
190
|
+
__MANDU_HMR_TEST_PORT_STATE__?: { next: number };
|
|
191
|
+
};
|
|
192
|
+
stateGlobal.__MANDU_HMR_TEST_PORT_STATE__ ??= { next: 0 };
|
|
193
|
+
const index = stateGlobal.__MANDU_HMR_TEST_PORT_STATE__.next++;
|
|
194
|
+
return 41000 + (((process.pid % 3500) * 2 + index * 2) % 7000);
|
|
195
|
+
}
|
|
191
196
|
|
|
192
197
|
/**
|
|
193
198
|
* A wrapper around WebSocket that stashes every incoming message in an
|
|
@@ -206,7 +211,7 @@ interface MessageClient {
|
|
|
206
211
|
|
|
207
212
|
async function connectWS(hmrPort: number, since?: number): Promise<MessageClient> {
|
|
208
213
|
const qs = since !== undefined ? `?since=${since}` : "";
|
|
209
|
-
const ws = new WebSocket(`ws://
|
|
214
|
+
const ws = new WebSocket(`ws://127.0.0.1:${hmrPort}${qs ? "/" + qs : ""}`);
|
|
210
215
|
const messages: unknown[] = [];
|
|
211
216
|
const waiters: Array<{ idx: number; resolve: (v: unknown) => void; reject: (e: Error) => void; timer: ReturnType<typeof setTimeout> }> = [];
|
|
212
217
|
|
|
@@ -262,14 +267,14 @@ function waitFor(ms: number): Promise<void> {
|
|
|
262
267
|
return new Promise((r) => setTimeout(r, ms));
|
|
263
268
|
}
|
|
264
269
|
|
|
265
|
-
|
|
270
|
+
describeCoreIsolated("createHMRServer — replay buffer semantics", () => {
|
|
266
271
|
let server: HMRServer | null = null;
|
|
267
272
|
let hmrPort = 0;
|
|
268
273
|
|
|
269
274
|
beforeEach(() => {
|
|
270
275
|
const basePort = pickPort();
|
|
271
276
|
hmrPort = basePort + PORTS.HMR_OFFSET;
|
|
272
|
-
server = createHMRServer(basePort);
|
|
277
|
+
server = createHMRServer(basePort, { hostname: "127.0.0.1" });
|
|
273
278
|
});
|
|
274
279
|
|
|
275
280
|
afterEach(() => {
|
|
@@ -366,14 +371,14 @@ describe("createHMRServer — replay buffer semantics", () => {
|
|
|
366
371
|
// Section C — Wire-format + layout-update + invalidate upstream
|
|
367
372
|
// ═══════════════════════════════════════════════════════════════════
|
|
368
373
|
|
|
369
|
-
|
|
374
|
+
describeCoreIsolated("createHMRServer — wire format + event dispatch", () => {
|
|
370
375
|
let server: HMRServer | null = null;
|
|
371
376
|
let hmrPort = 0;
|
|
372
377
|
|
|
373
378
|
beforeEach(() => {
|
|
374
379
|
const basePort = pickPort();
|
|
375
380
|
hmrPort = basePort + PORTS.HMR_OFFSET;
|
|
376
|
-
server = createHMRServer(basePort);
|
|
381
|
+
server = createHMRServer(basePort, { hostname: "127.0.0.1" });
|
|
377
382
|
});
|
|
378
383
|
|
|
379
384
|
afterEach(() => {
|
|
@@ -442,6 +447,39 @@ describe("createHMRServer — wire format + event dispatch", () => {
|
|
|
442
447
|
});
|
|
443
448
|
});
|
|
444
449
|
|
|
450
|
+
test("[13a] a build error reaches new browsers until a successful recovery", async () => {
|
|
451
|
+
server!.broadcast({
|
|
452
|
+
type: "error",
|
|
453
|
+
data: {
|
|
454
|
+
name: "BuildError",
|
|
455
|
+
kind: "build",
|
|
456
|
+
message: "synthetic build failure",
|
|
457
|
+
routeId: "home",
|
|
458
|
+
timestamp: Date.now(),
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
const failedClient = await connectWS(hmrPort);
|
|
463
|
+
expect(await failedClient.awaitMessage(0)).toMatchObject({ type: "connected" });
|
|
464
|
+
expect(await failedClient.awaitMessage(1)).toMatchObject({
|
|
465
|
+
type: "error",
|
|
466
|
+
data: {
|
|
467
|
+
name: "BuildError",
|
|
468
|
+
kind: "build",
|
|
469
|
+
message: "synthetic build failure",
|
|
470
|
+
routeId: "home",
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
failedClient.ws.close();
|
|
474
|
+
|
|
475
|
+
server!.broadcast({ type: "reload", data: { timestamp: Date.now() } });
|
|
476
|
+
const recoveredClient = await connectWS(hmrPort);
|
|
477
|
+
expect(await recoveredClient.awaitMessage(0)).toMatchObject({ type: "connected" });
|
|
478
|
+
await waitFor(25);
|
|
479
|
+
expect(recoveredClient.peek()).toHaveLength(1);
|
|
480
|
+
recoveredClient.ws.close();
|
|
481
|
+
});
|
|
482
|
+
|
|
445
483
|
test("[14] reconnect with since=0 after many broadcasts and no buffered survivors → full-reload", async () => {
|
|
446
484
|
// Fire enough envelopes to push id past MAX_REPLAY_BUFFER — oldestId
|
|
447
485
|
// becomes > 0, so since=0 is older than oldestId.
|
|
@@ -490,7 +528,7 @@ describe("createHMRServer — wire format + event dispatch", () => {
|
|
|
490
528
|
// Section D — Integration: runtime + envelope produced by server
|
|
491
529
|
// ═══════════════════════════════════════════════════════════════════
|
|
492
530
|
|
|
493
|
-
|
|
531
|
+
describeCoreIsolated("integration — ManduHot + broadcastVite envelope shape", () => {
|
|
494
532
|
beforeEach(() => {
|
|
495
533
|
_resetRegistryForTests();
|
|
496
534
|
});
|
|
@@ -521,7 +559,7 @@ describe("integration — ManduHot + broadcastVite envelope shape", () => {
|
|
|
521
559
|
|
|
522
560
|
test("[17] HMRReplayEnvelope id is monotonic even across size eviction (ids don't reset)", () => {
|
|
523
561
|
const envelopes: HMRReplayEnvelope[] = [];
|
|
524
|
-
const server = createHMRServer(pickPort());
|
|
562
|
+
const server = createHMRServer(pickPort(), { hostname: "127.0.0.1" });
|
|
525
563
|
try {
|
|
526
564
|
for (let i = 0; i < MAX_REPLAY_BUFFER + 3; i++) {
|
|
527
565
|
envelopes.push(server.broadcastVite({ type: "full-reload", path: `/p-${i}` }));
|
|
@@ -1,140 +1,179 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Regression guard for issues #322 / #323 — dev island hydration broke with
|
|
3
|
-
* `TypeError: jsxDEV is not a function`.
|
|
4
|
-
*
|
|
5
|
-
* #322: the shim imported `jsxDEV` from bare `"react"`, which (via the import
|
|
6
|
-
* map → `_react.js`) only worked if `_react.js` re-exported jsxDEV.
|
|
7
|
-
* #323: the "fix" changed the import to `"react/jsx-dev-runtime"`, but the
|
|
8
|
-
* import map maps `react/jsx-dev-runtime` to THIS shim — a circular
|
|
9
|
-
* self-import, so jsxDEV stayed `undefined`.
|
|
10
|
-
*
|
|
11
|
-
* The correct source: import JSX runtime functions from bare `"react"`, which
|
|
12
|
-
* the import map resolves to the self-contained `_react.js` vendor bundle
|
|
13
|
-
* (built with no `external`, so the real jsx/jsxs/jsxDEV/Fragment are inlined
|
|
14
|
-
* and re-exported). The shim must NEVER import from the import-map-aliased
|
|
15
|
-
* subpath that points back at itself.
|
|
16
|
-
*/
|
|
17
|
-
import { describe, expect, test } from "bun:test";
|
|
18
|
-
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
19
|
-
import { tmpdir } from "node:os";
|
|
20
|
-
import path from "node:path";
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
expect(source).
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
// the emitted
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// re-
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
// jsxDEV
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"export const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
await
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Regression guard for issues #322 / #323 — dev island hydration broke with
|
|
3
|
+
* `TypeError: jsxDEV is not a function`.
|
|
4
|
+
*
|
|
5
|
+
* #322: the shim imported `jsxDEV` from bare `"react"`, which (via the import
|
|
6
|
+
* map → `_react.js`) only worked if `_react.js` re-exported jsxDEV.
|
|
7
|
+
* #323: the "fix" changed the import to `"react/jsx-dev-runtime"`, but the
|
|
8
|
+
* import map maps `react/jsx-dev-runtime` to THIS shim — a circular
|
|
9
|
+
* self-import, so jsxDEV stayed `undefined`.
|
|
10
|
+
*
|
|
11
|
+
* The correct source: import JSX runtime functions from bare `"react"`, which
|
|
12
|
+
* the import map resolves to the self-contained `_react.js` vendor bundle
|
|
13
|
+
* (built with no `external`, so the real jsx/jsxs/jsxDEV/Fragment are inlined
|
|
14
|
+
* and re-exported). The shim must NEVER import from the import-map-aliased
|
|
15
|
+
* subpath that points back at itself.
|
|
16
|
+
*/
|
|
17
|
+
import { describe, expect, test } from "bun:test";
|
|
18
|
+
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
19
|
+
import { tmpdir } from "node:os";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import {
|
|
22
|
+
_testOnly_generateJsxDevRuntimeShimSource,
|
|
23
|
+
_testOnly_generateJsxRuntimeShimSource,
|
|
24
|
+
} from "../build";
|
|
25
|
+
|
|
26
|
+
const describeBundler = describe.skipIf(
|
|
27
|
+
process.env.MANDU_SKIP_BUNDLER_TESTS === "1",
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
describeBundler("JSX runtime shim sources (issues #322 / #323)", () => {
|
|
31
|
+
test("dev shim imports jsxDEV/Fragment from bare 'react', never the self-aliased subpath", () => {
|
|
32
|
+
const source = _testOnly_generateJsxDevRuntimeShimSource();
|
|
33
|
+
|
|
34
|
+
// #323: must NOT import from 'react/jsx-dev-runtime' — that is this shim's
|
|
35
|
+
// own import-map alias, which would be a circular self-import. (Targets
|
|
36
|
+
// the import statement, not comment mentions of the path.)
|
|
37
|
+
expect(source).not.toMatch(/from\s*['"]react\/jsx-dev-runtime['"]/);
|
|
38
|
+
|
|
39
|
+
// Correct: jsxDEV (+ jsx/jsxs fallback inputs) come from bare 'react'
|
|
40
|
+
// (→ _react.js). Import includes jsxDEV and resolves from "react".
|
|
41
|
+
expect(source).toMatch(/import\s*\{[^}]*\bjsxDEV\b[^}]*\}\s*from\s*['"]react['"]/);
|
|
42
|
+
|
|
43
|
+
// Shim still re-exports what the import map expects.
|
|
44
|
+
expect(source).toContain("export { jsxDEV, Fragment }");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("prod shim imports jsx/jsxs/Fragment from bare 'react', never the self-aliased subpath", () => {
|
|
48
|
+
const source = _testOnly_generateJsxRuntimeShimSource();
|
|
49
|
+
|
|
50
|
+
// #323: must NOT import from 'react/jsx-runtime' (this shim's own alias).
|
|
51
|
+
expect(source).not.toMatch(/from\s*['"]react\/jsx-runtime['"]/);
|
|
52
|
+
|
|
53
|
+
expect(source).toMatch(
|
|
54
|
+
/import\s*\{\s*jsx\s*,\s*jsxs\s*,\s*Fragment\s*\}\s*from\s*['"]react['"]/,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
expect(source).toContain("export { jsx, jsxs, Fragment }");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// #323 build-level guard: the SOURCE test alone missed the real bug because
|
|
61
|
+
// the broken 0.54.24 build still emitted `from "react/jsx-dev-runtime"` in
|
|
62
|
+
// the OUTPUT (the import-map alias pointing back at this shim). Build the
|
|
63
|
+
// shim the same way buildVendorShims does (external: ["react"]) and assert
|
|
64
|
+
// the emitted module never self-imports the alias.
|
|
65
|
+
test("built dev shim never self-imports react/jsx-dev-runtime", async () => {
|
|
66
|
+
const dir = await mkdtemp(path.join(tmpdir(), "mandu-jsx-shim-"));
|
|
67
|
+
try {
|
|
68
|
+
const srcPath = path.join(dir, "_jsx-dev-runtime.js");
|
|
69
|
+
await writeFile(srcPath, _testOnly_generateJsxDevRuntimeShimSource(), "utf-8");
|
|
70
|
+
|
|
71
|
+
const result = await Bun.build({
|
|
72
|
+
entrypoints: [srcPath],
|
|
73
|
+
external: ["react"],
|
|
74
|
+
target: "browser",
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(result.success).toBe(true);
|
|
78
|
+
const out = await result.outputs[0]!.text();
|
|
79
|
+
|
|
80
|
+
// The emitted shim must source jsxDEV from bare 'react' (→ _react.js),
|
|
81
|
+
// NOT from the import-map alias that resolves back to itself.
|
|
82
|
+
expect(out).not.toMatch(/from\s*["']react\/jsx-dev-runtime["']/);
|
|
83
|
+
expect(out).toMatch(/from\s*["']react["']/);
|
|
84
|
+
expect(out).toContain("jsxDEV");
|
|
85
|
+
} finally {
|
|
86
|
+
await rm(dir, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// #323 re-regression (0.54.30): a SOURCE/static check passed but at RUNTIME
|
|
91
|
+
// `_react.js`'s jsxDEV was `undefined` (jsxDEV is a React dev-only export;
|
|
92
|
+
// a production-built _react.js drops it while keeping jsx/jsxs). The shim
|
|
93
|
+
// re-exported that undefined → "jsxDEV is not a function". Guard the VALUE:
|
|
94
|
+
// build the dev shim against a `react` stub whose jsxDEV is undefined (the
|
|
95
|
+
// exact failing condition) and assert the shim still exposes a callable
|
|
96
|
+
// jsxDEV via its jsx/jsxs fallback.
|
|
97
|
+
test("dev shim exposes a callable jsxDEV even when react.jsxDEV is undefined (runtime value)", async () => {
|
|
98
|
+
const dir = await mkdtemp(path.join(tmpdir(), "mandu-jsx-shim-rt-"));
|
|
99
|
+
try {
|
|
100
|
+
// Stub for bare 'react' = a production-built _react.js: jsx/jsxs are real
|
|
101
|
+
// functions, jsxDEV is missing.
|
|
102
|
+
const reactStub = path.join(dir, "react-stub.js");
|
|
103
|
+
await writeFile(
|
|
104
|
+
reactStub,
|
|
105
|
+
[
|
|
106
|
+
"export const jsx = (type) => ({ type, runtime: 'jsx' });",
|
|
107
|
+
"export const jsxs = (type) => ({ type, runtime: 'jsxs' });",
|
|
108
|
+
"export const Fragment = Symbol.for('react.fragment');",
|
|
109
|
+
"export const jsxDEV = undefined;",
|
|
110
|
+
].join("\n"),
|
|
111
|
+
"utf-8",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// Generate the real shim source, but resolve its bare `react` import to
|
|
115
|
+
// the stub so we can drive the undefined-jsxDEV condition.
|
|
116
|
+
const shimSource = _testOnly_generateJsxDevRuntimeShimSource().replace(
|
|
117
|
+
/from\s*['"]react['"]/g,
|
|
118
|
+
`from ${JSON.stringify(reactStub.replace(/\\/g, "/"))}`,
|
|
119
|
+
);
|
|
120
|
+
const shimSrc = path.join(dir, "_jsx-dev-runtime.src.js");
|
|
121
|
+
await writeFile(shimSrc, shimSource, "utf-8");
|
|
122
|
+
|
|
123
|
+
const result = await Bun.build({
|
|
124
|
+
entrypoints: [shimSrc],
|
|
125
|
+
target: "browser",
|
|
126
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
127
|
+
});
|
|
128
|
+
expect(result.success).toBe(true);
|
|
129
|
+
|
|
130
|
+
const outPath = path.join(dir, "_jsx-dev-runtime.built.js");
|
|
131
|
+
await writeFile(outPath, await result.outputs[0]!.text(), "utf-8");
|
|
132
|
+
|
|
133
|
+
// Import through a relative specifier in a fresh Bun process. This
|
|
134
|
+
// exercises the public export while avoiding Bun 1.3's unreliable
|
|
135
|
+
// in-process resolution of temporary absolute file URLs on macOS.
|
|
136
|
+
const probePath = path.join(dir, "probe.js");
|
|
137
|
+
await writeFile(
|
|
138
|
+
probePath,
|
|
139
|
+
[
|
|
140
|
+
"import * as runtime from './_jsx-dev-runtime.built.js';",
|
|
141
|
+
"console.log(JSON.stringify({",
|
|
142
|
+
" type: typeof runtime.jsxDEV,",
|
|
143
|
+
" single: runtime.jsxDEV('div', {}, undefined, false).runtime,",
|
|
144
|
+
" multi: runtime.jsxDEV('div', {}, undefined, true).runtime,",
|
|
145
|
+
"}));",
|
|
146
|
+
].join("\n"),
|
|
147
|
+
"utf-8",
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const proc = Bun.spawn([process.execPath, "run", probePath], {
|
|
151
|
+
cwd: dir,
|
|
152
|
+
stdin: "ignore",
|
|
153
|
+
stdout: "pipe",
|
|
154
|
+
stderr: "pipe",
|
|
155
|
+
});
|
|
156
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
157
|
+
new Response(proc.stdout).text(),
|
|
158
|
+
new Response(proc.stderr).text(),
|
|
159
|
+
proc.exited,
|
|
160
|
+
]);
|
|
161
|
+
if (exitCode !== 0) {
|
|
162
|
+
throw new Error(`Built JSX runtime probe failed (${exitCode}): ${stderr}`);
|
|
163
|
+
}
|
|
164
|
+
const probe = JSON.parse(stdout.trim()) as {
|
|
165
|
+
type: string;
|
|
166
|
+
single: string;
|
|
167
|
+
multi: string;
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// The whole point of #323's re-regression: jsxDEV must be CALLABLE.
|
|
171
|
+
expect(probe.type).toBe("function");
|
|
172
|
+
// Fallback routes static-children to jsxs, otherwise jsx.
|
|
173
|
+
expect(probe.single).toBe("jsx");
|
|
174
|
+
expect(probe.multi).toBe("jsxs");
|
|
175
|
+
} finally {
|
|
176
|
+
await rm(dir, { recursive: true, force: true });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
});
|