@comfyorg/account-core 1.0.0-alpha.0 → 1.0.0-alpha.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/dist/core/billing/billingContracts.d.ts +29 -14
- package/dist/core/billing/billingErrorBody.d.ts +8 -4
- package/dist/core/billing/billingErrorBody.js +14 -4
- package/dist/core/billing/credentialedTransport.d.ts +23 -7
- package/dist/core/billing/credentialedTransport.js +72 -8
- package/dist/core/billing/events.d.ts +47 -0
- package/dist/core/billing/events.js +43 -0
- package/dist/core/billing/index.d.ts +16 -11
- package/dist/core/billing/index.js +8 -6
- package/dist/core/billing/operationLifecycle.d.ts +34 -1
- package/dist/core/billing/operationLifecycle.js +146 -67
- package/dist/core/billing/operationPointer.d.ts +27 -2
- package/dist/core/billing/operationPointer.js +36 -25
- package/dist/core/billing/operationPolicy.d.ts +44 -7
- package/dist/core/billing/operationPolicy.js +64 -3
- package/dist/core/billing/operationState.d.ts +102 -18
- package/dist/core/billing/operationState.js +78 -8
- package/dist/core/billing/paymentCopy.d.ts +6 -1
- package/dist/core/billing/paymentCopy.js +34 -1
- package/dist/core/billing/paymentProjection.d.ts +14 -2
- package/dist/core/billing/paymentProjection.js +37 -9
- package/dist/core/billing/scopedReader.d.ts +11 -5
- package/dist/core/billing/scopedReader.js +20 -9
- package/dist/core/billing/sharedRead.js +4 -2
- package/dist/core/billing/subscriptionCommands.d.ts +37 -9
- package/dist/core/billing/subscriptionCommands.js +42 -30
- package/dist/core/billing/topup.d.ts +26 -0
- package/dist/core/billing/topup.js +36 -2
- package/dist/core/billing/wireCents.d.ts +8 -0
- package/dist/core/billing/wireCents.js +8 -0
- package/dist/core/billing/workspaceInvites.d.ts +12 -0
- package/dist/core/billing/workspaceInvites.js +26 -0
- package/dist/core/identity.d.ts +6 -6
- package/dist/core/identity.js +4 -10
- package/dist/core/lazyIdentity.d.ts +31 -0
- package/dist/core/lazyIdentity.js +96 -0
- package/dist/core/requestAuth.d.ts +30 -0
- package/dist/core/requestAuth.js +41 -0
- package/dist/core/session.d.ts +12 -18
- package/dist/core/session.js +16 -26
- package/dist/core/sessionContracts.d.ts +39 -2
- package/dist/core/sessionTokenMint.d.ts +33 -0
- package/dist/core/sessionTokenMint.js +205 -0
- package/dist/core/webSession.d.ts +25 -0
- package/dist/core/webSession.js +137 -0
- package/dist/core/webSessionFlag.d.ts +20 -0
- package/dist/core/webSessionFlag.js +62 -0
- package/dist/core/webSessionIdentity.d.ts +291 -0
- package/dist/core/webSessionIdentity.js +496 -0
- package/dist/firebase/configSource.d.ts +54 -0
- package/dist/firebase/configSource.js +111 -0
- package/dist/firebase/index.d.ts +71 -8
- package/dist/firebase/index.js +236 -23
- package/dist/firebase/popupWatch.d.ts +42 -0
- package/dist/firebase/popupWatch.js +140 -0
- package/dist/testing.d.ts +38 -0
- package/dist/testing.js +116 -0
- package/dist/web/crossTabRefresh.d.ts +4 -2
- package/dist/web/crossTabRefresh.js +13 -0
- package/dist/workspaceLink.d.ts +45 -0
- package/dist/workspaceLink.js +78 -0
- package/package.json +30 -6
package/dist/testing.js
CHANGED
|
@@ -1,3 +1,119 @@
|
|
|
1
1
|
export { brandIdentity as createTestIdentity } from './core/identity.js';
|
|
2
2
|
// Enumerating wording (CWE-204) each host asserts its copy never matches; shared so the two suites can't drift.
|
|
3
3
|
export const ENUMERATION_ORACLE = /\bexists?\b|already (?:registered|in use|have|exists)|wrong password|no account|not found|is registered|different (?:sign-in method|credential)/i;
|
|
4
|
+
export function fakeWebSessionUser(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
id: 'user-1',
|
|
7
|
+
email: 'user-1@example.com',
|
|
8
|
+
name: 'Test User',
|
|
9
|
+
emailVerified: true,
|
|
10
|
+
signInProvider: 'google.com',
|
|
11
|
+
...overrides
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const FAKE_CSRF_TOKEN = 'fake-csrf-token';
|
|
15
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
16
|
+
function jsonResponse(status, body) {
|
|
17
|
+
return new Response(JSON.stringify(body), {
|
|
18
|
+
status,
|
|
19
|
+
headers: { 'Content-Type': 'application/json' }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
function errorResponse(status, code) {
|
|
23
|
+
return jsonResponse(status, { code, message: code });
|
|
24
|
+
}
|
|
25
|
+
function sessionBody(user, now) {
|
|
26
|
+
return {
|
|
27
|
+
user: {
|
|
28
|
+
id: user.id,
|
|
29
|
+
email: user.email,
|
|
30
|
+
name: user.name,
|
|
31
|
+
email_verified: user.emailVerified,
|
|
32
|
+
sign_in_provider: user.signInProvider
|
|
33
|
+
},
|
|
34
|
+
csrf_token: FAKE_CSRF_TOKEN,
|
|
35
|
+
expires_at: new Date(now() + DAY_MS).toISOString(),
|
|
36
|
+
absolute_expires_at: new Date(now() + 7 * DAY_MS).toISOString()
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function routesFor(signInUser, now) {
|
|
40
|
+
const revoke = (endpoint) => {
|
|
41
|
+
endpoint.state = { kind: 'dead', code: 'session_revoked' };
|
|
42
|
+
};
|
|
43
|
+
return new Map([
|
|
44
|
+
[
|
|
45
|
+
'POST /api/auth/session',
|
|
46
|
+
(endpoint, headers) => {
|
|
47
|
+
if (!headers.get('authorization')?.startsWith('Bearer ')) {
|
|
48
|
+
return errorResponse(401, 'no_session');
|
|
49
|
+
}
|
|
50
|
+
endpoint.state = { kind: 'live', user: signInUser };
|
|
51
|
+
return jsonResponse(200, { success: true, expiresIn: DAY_MS / 1000 });
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
'DELETE /api/auth/session',
|
|
56
|
+
(endpoint) => {
|
|
57
|
+
revoke(endpoint);
|
|
58
|
+
return jsonResponse(200, { success: true });
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
'GET /api/auth/session',
|
|
63
|
+
({ state }) => state.kind === 'live'
|
|
64
|
+
? jsonResponse(200, sessionBody(state.user, now))
|
|
65
|
+
: errorResponse(401, state.kind === 'dead' ? state.code : 'no_session')
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'POST /api/auth/sessions/revoke-all',
|
|
69
|
+
(endpoint, headers) => {
|
|
70
|
+
if (!headers.get('authorization')?.startsWith('Bearer ')) {
|
|
71
|
+
return errorResponse(401, 'no_session');
|
|
72
|
+
}
|
|
73
|
+
if (endpoint.state.kind === 'dead') {
|
|
74
|
+
return errorResponse(401, endpoint.state.code);
|
|
75
|
+
}
|
|
76
|
+
if (headers.get('x-csrf-token') !== FAKE_CSRF_TOKEN) {
|
|
77
|
+
return errorResponse(403, 'csrf_invalid');
|
|
78
|
+
}
|
|
79
|
+
revoke(endpoint);
|
|
80
|
+
return jsonResponse(200, { revoked: 1 });
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A `fetch` serving ingest's session routes under `/api/auth`. POST with a
|
|
87
|
+
* bearer proof signs `signInUser` in; DELETE and revoke-all leave the cookie
|
|
88
|
+
* revoked, as the real endpoint does.
|
|
89
|
+
*/
|
|
90
|
+
export function createFakeWebSessionEndpoint({ state, signInUser = fakeWebSessionUser(), now = Date.now }) {
|
|
91
|
+
const requests = [];
|
|
92
|
+
const routes = routesFor(signInUser, now);
|
|
93
|
+
const endpoint = {
|
|
94
|
+
state,
|
|
95
|
+
requests,
|
|
96
|
+
fetch: async (input, init = {}) => {
|
|
97
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
98
|
+
const { pathname } = new URL(input instanceof Request ? input.url : String(input));
|
|
99
|
+
const headers = new Headers(init.headers);
|
|
100
|
+
requests.push({
|
|
101
|
+
method,
|
|
102
|
+
path: pathname,
|
|
103
|
+
credentials: init.credentials,
|
|
104
|
+
cache: init.cache,
|
|
105
|
+
headers: Object.fromEntries(headers)
|
|
106
|
+
});
|
|
107
|
+
const current = endpoint.state;
|
|
108
|
+
if (current.kind === 'network_error') {
|
|
109
|
+
throw new TypeError('Failed to fetch');
|
|
110
|
+
}
|
|
111
|
+
if (current.kind === 'unavailable') {
|
|
112
|
+
return errorResponse(current.status, 'unavailable');
|
|
113
|
+
}
|
|
114
|
+
const route = routes.get(`${method} ${pathname}`);
|
|
115
|
+
return route ? route(endpoint, headers) : errorResponse(404, 'not_found');
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return endpoint;
|
|
119
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { CrossTabRefreshPort } from '../core/session.js';
|
|
1
|
+
import type { AccountCredential, CrossTabRefreshPort } from '../core/session.js';
|
|
2
|
+
import type { VisibilityPort } from '../core/webSessionIdentity.js';
|
|
2
3
|
/**
|
|
3
4
|
* Web Locks + BroadcastChannel implementation of the cross-tab refresh port.
|
|
4
5
|
* The coordination key names both the lock and the channel. Resolves to
|
|
@@ -15,4 +16,5 @@ import type { CrossTabRefreshPort } from '../core/session.js';
|
|
|
15
16
|
* can do to siblings (hand them a token), not what it can reach. Hosts that
|
|
16
17
|
* load third-party script into the realm accept that when they opt in.
|
|
17
18
|
*/
|
|
18
|
-
export declare function createWebCrossTabRefreshPort(): CrossTabRefreshPort | undefined;
|
|
19
|
+
export declare function createWebCrossTabRefreshPort<Message = AccountCredential>(): CrossTabRefreshPort<Message> | undefined;
|
|
20
|
+
export declare function createWebVisibilityPort(): VisibilityPort | undefined;
|
|
@@ -119,3 +119,16 @@ export function createWebCrossTabRefreshPort() {
|
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
+
export function createWebVisibilityPort() {
|
|
123
|
+
if (typeof document === 'undefined')
|
|
124
|
+
return undefined;
|
|
125
|
+
const isVisible = () => document.visibilityState === 'visible';
|
|
126
|
+
return {
|
|
127
|
+
isVisible,
|
|
128
|
+
onChange: (listener) => {
|
|
129
|
+
const handler = () => listener(isVisible());
|
|
130
|
+
document.addEventListener('visibilitychange', handler);
|
|
131
|
+
return () => document.removeEventListener('visibilitychange', handler);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace deep-link contract: the one query parameter, `workspace`,
|
|
3
|
+
* that every surface — the cloud app, comfy.org, and the billing SDK's entry
|
|
4
|
+
* URL — reads and writes to say which workspace a link is for.
|
|
5
|
+
*
|
|
6
|
+
* It is the URL parameter only, never the token API field. `POST
|
|
7
|
+
* /api/auth/token` still takes `{ workspace_id }` in its request body; that
|
|
8
|
+
* name does not change here and this module does not touch it.
|
|
9
|
+
*
|
|
10
|
+
* A destination applies the link before it loads workspace-scoped content:
|
|
11
|
+
* it mints a token for the named id and lets the server decide membership,
|
|
12
|
+
* the same way any other mint does. A payment or billing destination fails
|
|
13
|
+
* closed on a link it cannot honor and never acts on a workspace other than
|
|
14
|
+
* the one named — silently falling back to the caller's own active workspace
|
|
15
|
+
* would bill or expose the wrong one. A navigation destination may fall back
|
|
16
|
+
* to its own active workspace when the link is absent or invalid, but must
|
|
17
|
+
* show which workspace it landed on rather than switch silently. The
|
|
18
|
+
* parameter is carried through the sign-in redirect like any other query
|
|
19
|
+
* parameter on the path being returned to.
|
|
20
|
+
*/
|
|
21
|
+
export declare const WORKSPACE_LINK_PARAM = "workspace";
|
|
22
|
+
export type WorkspaceLinkRead = {
|
|
23
|
+
readonly status: 'absent';
|
|
24
|
+
} | {
|
|
25
|
+
readonly status: 'ok';
|
|
26
|
+
readonly workspaceId: string;
|
|
27
|
+
} | {
|
|
28
|
+
readonly status: 'invalid';
|
|
29
|
+
};
|
|
30
|
+
export declare function isWorkspaceId(value: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Reads the link out of a URL, a `URLSearchParams`, or a string (a full URL
|
|
33
|
+
* or the path-and-query form a router hands over). A missing parameter is
|
|
34
|
+
* `absent`; a present-but-empty value is `invalid`, the same as any other
|
|
35
|
+
* value outside the shared charset — an empty `workspace=` is a producer
|
|
36
|
+
* bug, not "no link," and a payment destination has to fail closed on it
|
|
37
|
+
* rather than treat it as though the link were never there. A repeated
|
|
38
|
+
* parameter is also `invalid` rather than picking one, so a caller cannot be
|
|
39
|
+
* pointed at two different workspaces by ambiguity.
|
|
40
|
+
*/
|
|
41
|
+
export declare function readWorkspaceLink(source: URL | URLSearchParams | string): WorkspaceLinkRead;
|
|
42
|
+
/** Returns a new `URL` carrying the link; never mutates `url`. Throws `RangeError` on an id outside the shared charset. */
|
|
43
|
+
export declare function withWorkspaceLink(url: URL, workspaceId: string): URL;
|
|
44
|
+
/** Returns a new `URL` with the link removed; never mutates `url`. */
|
|
45
|
+
export declare function withoutWorkspaceLink(url: URL): URL;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The workspace deep-link contract: the one query parameter, `workspace`,
|
|
3
|
+
* that every surface — the cloud app, comfy.org, and the billing SDK's entry
|
|
4
|
+
* URL — reads and writes to say which workspace a link is for.
|
|
5
|
+
*
|
|
6
|
+
* It is the URL parameter only, never the token API field. `POST
|
|
7
|
+
* /api/auth/token` still takes `{ workspace_id }` in its request body; that
|
|
8
|
+
* name does not change here and this module does not touch it.
|
|
9
|
+
*
|
|
10
|
+
* A destination applies the link before it loads workspace-scoped content:
|
|
11
|
+
* it mints a token for the named id and lets the server decide membership,
|
|
12
|
+
* the same way any other mint does. A payment or billing destination fails
|
|
13
|
+
* closed on a link it cannot honor and never acts on a workspace other than
|
|
14
|
+
* the one named — silently falling back to the caller's own active workspace
|
|
15
|
+
* would bill or expose the wrong one. A navigation destination may fall back
|
|
16
|
+
* to its own active workspace when the link is absent or invalid, but must
|
|
17
|
+
* show which workspace it landed on rather than switch silently. The
|
|
18
|
+
* parameter is carried through the sign-in redirect like any other query
|
|
19
|
+
* parameter on the path being returned to.
|
|
20
|
+
*/
|
|
21
|
+
export const WORKSPACE_LINK_PARAM = 'workspace';
|
|
22
|
+
/** Same charset as `@comfyorg/billing-contract`'s identifiers: no path separator, query delimiter, or percent escape survives it. */
|
|
23
|
+
const WORKSPACE_ID = /^[A-Za-z0-9_-]{1,128}$/;
|
|
24
|
+
export function isWorkspaceId(value) {
|
|
25
|
+
return WORKSPACE_ID.test(value);
|
|
26
|
+
}
|
|
27
|
+
/** Reserved by RFC 2606; only used to parse a path+query string that carries no origin of its own. */
|
|
28
|
+
const PARSE_BASE = 'https://workspace-link.invalid/';
|
|
29
|
+
function searchParamsOf(source) {
|
|
30
|
+
if (source instanceof URLSearchParams)
|
|
31
|
+
return source;
|
|
32
|
+
if (source instanceof URL)
|
|
33
|
+
return source.searchParams;
|
|
34
|
+
try {
|
|
35
|
+
return new URL(source, PARSE_BASE).searchParams;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Reads the link out of a URL, a `URLSearchParams`, or a string (a full URL
|
|
43
|
+
* or the path-and-query form a router hands over). A missing parameter is
|
|
44
|
+
* `absent`; a present-but-empty value is `invalid`, the same as any other
|
|
45
|
+
* value outside the shared charset — an empty `workspace=` is a producer
|
|
46
|
+
* bug, not "no link," and a payment destination has to fail closed on it
|
|
47
|
+
* rather than treat it as though the link were never there. A repeated
|
|
48
|
+
* parameter is also `invalid` rather than picking one, so a caller cannot be
|
|
49
|
+
* pointed at two different workspaces by ambiguity.
|
|
50
|
+
*/
|
|
51
|
+
export function readWorkspaceLink(source) {
|
|
52
|
+
const params = searchParamsOf(source);
|
|
53
|
+
if (!params)
|
|
54
|
+
return { status: 'absent' };
|
|
55
|
+
const values = params.getAll(WORKSPACE_LINK_PARAM);
|
|
56
|
+
if (values.length === 0)
|
|
57
|
+
return { status: 'absent' };
|
|
58
|
+
if (values.length > 1)
|
|
59
|
+
return { status: 'invalid' };
|
|
60
|
+
const [value] = values;
|
|
61
|
+
if (!isWorkspaceId(value))
|
|
62
|
+
return { status: 'invalid' };
|
|
63
|
+
return { status: 'ok', workspaceId: value };
|
|
64
|
+
}
|
|
65
|
+
/** Returns a new `URL` carrying the link; never mutates `url`. Throws `RangeError` on an id outside the shared charset. */
|
|
66
|
+
export function withWorkspaceLink(url, workspaceId) {
|
|
67
|
+
if (!isWorkspaceId(workspaceId))
|
|
68
|
+
throw new RangeError(`not a workspace id: ${workspaceId}`);
|
|
69
|
+
const next = new URL(url.href);
|
|
70
|
+
next.searchParams.set(WORKSPACE_LINK_PARAM, workspaceId);
|
|
71
|
+
return next;
|
|
72
|
+
}
|
|
73
|
+
/** Returns a new `URL` with the link removed; never mutates `url`. */
|
|
74
|
+
export function withoutWorkspaceLink(url) {
|
|
75
|
+
const next = new URL(url.href);
|
|
76
|
+
next.searchParams.delete(WORKSPACE_LINK_PARAM);
|
|
77
|
+
return next;
|
|
78
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comfyorg/account-core",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
4
|
"description": "Shared account layer: a framework-free session core that exchanges a signed-in identity for the workspace-scoped Cloud JWT with valid-on-read freshness, plus the package-owned Firebase identity entry. It has no view layer and no Vue dependency; the composables and unstyled components ship from @comfyorg/account-ui",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"homepage": "https://comfy.org",
|
|
@@ -29,6 +29,26 @@
|
|
|
29
29
|
"types": "./dist/core/session.d.ts",
|
|
30
30
|
"import": "./dist/core/session.js"
|
|
31
31
|
},
|
|
32
|
+
"./webSession": {
|
|
33
|
+
"types": "./dist/core/webSession.d.ts",
|
|
34
|
+
"import": "./dist/core/webSession.js"
|
|
35
|
+
},
|
|
36
|
+
"./webSessionFlag": {
|
|
37
|
+
"types": "./dist/core/webSessionFlag.d.ts",
|
|
38
|
+
"import": "./dist/core/webSessionFlag.js"
|
|
39
|
+
},
|
|
40
|
+
"./requestAuth": {
|
|
41
|
+
"types": "./dist/core/requestAuth.d.ts",
|
|
42
|
+
"import": "./dist/core/requestAuth.js"
|
|
43
|
+
},
|
|
44
|
+
"./sessionTokenMint": {
|
|
45
|
+
"types": "./dist/core/sessionTokenMint.d.ts",
|
|
46
|
+
"import": "./dist/core/sessionTokenMint.js"
|
|
47
|
+
},
|
|
48
|
+
"./webSessionIdentity": {
|
|
49
|
+
"types": "./dist/core/webSessionIdentity.d.ts",
|
|
50
|
+
"import": "./dist/core/webSessionIdentity.js"
|
|
51
|
+
},
|
|
32
52
|
"./billing": {
|
|
33
53
|
"types": "./dist/core/billing/index.d.ts",
|
|
34
54
|
"import": "./dist/core/billing/index.js"
|
|
@@ -73,6 +93,10 @@
|
|
|
73
93
|
"types": "./dist/redirect.d.ts",
|
|
74
94
|
"import": "./dist/redirect.js"
|
|
75
95
|
},
|
|
96
|
+
"./workspaceLink": {
|
|
97
|
+
"types": "./dist/workspaceLink.d.ts",
|
|
98
|
+
"import": "./dist/workspaceLink.js"
|
|
99
|
+
},
|
|
76
100
|
"./telemetry": {
|
|
77
101
|
"types": "./dist/telemetry.d.ts",
|
|
78
102
|
"import": "./dist/telemetry.js"
|
|
@@ -87,15 +111,15 @@
|
|
|
87
111
|
"access": "public"
|
|
88
112
|
},
|
|
89
113
|
"dependencies": {
|
|
90
|
-
"@comfyorg/ingest-types": "^1.
|
|
114
|
+
"@comfyorg/ingest-types": "^1.1.0"
|
|
91
115
|
},
|
|
92
116
|
"devDependencies": {
|
|
93
117
|
"@types/node": "^25.9.5",
|
|
94
|
-
"@vitest/coverage-v8": "^
|
|
118
|
+
"@vitest/coverage-v8": "^5.0.2",
|
|
95
119
|
"firebase": "^11.6.0",
|
|
96
|
-
"happy-dom": "20.
|
|
120
|
+
"happy-dom": "20.14.5",
|
|
97
121
|
"typescript": "^6.0.3",
|
|
98
|
-
"vitest": "^
|
|
122
|
+
"vitest": "^5.0.2",
|
|
99
123
|
"zod": "^3.23.8"
|
|
100
124
|
},
|
|
101
125
|
"peerDependencies": {
|
|
@@ -109,6 +133,6 @@
|
|
|
109
133
|
"lint": "oxlint src --type-aware && eslint src",
|
|
110
134
|
"smoke:pack": "tsx scripts/smokePack.ts",
|
|
111
135
|
"test": "vitest run",
|
|
112
|
-
"typecheck": "tsc --noEmit && tsc -p scripts/tsconfig.json"
|
|
136
|
+
"typecheck": "tsx ../../scripts/vue-tsc-native.ts --noEmit && tsx ../../scripts/vue-tsc-native.ts -p scripts/tsconfig.json"
|
|
113
137
|
}
|
|
114
138
|
}
|