@bakery-framework/plugin-vue 2.0.0-rc.1 → 2.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +2 -2
- package/src/index.ts +11 -0
- package/src/server-context.ts +37 -0
- package/src/utils.ts +46 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bakery-framework/plugin-vue",
|
|
3
|
-
"version": "2.0.0-rc.
|
|
3
|
+
"version": "2.0.0-rc.3",
|
|
4
4
|
"description": "Bakery vue plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bakery",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"vue": "^3.5.38"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@bakery-framework/core": "^2.0.0-rc.
|
|
46
|
+
"@bakery-framework/core": "^2.0.0-rc.3"
|
|
47
47
|
},
|
|
48
48
|
"engines": {
|
|
49
49
|
"bun": ">=1.4.0"
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,17 @@ import { rewriteVueImports } from './utils'
|
|
|
12
12
|
*/
|
|
13
13
|
export type { CustomElementsOption, VuePluginOptions } from './types'
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* The request body inside a `<script server>` block, and anything it calls.
|
|
17
|
+
*
|
|
18
|
+
* Pairs with `getRequest()` from `@bakery-framework/core`. Both replace the
|
|
19
|
+
* `req` and `body` globals this plugin declared in `vue.d.ts`, which only
|
|
20
|
+
* reached a file when that `.d.ts` happened to land in the tsconfig project
|
|
21
|
+
* an editor resolved for it. The globals still work; they are no longer the
|
|
22
|
+
* only way, and they are no longer load-bearing.
|
|
23
|
+
*/
|
|
24
|
+
export { getBody } from './server-context'
|
|
25
|
+
|
|
15
26
|
export default function vuePlugin(options?: VuePluginOptions) {
|
|
16
27
|
if (options) setVuePluginOptions(options)
|
|
17
28
|
return definePlugin({
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The parsed body of the request a `<script server>` block is running for.
|
|
5
|
+
*
|
|
6
|
+
* Separate from core's `getRequest()` deliberately: the request is core's and
|
|
7
|
+
* every surface has one, while `body` is this plugin's own computation
|
|
8
|
+
* (`VueHandler.params(req, finalParams)`, which merges route params with the
|
|
9
|
+
* parsed payload). A `.tsx` page receives its body as a function argument and
|
|
10
|
+
* has no use for this.
|
|
11
|
+
*
|
|
12
|
+
* Why it exists at all is the same reason as `getRequest()`. The block used
|
|
13
|
+
* to reach `body` as a global declared in `vue.d.ts`, which only works if
|
|
14
|
+
* that `.d.ts` lands in whatever tsconfig project an editor resolves for the
|
|
15
|
+
* SFC, and for an SFC it routinely does not. An import cannot fail that way,
|
|
16
|
+
* it carries a real type instead of `any`, and it reaches a helper the block
|
|
17
|
+
* calls rather than stopping at the wrapper's parameter list.
|
|
18
|
+
*/
|
|
19
|
+
const bodyStore = new AsyncLocalStorage<{ body: unknown }>()
|
|
20
|
+
|
|
21
|
+
/** Run `fn` with `body` visible to `getBody()`. Called by the plugin only. */
|
|
22
|
+
export function runWithServerBody<T>(body: unknown, fn: () => T): T {
|
|
23
|
+
return bodyStore.run({ body }, fn)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The request body, inside a `<script server>` block or anything it calls.
|
|
28
|
+
*
|
|
29
|
+
* Unlike {@link import('@bakery-framework/core').getRequest} this does **not**
|
|
30
|
+
* throw when absent: a GET with no payload and no route params is an ordinary
|
|
31
|
+
* request, and `undefined` is the honest answer rather than an error
|
|
32
|
+
* condition. The type parameter is the caller's assertion about a shape the
|
|
33
|
+
* framework cannot know, exactly as it was when this was an `any` global.
|
|
34
|
+
*/
|
|
35
|
+
export function getBody<T = unknown>(): T | undefined {
|
|
36
|
+
return bodyStore.getStore()?.body as T | undefined
|
|
37
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
unlink as nodeUnlink,
|
|
5
5
|
} from 'node:fs/promises'
|
|
6
6
|
import { LRUCache } from '@bakery-framework/core/cache/lru'
|
|
7
|
-
import { Bakery } from '@bakery-framework/core/core/bakery'
|
|
7
|
+
import { Bakery, hostStore } from '@bakery-framework/core/core/bakery'
|
|
8
8
|
import { Logger } from '@bakery-framework/core/logger'
|
|
9
9
|
import { fs, is, Try } from '@bakery-framework/core/utils'
|
|
10
|
+
import { runWithServerBody } from './server-context'
|
|
11
|
+
import type { HostContext } from '@bakery-framework/core/core/bakery'
|
|
10
12
|
import type { ParsedCacheEntry, ServerResponseOptions, VueMeta } from './types'
|
|
11
13
|
|
|
12
14
|
const logger = new Logger('vue')
|
|
@@ -434,6 +436,34 @@ ${
|
|
|
434
436
|
`
|
|
435
437
|
}
|
|
436
438
|
|
|
439
|
+
/**
|
|
440
|
+
* Run `fn` in a host store that carries `req`.
|
|
441
|
+
*
|
|
442
|
+
* Preserves an outer store when there is one: it holds the host config and
|
|
443
|
+
* hostname the router resolved, and only `req` needs adding. Outside one
|
|
444
|
+
* (a test driving a route directly, a direct handler call) it builds a
|
|
445
|
+
* minimal context from the process config, which is what `Bakery.config`
|
|
446
|
+
* already falls back to.
|
|
447
|
+
*/
|
|
448
|
+
function runInRequest<T>(req: Request, fn: () => T): T {
|
|
449
|
+
const outer = hostStore.getStore()
|
|
450
|
+
if (outer) return hostStore.run({ ...outer, req }, fn)
|
|
451
|
+
|
|
452
|
+
// No outer store: carry the request and leave `config` unread.
|
|
453
|
+
//
|
|
454
|
+
// A plain `{ config: Bakery.config, ... }` throws "Config has not been
|
|
455
|
+
// initialized" the moment this runs before `initConfig()`, which is every
|
|
456
|
+
// test that drives a route directly, so the eager read reintroduced the
|
|
457
|
+
// failure it was added to fix. A getter defers it to whoever actually
|
|
458
|
+
// wants the config, and nothing in a block that only needs `req` does.
|
|
459
|
+
const ctx = { hostname: '', req } as HostContext
|
|
460
|
+
Object.defineProperty(ctx, 'config', {
|
|
461
|
+
get: () => Bakery.config,
|
|
462
|
+
enumerable: true,
|
|
463
|
+
})
|
|
464
|
+
return hostStore.run(ctx, fn)
|
|
465
|
+
}
|
|
466
|
+
|
|
437
467
|
/** Drop stale compiled versions of the same source so the cache dir stays bounded. */
|
|
438
468
|
async function pruneServerCache(dir: string, id: string, keepName: string) {
|
|
439
469
|
const entries = await Try(nodeReaddir(dir))
|
|
@@ -524,7 +554,21 @@ export async function getServerResponse(options: ServerResponseOptions) {
|
|
|
524
554
|
})
|
|
525
555
|
const execPromise = Promise.resolve(
|
|
526
556
|
is.function(exported)
|
|
527
|
-
?
|
|
557
|
+
? // The block runs inside a store carrying its own request, so
|
|
558
|
+
// `getRequest()` works whatever the caller did. Depending on an
|
|
559
|
+
// outer store looked fine because the worker always enters one,
|
|
560
|
+
// and then every test that drives a route directly got a throw,
|
|
561
|
+
// which `getServerResponse` swallows into `{}`: a guard block
|
|
562
|
+
// stopped redirecting and served 200. Entering it here is what
|
|
563
|
+
// makes the function total.
|
|
564
|
+
//
|
|
565
|
+
// An outer store is preserved rather than replaced: it carries the
|
|
566
|
+
// host config and hostname this request resolved.
|
|
567
|
+
runInRequest(req, () =>
|
|
568
|
+
runWithServerBody(body, () =>
|
|
569
|
+
exported(req, body, actionName, actionArgs),
|
|
570
|
+
),
|
|
571
|
+
)
|
|
528
572
|
: exported,
|
|
529
573
|
)
|
|
530
574
|
const params = await Promise.race([execPromise, timeoutPromise])
|