@defjs/vue 0.0.1-beta.0 → 0.0.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 +69 -39
- package/index.d.ts +23 -11
- package/index.js +26 -13
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# @defjs/vue
|
|
2
2
|
|
|
3
|
-
Thin Vue 3
|
|
3
|
+
Thin Vue 3 plugin for `@defjs/core`. It provides an existing Defjs client through Vue injection.
|
|
4
|
+
|
|
5
|
+
Keep reusable SDK definitions in their own package or module. A Vue application configures the client, provides it through this plugin, and executes imported definitions. The SDK does not need a Vue dependency or an application-owned client.
|
|
6
|
+
|
|
7
|
+
This plugin is optional: import a fixed application-owned client, pass a `Client` explicitly, use Vue injection for tree-level defaults and overrides, or choose your own organization. SDK definitions do not require this plugin. Share an instance only when its captured configuration and state are safe to share; request-specific identity or credentials belong to the appropriate request boundary.
|
|
4
8
|
|
|
5
9
|
Supports Vue 3+.
|
|
6
10
|
|
|
@@ -16,7 +20,7 @@ The package is ESM and requires `@defjs/core` as a peer in the `^0.4.0` range. I
|
|
|
16
20
|
|
|
17
21
|
`createClientPlugin(client)` creates a Vue plugin that provides the exact `Client` instance supplied by the application. `injectClient()` reads the nearest provided instance. `HTTP_CLIENT` remains public for native subtree overrides.
|
|
18
22
|
|
|
19
|
-
Client creation and configuration stay in `@defjs/core`. The
|
|
23
|
+
Client creation and configuration stay in `@defjs/core`. The plugin does not create, cache, replace, dispose, abort, or close anything on behalf of the application, and it does not add transport behavior, state management, retries, or a Nuxt module.
|
|
20
24
|
|
|
21
25
|
## Quick Start
|
|
22
26
|
|
|
@@ -24,42 +28,50 @@ Create and own the client at the application entry point:
|
|
|
24
28
|
|
|
25
29
|
```ts
|
|
26
30
|
// main.ts
|
|
27
|
-
import { createClient, withEndpoint } from
|
|
28
|
-
import { createClientPlugin } from
|
|
29
|
-
import { createApp } from
|
|
30
|
-
import App from
|
|
31
|
+
import { createClient, withEndpoint } from "@defjs/core";
|
|
32
|
+
import { createClientPlugin } from "@defjs/vue";
|
|
33
|
+
import { createApp } from "vue";
|
|
34
|
+
import App from "./App.vue";
|
|
31
35
|
|
|
32
|
-
const client = createClient(withEndpoint(
|
|
33
|
-
const app = createApp(App)
|
|
36
|
+
const client = createClient(withEndpoint("https://api.example.com"));
|
|
37
|
+
const app = createApp(App);
|
|
34
38
|
|
|
35
|
-
app.use(createClientPlugin(client))
|
|
36
|
-
app.mount(
|
|
39
|
+
app.use(createClientPlugin(client));
|
|
40
|
+
app.mount("#app");
|
|
37
41
|
```
|
|
38
42
|
|
|
39
|
-
Use the injected client inside a component or composable and own cleanup where the work starts:
|
|
43
|
+
Import `getUser` from your SDK or a local `api.ts` module that exports a `defineRequest(...)` endpoint. Use the injected client inside a component or composable and own cleanup where the work starts. Best practice: name the command, pass that name to `execute`, and return before reading the user when `error` is set. Abort still belongs to the watcher:
|
|
40
44
|
|
|
41
45
|
```vue
|
|
42
46
|
<script setup lang="ts">
|
|
43
|
-
import { ref, watch } from
|
|
44
|
-
import { injectClient } from
|
|
45
|
-
import { getUser } from
|
|
47
|
+
import { ref, watch } from "vue";
|
|
48
|
+
import { injectClient } from "@defjs/vue";
|
|
49
|
+
import { getUser } from "./api";
|
|
46
50
|
|
|
47
|
-
const props = defineProps<{ id: number }>()
|
|
48
|
-
const client = injectClient()
|
|
49
|
-
const name = ref(
|
|
51
|
+
const props = defineProps<{ id: number }>();
|
|
52
|
+
const client = injectClient();
|
|
53
|
+
const name = ref("");
|
|
50
54
|
|
|
51
55
|
watch(
|
|
52
56
|
() => props.id,
|
|
53
57
|
(id, _previousId, onCleanup) => {
|
|
54
|
-
const abort = new AbortController()
|
|
55
|
-
onCleanup(() => abort.abort())
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
const abort = new AbortController();
|
|
59
|
+
onCleanup(() => abort.abort());
|
|
60
|
+
|
|
61
|
+
const command = getUser({ id });
|
|
62
|
+
void client
|
|
63
|
+
.execute(command, { signal: abort.signal })
|
|
64
|
+
.then(([error, user]) => {
|
|
65
|
+
if (abort.signal.aborted) return;
|
|
66
|
+
if (error) {
|
|
67
|
+
name.value = error.message;
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
name.value = user.name;
|
|
71
|
+
});
|
|
60
72
|
},
|
|
61
73
|
{ immediate: true },
|
|
62
|
-
)
|
|
74
|
+
);
|
|
63
75
|
</script>
|
|
64
76
|
|
|
65
77
|
<template>
|
|
@@ -70,30 +82,38 @@ watch(
|
|
|
70
82
|
Configure interceptors in core before installing the plugin:
|
|
71
83
|
|
|
72
84
|
```ts
|
|
73
|
-
import {
|
|
74
|
-
|
|
85
|
+
import {
|
|
86
|
+
createClient,
|
|
87
|
+
createHttpInterceptor,
|
|
88
|
+
withEndpoint,
|
|
89
|
+
withInterceptors,
|
|
90
|
+
} from "@defjs/core";
|
|
91
|
+
import { createClientPlugin } from "@defjs/vue";
|
|
75
92
|
|
|
76
93
|
const auth = createHttpInterceptor((request, next) => {
|
|
77
|
-
const headers = new Headers(request.headers)
|
|
78
|
-
headers.set(
|
|
79
|
-
return next({ ...request, headers })
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
const client = createClient(
|
|
83
|
-
|
|
94
|
+
const headers = new Headers(request.headers);
|
|
95
|
+
headers.set("Authorization", `Bearer ${getAccessToken()}`);
|
|
96
|
+
return next({ ...request, headers });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
const client = createClient(
|
|
100
|
+
withEndpoint("https://api.example.com"),
|
|
101
|
+
withInterceptors(auth),
|
|
102
|
+
);
|
|
103
|
+
app.use(createClientPlugin(client));
|
|
84
104
|
```
|
|
85
105
|
|
|
86
106
|
For a subtree override, use Vue's native provider with the public key:
|
|
87
107
|
|
|
88
108
|
```ts
|
|
89
|
-
import { createClient, withEndpoint } from
|
|
90
|
-
import { HTTP_CLIENT } from
|
|
91
|
-
import { provide } from
|
|
109
|
+
import { createClient, withEndpoint } from "@defjs/core";
|
|
110
|
+
import { HTTP_CLIENT } from "@defjs/vue";
|
|
111
|
+
import { provide } from "vue";
|
|
92
112
|
|
|
93
|
-
provide(HTTP_CLIENT, createClient(withEndpoint(
|
|
113
|
+
provide(HTTP_CLIENT, createClient(withEndpoint("https://preview.example.com")));
|
|
94
114
|
```
|
|
95
115
|
|
|
96
|
-
Descendants receive that exact client; siblings outside the subtree continue to receive the app-level client.
|
|
116
|
+
Descendants receive that exact client; siblings outside the subtree continue to receive the app-level client. The providing component itself still reads from its ancestors.
|
|
97
117
|
|
|
98
118
|
## API
|
|
99
119
|
|
|
@@ -103,7 +123,17 @@ Creates a Vue plugin that provides the supplied client instance.
|
|
|
103
123
|
|
|
104
124
|
### `injectClient(): Client`
|
|
105
125
|
|
|
106
|
-
|
|
126
|
+
```ts
|
|
127
|
+
function injectClient(key?: InjectionKey<Client>): Client;
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Returns the nearest client provided for `key`; omitted or `undefined` uses `HTTP_CLIENT`. For a custom `InjectionKey<Client>`, use `app.provide(key, client)` or an ancestor's `provide(key, client)`. Both sides must share the same `Symbol` object: matching descriptions do not make keys equal. `createClientPlugin(client)` still takes only the client and provides `HTTP_CLIENT`.
|
|
131
|
+
|
|
132
|
+
Call in a valid Vue injection context, such as synchronous `setup()` or `app.runWithContext()` (Vue 3.3+). The latter is synchronous app-level context only: no component subtree overrides and no context across ordinary `await`. Compiler-restored contexts after top-level `await` in `<script setup>` remain valid.
|
|
133
|
+
|
|
134
|
+
Throws an `Error` without an active injection context or a client for the selected key; a missing custom key never falls back to `HTTP_CLIENT`. Installing the plugin alone does not establish an injection context.
|
|
135
|
+
|
|
136
|
+
An application function that only retrieves a client may use `explicitClient ?? injectClient()`: only the fallback branch needs injection context. Other lifecycle APIs in that composable retain their own context requirements. Read the client before async work and reuse the object.
|
|
107
137
|
|
|
108
138
|
### `HTTP_CLIENT: InjectionKey<Client>`
|
|
109
139
|
|
package/index.d.ts
CHANGED
|
@@ -1,36 +1,48 @@
|
|
|
1
1
|
import { InjectionKey, Plugin } from "vue";
|
|
2
2
|
import { Client } from "@defjs/core";
|
|
3
|
-
//#region src/core.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Vue injection key for the Defjs {@link Client} provided by {@link createClientPlugin}.
|
|
6
5
|
*
|
|
7
|
-
* Prefer {@link injectClient} over calling `inject(HTTP_CLIENT)` directly.
|
|
6
|
+
* Prefer {@link injectClient} over calling `inject(HTTP_CLIENT)` directly. `inject(HTTP_CLIENT)` returns `undefined` when nothing was provided. Override a subtree with `provide(HTTP_CLIENT, childClient)`.
|
|
7
|
+
*
|
|
8
|
+
* @group vue
|
|
9
|
+
* @see {@link Client}
|
|
10
|
+
* @see {@link injectClient}
|
|
8
11
|
*/
|
|
9
12
|
export declare const HTTP_CLIENT: InjectionKey<Client>;
|
|
10
13
|
/**
|
|
11
14
|
* Create a Vue plugin that provides an existing Defjs client via {@link HTTP_CLIENT}.
|
|
12
15
|
*
|
|
13
|
-
* Does not create, cache, or dispose the client — pass an instance from `createClient`.
|
|
16
|
+
* Does not create, cache, or dispose the client — pass an instance from `createClient`. Installing it calls `app.provide(HTTP_CLIENT, client)`, so components read the client with `injectClient()`.
|
|
14
17
|
*
|
|
15
18
|
* @param client - The Client instance to provide to the app.
|
|
16
19
|
* @returns A Vue `Plugin` for `app.use(...)`.
|
|
17
20
|
*
|
|
18
|
-
* @
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* ```
|
|
21
|
+
* @group vue
|
|
22
|
+
* @see {@link Client}
|
|
23
|
+
* @exampleFile examples/api/create-client-plugin.ts#example
|
|
22
24
|
*/
|
|
23
25
|
export declare function createClientPlugin(client: Client): Plugin;
|
|
24
26
|
/**
|
|
25
|
-
*
|
|
27
|
+
* Vue composable that injects the Defjs client for a key, defaulting to {@link HTTP_CLIENT}.
|
|
28
|
+
*
|
|
29
|
+
* `injectClient(key)` reads the nearest provider for that key; omitted or `undefined` uses `HTTP_CLIENT`. Call in a valid Vue injection context: synchronous `setup()`, compiler-restored `<script setup>` after top-level `await`, or a synchronous `app.runWithContext()` callback (Vue 3.3+). `app.runWithContext()` supplies only synchronous app-level context, not subtree overrides or context across ordinary `await`.
|
|
30
|
+
*
|
|
31
|
+
* For a custom `InjectionKey<Client>`, use `app.provide(key, client)` or an ancestor’s `provide(key, client)`. Share the same `Symbol` object; matching descriptions do not make keys equal. `createClientPlugin(client)` still takes only the client and provides `HTTP_CLIENT`.
|
|
32
|
+
*
|
|
33
|
+
* It returns the exact client provided for the selected key, and throws an `Error` without an active injection context or a client for the selected key. Installing the plugin alone does not establish an injection context.
|
|
26
34
|
*
|
|
35
|
+
* @param key - Injection key; use the same object with `app.provide` or `provide`.
|
|
36
|
+
* Defaults to `HTTP_CLIENT`, as provided by {@link createClientPlugin}.
|
|
27
37
|
* @returns The injected `Client` instance.
|
|
28
|
-
* @throws If no client was provided
|
|
38
|
+
* @throws If called outside a Vue injection context or no client was provided for
|
|
39
|
+
* the requested key. A missing custom key does not fall back to `HTTP_CLIENT`.
|
|
29
40
|
*
|
|
30
41
|
* @example
|
|
31
42
|
* ```ts
|
|
32
43
|
* const client = injectClient()
|
|
33
44
|
* ```
|
|
45
|
+
* @group vue
|
|
46
|
+
* @see {@link Client}
|
|
34
47
|
*/
|
|
35
|
-
export declare function injectClient(): Client;
|
|
36
|
-
//#endregion
|
|
48
|
+
export declare function injectClient(key?: InjectionKey<Client>): Client;
|
package/index.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
import { inject } from "vue";
|
|
2
|
-
|
|
2
|
+
// src/core.ts
|
|
3
3
|
/**
|
|
4
4
|
* Vue injection key for the Defjs {@link Client} provided by {@link createClientPlugin}.
|
|
5
5
|
*
|
|
6
|
-
* Prefer {@link injectClient} over calling `inject(HTTP_CLIENT)` directly.
|
|
6
|
+
* Prefer {@link injectClient} over calling `inject(HTTP_CLIENT)` directly. `inject(HTTP_CLIENT)` returns `undefined` when nothing was provided. Override a subtree with `provide(HTTP_CLIENT, childClient)`.
|
|
7
|
+
*
|
|
8
|
+
* @group vue
|
|
9
|
+
* @see {@link Client}
|
|
10
|
+
* @see {@link injectClient}
|
|
7
11
|
*/
|
|
8
12
|
const HTTP_CLIENT = Symbol("HTTP_CLIENT");
|
|
9
13
|
/**
|
|
10
14
|
* Create a Vue plugin that provides an existing Defjs client via {@link HTTP_CLIENT}.
|
|
11
15
|
*
|
|
12
|
-
* Does not create, cache, or dispose the client — pass an instance from `createClient`.
|
|
16
|
+
* Does not create, cache, or dispose the client — pass an instance from `createClient`. Installing it calls `app.provide(HTTP_CLIENT, client)`, so components read the client with `injectClient()`.
|
|
13
17
|
*
|
|
14
18
|
* @param client - The Client instance to provide to the app.
|
|
15
19
|
* @returns A Vue `Plugin` for `app.use(...)`.
|
|
16
20
|
*
|
|
17
|
-
* @
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* ```
|
|
21
|
+
* @group vue
|
|
22
|
+
* @see {@link Client}
|
|
23
|
+
* @exampleFile examples/api/create-client-plugin.ts#example
|
|
21
24
|
*/
|
|
22
25
|
function createClientPlugin(client) {
|
|
23
26
|
return { install(app) {
|
|
@@ -25,20 +28,30 @@ function createClientPlugin(client) {
|
|
|
25
28
|
} };
|
|
26
29
|
}
|
|
27
30
|
/**
|
|
28
|
-
*
|
|
31
|
+
* Vue composable that injects the Defjs client for a key, defaulting to {@link HTTP_CLIENT}.
|
|
32
|
+
*
|
|
33
|
+
* `injectClient(key)` reads the nearest provider for that key; omitted or `undefined` uses `HTTP_CLIENT`. Call in a valid Vue injection context: synchronous `setup()`, compiler-restored `<script setup>` after top-level `await`, or a synchronous `app.runWithContext()` callback (Vue 3.3+). `app.runWithContext()` supplies only synchronous app-level context, not subtree overrides or context across ordinary `await`.
|
|
34
|
+
*
|
|
35
|
+
* For a custom `InjectionKey<Client>`, use `app.provide(key, client)` or an ancestor’s `provide(key, client)`. Share the same `Symbol` object; matching descriptions do not make keys equal. `createClientPlugin(client)` still takes only the client and provides `HTTP_CLIENT`.
|
|
36
|
+
*
|
|
37
|
+
* It returns the exact client provided for the selected key, and throws an `Error` without an active injection context or a client for the selected key. Installing the plugin alone does not establish an injection context.
|
|
29
38
|
*
|
|
39
|
+
* @param key - Injection key; use the same object with `app.provide` or `provide`.
|
|
40
|
+
* Defaults to `HTTP_CLIENT`, as provided by {@link createClientPlugin}.
|
|
30
41
|
* @returns The injected `Client` instance.
|
|
31
|
-
* @throws If no client was provided
|
|
42
|
+
* @throws If called outside a Vue injection context or no client was provided for
|
|
43
|
+
* the requested key. A missing custom key does not fall back to `HTTP_CLIENT`.
|
|
32
44
|
*
|
|
33
45
|
* @example
|
|
34
46
|
* ```ts
|
|
35
47
|
* const client = injectClient()
|
|
36
48
|
* ```
|
|
49
|
+
* @group vue
|
|
50
|
+
* @see {@link Client}
|
|
37
51
|
*/
|
|
38
|
-
function injectClient() {
|
|
39
|
-
const client = inject(
|
|
40
|
-
if (!client) throw new Error("No Defjs client
|
|
52
|
+
function injectClient(key = HTTP_CLIENT) {
|
|
53
|
+
const client = inject(key);
|
|
54
|
+
if (!client) throw new Error(key === HTTP_CLIENT ? "No Defjs client available. Call injectClient() in a Vue injection context (such as synchronous setup() or app.runWithContext()) and provide a client with app.use(createClientPlugin(client)) or provide(HTTP_CLIENT, client)." : "No Defjs client available for the requested injection key. Call injectClient(key) in a Vue injection context and provide a client with app.provide(key, client) or provide(key, client). Use the same key object when providing and injecting.");
|
|
41
55
|
return client;
|
|
42
56
|
}
|
|
43
|
-
//#endregion
|
|
44
57
|
export { HTTP_CLIENT, createClientPlugin, injectClient };
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defjs/vue",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Provide an existing Defjs client to a Vue application.",
|
|
4
5
|
"bugs": {
|
|
5
6
|
"url": "https://github.com/defjs/defjs/issues"
|
|
6
7
|
},
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
"main": "./index.js",
|
|
14
15
|
"types": "./index.d.ts",
|
|
15
16
|
"peerDependencies": {
|
|
16
|
-
"@defjs/core": "0.4.0
|
|
17
|
+
"@defjs/core": "^0.4.0",
|
|
17
18
|
"vue": ">=3.0.0"
|
|
18
19
|
},
|
|
19
20
|
"module": "./index.js",
|