@nuxtjp/local-runtime 0.1.3 → 0.1.5
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.ja.md +4 -1
- package/README.md +7 -2
- package/dist/module.json +2 -2
- package/dist/runtime/app/composables/useLocalRuntime.d.ts +1 -2
- package/dist/runtime/app/composables/useLocalRuntime.js +5 -46
- package/dist/runtime/app/connection.d.ts +94 -0
- package/dist/runtime/app/connection.js +57 -0
- package/dist/runtime/client/client.d.ts +2 -0
- package/dist/runtime/client/client.js +30 -4
- package/package.json +14 -28
- package/security/LICENSE-simple-git +20 -0
- package/security/README.md +2 -0
- package/security/dependency-security.check.cjs +11 -0
- package/security/patches/simple-git@4.0.2.patch +11 -0
- package/security/project.mjs +1 -1
package/README.ja.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
`0.1.1`はnpmで公開済みです。Node `^22.19.0`または`^24.11.0`、Nuxt `^4.5.1`、Vue `^3.5.40`を使用してください。
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npm install @nuxtjp/local-runtime
|
|
12
|
+
npm install @nuxtjp/local-runtime
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```ts
|
|
@@ -69,3 +69,6 @@ module/client API、loopback endpointの規則、明示接続の動作、ライ
|
|
|
69
69
|
[使い方](https://github.com/nuxtjp/nuxt-local-runtime/blob/main/docs/getting-started.md) · [Schema](schemas) · [実装](https://github.com/nuxtjp/nuxt-local-runtime/tree/main/src) · [テスト](https://github.com/nuxtjp/nuxt-local-runtime/tree/main/test) · [貢献方法](https://github.com/nuxtjp/nuxt-local-runtime/blob/main/CONTRIBUTING.md) · [セキュリティ報告](https://github.com/nuxtjp/nuxt-local-runtime/blob/main/SECURITY.md)
|
|
70
70
|
|
|
71
71
|
コードは[Apache-2.0](LICENSE)です。[NOTICE](NOTICE)と[以前の許諾](LICENSE-PREVIOUS)を保持してください。
|
|
72
|
+
|
|
73
|
+
Dependency updates: [repository policy](docs/dependency-updates.md).
|
|
74
|
+
Registry releases: [version tags and GitHub OIDC](docs/registry-release.md).
|
package/README.md
CHANGED
|
@@ -6,10 +6,10 @@ Connect a browser interface to one explicitly configured local Rust runtime, aft
|
|
|
6
6
|
|
|
7
7
|
## Install and use
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Install the package version `0.1.4` after it is available on npm. Use Node `^22.19.0` or `^24.11.0`, Nuxt `^4.5.1` and Vue `^3.5.40`.
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npm install @nuxtjp/local-runtime
|
|
12
|
+
npm install @nuxtjp/local-runtime
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
```ts
|
|
@@ -73,3 +73,8 @@ Code: [Apache-2.0](LICENSE). Retain [NOTICE](NOTICE) and the prior grants in [LI
|
|
|
73
73
|
## Consumer dependency security
|
|
74
74
|
|
|
75
75
|
See [dependency security backports](security/README.md) before installing this package in a Nuxt application. pnpm consumers must explicitly apply the included backports and verify their locked dependency tree; ordinary npm installation does not apply them.
|
|
76
|
+
|
|
77
|
+
[OSS responsibility and source completion](docs/oss-source-contract.md) defines the supported package boundary and required acceptance. It does not declare unexecuted source, distribution or production checks complete.
|
|
78
|
+
|
|
79
|
+
Dependency updates: [repository policy](docs/dependency-updates.md).
|
|
80
|
+
Registry releases: [version tags and GitHub OIDC](docs/registry-release.md).
|
package/dist/module.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ViewEnvelope } from '../../shared/types.js';
|
|
2
1
|
export declare function useLocalRuntime(): {
|
|
3
2
|
state: Readonly<import("vue").Ref<{
|
|
4
3
|
readonly phase: import("../../client/index.js").ConnectionPhase;
|
|
@@ -87,6 +86,6 @@ export declare function useLocalRuntime(): {
|
|
|
87
86
|
}>>;
|
|
88
87
|
connected: import("vue").ComputedRef<boolean>;
|
|
89
88
|
connect: (capabilities?: string[]) => Promise<void>;
|
|
90
|
-
view: (capabilityId: string, viewId: string) => Promise<ViewEnvelope>;
|
|
89
|
+
view: (capabilityId: string, viewId: string) => Promise<import("../../client/index.js").ViewEnvelope>;
|
|
91
90
|
disconnect: () => Promise<void>;
|
|
92
91
|
};
|
|
@@ -1,51 +1,10 @@
|
|
|
1
|
-
import { computed, readonly, ref } from "vue";
|
|
2
1
|
import { useRuntimeConfig } from "#imports";
|
|
3
2
|
import { LocalRuntimeClient } from "../../client/client.js";
|
|
3
|
+
import { createLocalRuntimeConnection } from "../connection.js";
|
|
4
4
|
export function useLocalRuntime() {
|
|
5
5
|
const config = useRuntimeConfig();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
11
|
-
let client = null;
|
|
12
|
-
async function connect(capabilities = []) {
|
|
13
|
-
state.value = { phase: "connecting", manifest: null, error: null };
|
|
14
|
-
try {
|
|
15
|
-
const endpoint = config.public.nuxtJpLocalRuntime.endpoint;
|
|
16
|
-
client = new LocalRuntimeClient({
|
|
17
|
-
endpoint,
|
|
18
|
-
origin: window.location.origin
|
|
19
|
-
});
|
|
20
|
-
const manifest = await client.manifest();
|
|
21
|
-
await client.connect(capabilities);
|
|
22
|
-
state.value = { phase: "connected", manifest, error: null };
|
|
23
|
-
} catch (error) {
|
|
24
|
-
client = null;
|
|
25
|
-
state.value = {
|
|
26
|
-
phase: error instanceof DOMException && error.name === "NotAllowedError" ? "denied" : "unavailable",
|
|
27
|
-
manifest: null,
|
|
28
|
-
error: error instanceof Error ? error.message : "\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F"
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function view(capabilityId, viewId) {
|
|
33
|
-
if (!client) throw new Error("local runtime is not connected");
|
|
34
|
-
return client.view(capabilityId, viewId);
|
|
35
|
-
}
|
|
36
|
-
async function disconnect() {
|
|
37
|
-
try {
|
|
38
|
-
await client?.disconnect();
|
|
39
|
-
} finally {
|
|
40
|
-
client = null;
|
|
41
|
-
state.value = { phase: "idle", manifest: null, error: null };
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return {
|
|
45
|
-
state: readonly(state),
|
|
46
|
-
connected: computed(() => state.value.phase === "connected"),
|
|
47
|
-
connect,
|
|
48
|
-
view,
|
|
49
|
-
disconnect
|
|
50
|
-
};
|
|
6
|
+
return createLocalRuntimeConnection(() => new LocalRuntimeClient({
|
|
7
|
+
endpoint: config.public.nuxtJpLocalRuntime.endpoint,
|
|
8
|
+
origin: window.location.origin
|
|
9
|
+
}));
|
|
51
10
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { LocalRuntimeClient } from '../client/client.js';
|
|
2
|
+
import type { ViewEnvelope } from '../shared/types.js';
|
|
3
|
+
/** Own each connection attempt separately; only the latest operation publishes state. */
|
|
4
|
+
export declare function createLocalRuntimeConnection(createClient: () => LocalRuntimeClient): {
|
|
5
|
+
state: Readonly<import("vue").Ref<{
|
|
6
|
+
readonly phase: import("../client/index.js").ConnectionPhase;
|
|
7
|
+
readonly manifest: {
|
|
8
|
+
readonly schema: "nuxtjp://local-runtime/manifest/v1";
|
|
9
|
+
readonly runtime_id: string;
|
|
10
|
+
readonly protocol_version: "1";
|
|
11
|
+
readonly mode: "local-simulation" | "local-production";
|
|
12
|
+
readonly external_actions: false;
|
|
13
|
+
readonly engine: {
|
|
14
|
+
readonly kind: "rust-v8";
|
|
15
|
+
readonly status: "disabled" | "simulated" | "ready";
|
|
16
|
+
readonly isolation: "in-process-simulation" | "process";
|
|
17
|
+
readonly arbitrary_network: false;
|
|
18
|
+
readonly arbitrary_filesystem: false;
|
|
19
|
+
};
|
|
20
|
+
readonly security: {
|
|
21
|
+
readonly default_band: "sealed";
|
|
22
|
+
readonly external_egress: "deny";
|
|
23
|
+
readonly allowed_origins: readonly string[];
|
|
24
|
+
readonly limits: {
|
|
25
|
+
readonly max_request_bytes: number;
|
|
26
|
+
readonly max_response_bytes: number;
|
|
27
|
+
readonly max_messages_per_minute: number;
|
|
28
|
+
readonly max_bytes_per_minute: number;
|
|
29
|
+
readonly session_ttl_seconds: number;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
readonly capabilities: readonly {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly service_id: string;
|
|
35
|
+
readonly description: string;
|
|
36
|
+
readonly output_band: "session" | "public";
|
|
37
|
+
readonly payload_schema: {
|
|
38
|
+
readonly id: string;
|
|
39
|
+
readonly fields: readonly {
|
|
40
|
+
readonly name: string;
|
|
41
|
+
readonly kind: import("../client/index.js").PayloadValueKind;
|
|
42
|
+
}[];
|
|
43
|
+
};
|
|
44
|
+
}[];
|
|
45
|
+
} | null;
|
|
46
|
+
readonly error: string | null;
|
|
47
|
+
}, {
|
|
48
|
+
readonly phase: import("../client/index.js").ConnectionPhase;
|
|
49
|
+
readonly manifest: {
|
|
50
|
+
readonly schema: "nuxtjp://local-runtime/manifest/v1";
|
|
51
|
+
readonly runtime_id: string;
|
|
52
|
+
readonly protocol_version: "1";
|
|
53
|
+
readonly mode: "local-simulation" | "local-production";
|
|
54
|
+
readonly external_actions: false;
|
|
55
|
+
readonly engine: {
|
|
56
|
+
readonly kind: "rust-v8";
|
|
57
|
+
readonly status: "disabled" | "simulated" | "ready";
|
|
58
|
+
readonly isolation: "in-process-simulation" | "process";
|
|
59
|
+
readonly arbitrary_network: false;
|
|
60
|
+
readonly arbitrary_filesystem: false;
|
|
61
|
+
};
|
|
62
|
+
readonly security: {
|
|
63
|
+
readonly default_band: "sealed";
|
|
64
|
+
readonly external_egress: "deny";
|
|
65
|
+
readonly allowed_origins: readonly string[];
|
|
66
|
+
readonly limits: {
|
|
67
|
+
readonly max_request_bytes: number;
|
|
68
|
+
readonly max_response_bytes: number;
|
|
69
|
+
readonly max_messages_per_minute: number;
|
|
70
|
+
readonly max_bytes_per_minute: number;
|
|
71
|
+
readonly session_ttl_seconds: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly capabilities: readonly {
|
|
75
|
+
readonly id: string;
|
|
76
|
+
readonly service_id: string;
|
|
77
|
+
readonly description: string;
|
|
78
|
+
readonly output_band: "session" | "public";
|
|
79
|
+
readonly payload_schema: {
|
|
80
|
+
readonly id: string;
|
|
81
|
+
readonly fields: readonly {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly kind: import("../client/index.js").PayloadValueKind;
|
|
84
|
+
}[];
|
|
85
|
+
};
|
|
86
|
+
}[];
|
|
87
|
+
} | null;
|
|
88
|
+
readonly error: string | null;
|
|
89
|
+
}>>;
|
|
90
|
+
connected: import("vue").ComputedRef<boolean>;
|
|
91
|
+
connect: (capabilities?: string[]) => Promise<void>;
|
|
92
|
+
view: (capabilityId: string, viewId: string) => Promise<ViewEnvelope>;
|
|
93
|
+
disconnect: () => Promise<void>;
|
|
94
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { computed, readonly, ref } from "vue";
|
|
2
|
+
export function createLocalRuntimeConnection(createClient) {
|
|
3
|
+
const state = ref({ phase: "idle", manifest: null, error: null });
|
|
4
|
+
let generation = 0;
|
|
5
|
+
let client = null;
|
|
6
|
+
let pending = null;
|
|
7
|
+
async function connect(capabilities = []) {
|
|
8
|
+
const operation = ++generation;
|
|
9
|
+
const previous = pending || client;
|
|
10
|
+
client = null;
|
|
11
|
+
pending = null;
|
|
12
|
+
state.value = { phase: "connecting", manifest: null, error: null };
|
|
13
|
+
let attempt = null;
|
|
14
|
+
try {
|
|
15
|
+
attempt = createClient();
|
|
16
|
+
pending = attempt;
|
|
17
|
+
await previous?.disconnect();
|
|
18
|
+
if (operation !== generation) return;
|
|
19
|
+
const manifest = await attempt.manifest();
|
|
20
|
+
await attempt.connect(capabilities);
|
|
21
|
+
if (operation !== generation) {
|
|
22
|
+
await attempt.disconnect();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
client = attempt;
|
|
26
|
+
pending = null;
|
|
27
|
+
state.value = { phase: "connected", manifest, error: null };
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (operation !== generation) return;
|
|
30
|
+
try {
|
|
31
|
+
await attempt?.disconnect();
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
if (operation !== generation) return;
|
|
35
|
+
pending = null;
|
|
36
|
+
client = null;
|
|
37
|
+
state.value = {
|
|
38
|
+
phase: error instanceof DOMException && error.name === "NotAllowedError" ? "denied" : "unavailable",
|
|
39
|
+
manifest: null,
|
|
40
|
+
error: error instanceof Error ? error.message : "\u63A5\u7D9A\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F"
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function view(capabilityId, viewId) {
|
|
45
|
+
if (!client || state.value.phase !== "connected") throw new Error("local runtime is not connected");
|
|
46
|
+
return client.view(capabilityId, viewId);
|
|
47
|
+
}
|
|
48
|
+
async function disconnect() {
|
|
49
|
+
++generation;
|
|
50
|
+
const previous = pending || client;
|
|
51
|
+
pending = null;
|
|
52
|
+
client = null;
|
|
53
|
+
state.value = { phase: "idle", manifest: null, error: null };
|
|
54
|
+
await previous?.disconnect();
|
|
55
|
+
}
|
|
56
|
+
return { state: readonly(state), connected: computed(() => state.value.phase === "connected"), connect, view, disconnect };
|
|
57
|
+
}
|
|
@@ -12,6 +12,7 @@ export declare class LocalRuntimeClient {
|
|
|
12
12
|
private readonly fetcher;
|
|
13
13
|
private readonly maximumResponseBytes;
|
|
14
14
|
private readonly timeoutMilliseconds;
|
|
15
|
+
private operation;
|
|
15
16
|
private grant;
|
|
16
17
|
private runtimeManifest;
|
|
17
18
|
constructor(options: LocalRuntimeClientOptions);
|
|
@@ -19,4 +20,5 @@ export declare class LocalRuntimeClient {
|
|
|
19
20
|
connect(capabilities: string[]): Promise<SessionGrant>;
|
|
20
21
|
view(capabilityId: string, viewId: string): Promise<ViewEnvelope>;
|
|
21
22
|
disconnect(): Promise<void>;
|
|
23
|
+
private revoke;
|
|
22
24
|
}
|
|
@@ -12,6 +12,7 @@ export class LocalRuntimeClient {
|
|
|
12
12
|
fetcher;
|
|
13
13
|
maximumResponseBytes;
|
|
14
14
|
timeoutMilliseconds;
|
|
15
|
+
operation = 0;
|
|
15
16
|
grant = null;
|
|
16
17
|
runtimeManifest = null;
|
|
17
18
|
constructor(options) {
|
|
@@ -28,6 +29,7 @@ export class LocalRuntimeClient {
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
async manifest() {
|
|
32
|
+
const operation = this.operation;
|
|
31
33
|
const value = await localJson(
|
|
32
34
|
this.fetcher,
|
|
33
35
|
runtimeUrl(this.endpoint, "/v1/manifest"),
|
|
@@ -38,6 +40,7 @@ export class LocalRuntimeClient {
|
|
|
38
40
|
if (!isLocalRuntimeManifest(value) || !value.security.allowed_origins.includes(this.origin)) {
|
|
39
41
|
throw new Error("invalid runtime manifest");
|
|
40
42
|
}
|
|
43
|
+
if (operation !== this.operation) throw new Error("local runtime operation was superseded");
|
|
41
44
|
this.runtimeManifest = value;
|
|
42
45
|
return value;
|
|
43
46
|
}
|
|
@@ -52,6 +55,12 @@ export class LocalRuntimeClient {
|
|
|
52
55
|
if (capabilities.some((capability) => !available.has(capability))) {
|
|
53
56
|
throw new Error("requested capability is not declared");
|
|
54
57
|
}
|
|
58
|
+
const runtimeManifest = this.runtimeManifest;
|
|
59
|
+
const operation = ++this.operation;
|
|
60
|
+
const previous = this.grant;
|
|
61
|
+
this.grant = null;
|
|
62
|
+
if (previous) await this.revoke(previous.token);
|
|
63
|
+
if (operation !== this.operation) throw new Error("local runtime operation was superseded");
|
|
55
64
|
const request = {
|
|
56
65
|
schema: "nuxtjp://local-runtime/session-request/v1",
|
|
57
66
|
origin: this.origin,
|
|
@@ -69,10 +78,18 @@ export class LocalRuntimeClient {
|
|
|
69
78
|
const expected = [...new Set(capabilities)].sort();
|
|
70
79
|
const observed = isSessionGrant(value) ? [...value.granted_capabilities].sort() : [];
|
|
71
80
|
const now = Math.floor(Date.now() / 1e3);
|
|
72
|
-
const limits =
|
|
81
|
+
const limits = runtimeManifest.security.limits;
|
|
73
82
|
if (!isSessionGrant(value) || value.audience !== this.origin || value.expires_at_unix_seconds <= now || value.expires_at_unix_seconds > now + limits.session_ttl_seconds + 5 || JSON.stringify(observed) !== JSON.stringify(expected) || value.budget.max_messages > limits.max_messages_per_minute || value.budget.max_bytes > limits.max_bytes_per_minute) {
|
|
74
83
|
throw new Error("invalid runtime session grant");
|
|
75
84
|
}
|
|
85
|
+
if (operation !== this.operation) {
|
|
86
|
+
try {
|
|
87
|
+
await this.revoke(value.token);
|
|
88
|
+
} catch {
|
|
89
|
+
throw new Error("superseded runtime session cleanup failed");
|
|
90
|
+
}
|
|
91
|
+
throw new Error("local runtime operation was superseded");
|
|
92
|
+
}
|
|
76
93
|
this.grant = value;
|
|
77
94
|
return value;
|
|
78
95
|
}
|
|
@@ -83,6 +100,9 @@ export class LocalRuntimeClient {
|
|
|
83
100
|
if (!this.grant.granted_capabilities.includes(capabilityId)) {
|
|
84
101
|
throw new Error("capability was not granted");
|
|
85
102
|
}
|
|
103
|
+
const operation = this.operation;
|
|
104
|
+
const grant = this.grant;
|
|
105
|
+
const runtimeManifest = this.runtimeManifest;
|
|
86
106
|
const request = {
|
|
87
107
|
schema: "nuxtjp://local-runtime/view-request/v1",
|
|
88
108
|
capability_id: capabilityId,
|
|
@@ -92,20 +112,26 @@ export class LocalRuntimeClient {
|
|
|
92
112
|
const value = await localJson(
|
|
93
113
|
this.fetcher,
|
|
94
114
|
runtimeUrl(this.endpoint, "/v1/view"),
|
|
95
|
-
localRequest("POST", { body: request, token:
|
|
115
|
+
localRequest("POST", { body: request, token: grant.token }),
|
|
96
116
|
this.maximumResponseBytes,
|
|
97
117
|
this.timeoutMilliseconds
|
|
98
118
|
);
|
|
99
|
-
|
|
119
|
+
if (operation !== this.operation || this.grant !== grant) {
|
|
120
|
+
throw new Error("local runtime operation was superseded");
|
|
121
|
+
}
|
|
122
|
+
const capability = runtimeManifest.capabilities.find((item) => item.id === capabilityId);
|
|
100
123
|
if (!capability || !isViewEnvelope(value, capability.payload_schema) || value.capability_id !== capabilityId || value.view_id !== viewId) {
|
|
101
124
|
throw new Error("invalid runtime view");
|
|
102
125
|
}
|
|
103
126
|
return value;
|
|
104
127
|
}
|
|
105
128
|
async disconnect() {
|
|
129
|
+
++this.operation;
|
|
106
130
|
const token = this.grant?.token;
|
|
107
131
|
this.grant = null;
|
|
108
|
-
if (
|
|
132
|
+
if (token) await this.revoke(token);
|
|
133
|
+
}
|
|
134
|
+
async revoke(token) {
|
|
109
135
|
await localJson(
|
|
110
136
|
this.fetcher,
|
|
111
137
|
runtimeUrl(this.endpoint, "/v1/session"),
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuxtjp/local-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "Fail-closed Nuxt client for a loopback local runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
-
"packageManager": "pnpm@10.29.3",
|
|
8
7
|
"engines": {
|
|
9
8
|
"node": "^22.19.0 || ^24.11.0",
|
|
10
9
|
"pnpm": ">=10 <11"
|
|
@@ -41,31 +40,23 @@
|
|
|
41
40
|
},
|
|
42
41
|
"./schema": "./schemas/local-runtime-v1.schema.json"
|
|
43
42
|
},
|
|
44
|
-
"scripts": {
|
|
45
|
-
"build": "nuxt-module-build build && nuxt build playground",
|
|
46
|
-
"typecheck": "vue-tsc --noEmit --project tsconfig.json",
|
|
47
|
-
"test": "vitest run && pnpm run test:dependency-security && pnpm run test:consumer-security",
|
|
48
|
-
"prepack": "pnpm run test && pnpm run typecheck && pnpm run build",
|
|
49
|
-
"test:dependency-security": "node --test scripts/dependency-security.check.cjs",
|
|
50
|
-
"test:consumer-security": "node --test scripts/consumer-security.check.mjs"
|
|
51
|
-
},
|
|
52
43
|
"peerDependencies": {
|
|
53
44
|
"nuxt": "^4.5.1",
|
|
54
45
|
"vue": "^3.5.40"
|
|
55
46
|
},
|
|
56
47
|
"dependencies": {
|
|
57
|
-
"@nuxt/kit": "4.
|
|
58
|
-
"@nuxt/schema": "4.
|
|
48
|
+
"@nuxt/kit": "4.6.1",
|
|
49
|
+
"@nuxt/schema": "4.6.1"
|
|
59
50
|
},
|
|
60
51
|
"devDependencies": {
|
|
61
|
-
"@nuxt/module-builder": "1.0.
|
|
52
|
+
"@nuxt/module-builder": "1.0.3",
|
|
62
53
|
"@types/node": "24.12.0",
|
|
63
54
|
"ajv": "8.20.0",
|
|
64
|
-
"nuxt": "4.
|
|
55
|
+
"nuxt": "4.6.1",
|
|
65
56
|
"typescript": "5.9.3",
|
|
66
|
-
"vitest": "
|
|
57
|
+
"vitest": "5.0.3",
|
|
67
58
|
"vue": "3.5.43",
|
|
68
|
-
"vue-tsc": "3.
|
|
59
|
+
"vue-tsc": "3.3.13"
|
|
69
60
|
},
|
|
70
61
|
"repository": {
|
|
71
62
|
"type": "git",
|
|
@@ -80,16 +71,11 @@
|
|
|
80
71
|
"registry": "https://registry.npmjs.org",
|
|
81
72
|
"provenance": true
|
|
82
73
|
},
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
},
|
|
90
|
-
"patchedDependencies": {
|
|
91
|
-
"braces@3.0.3": "patches/braces@3.0.3.patch",
|
|
92
|
-
"node-forge@1.4.0": "patches/node-forge@1.4.0.patch"
|
|
93
|
-
}
|
|
74
|
+
"scripts": {
|
|
75
|
+
"build": "nuxt-module-build build && nuxt build playground",
|
|
76
|
+
"typecheck": "vue-tsc --noEmit --project tsconfig.json",
|
|
77
|
+
"test": "vitest run && pnpm run test:dependency-security && pnpm run test:consumer-security",
|
|
78
|
+
"test:dependency-security": "node --test scripts/dependency-security.check.cjs",
|
|
79
|
+
"test:consumer-security": "node --test scripts/consumer-security.check.mjs"
|
|
94
80
|
}
|
|
95
|
-
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Steve King
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/security/README.md
CHANGED
|
@@ -19,3 +19,5 @@ The RSA test creates an ephemeral key in memory and never reads a user key. Patc
|
|
|
19
19
|
Run `pnpm audit --json` on the installed application and retain the report. Only the two backported braces/node-forge advisories may remain; any other advisory, including simple-git, its argument parser, or esbuild, blocks validation. Run the dependency regression command on each install to reject missing or older active versions.
|
|
20
20
|
|
|
21
21
|
The code patch inputs retain third-party notices in `LICENSE-braces` (MIT) and `LICENSE-node-forge`. The original dependencies remain registry packages; the shipped files configure and test the consumer rather than silently replacing its package manager.
|
|
22
|
+
|
|
23
|
+
The simple-git 4.0.2 compatibility patch restores only the legacy ESM default factory used by Nuxt devtools. It aliases the existing, fixed `simpleGit` factory and does not change argument validation, executable selection, or Git execution. The named factory remains identical. `LICENSE-simple-git` retains its upstream MIT notice. A fresh consumer build and type check with devtools enabled are mandatory release checks.
|
|
@@ -57,3 +57,14 @@ test('active dependency graph uses every required upstream security version', ()
|
|
|
57
57
|
for (const manifest of matches) assert.equal(JSON.parse(fs.readFileSync(manifest, 'utf8')).version, version, name);
|
|
58
58
|
}
|
|
59
59
|
});
|
|
60
|
+
|
|
61
|
+
// Preserve the ESM entry point expected by current Nuxt devtools without downgrading Git protections.
|
|
62
|
+
test('fixed simple-git retains named and legacy ESM factory exports', async () => {
|
|
63
|
+
const { pathToFileURL } = require('node:url');
|
|
64
|
+
const { installedDependencies } = require('./resolved-dependency.cjs');
|
|
65
|
+
for (const manifest of installedDependencies(process.cwd(), 'simple-git', '4.0.2')) {
|
|
66
|
+
const module = await import(pathToFileURL(path.join(path.dirname(manifest), 'dist/index.mjs')).href);
|
|
67
|
+
assert.equal(typeof module.simpleGit, 'function');
|
|
68
|
+
assert.equal(module.default, module.simpleGit);
|
|
69
|
+
}
|
|
70
|
+
});
|
package/security/project.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { existsSync, lstatSync, readFileSync, mkdirSync, writeFileSync, renameSy
|
|
|
3
3
|
import { join, resolve } from 'node:path'
|
|
4
4
|
import { randomUUID } from 'node:crypto'
|
|
5
5
|
|
|
6
|
-
const patches = ['braces@3.0.3', 'node-forge@1.4.0']
|
|
6
|
+
const patches = ['braces@3.0.3', 'node-forge@1.4.0', 'simple-git@4.0.2']
|
|
7
7
|
function regular(path) {
|
|
8
8
|
const stat = lstatSync(path)
|
|
9
9
|
if (stat.isSymbolicLink() || !stat.isFile() || stat.size > 1024 * 1024) {
|