@quadrokit/client 0.2.1 → 0.2.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/dist/cli.mjs +55 -9
- package/dist/generate/catalog-session.d.ts +7 -0
- package/dist/generate/catalog-session.d.ts.map +1 -0
- package/dist/generate/catalog-session.mjs +46 -0
- package/dist/generate/dotenv-cwd.d.ts +3 -0
- package/dist/generate/dotenv-cwd.d.ts.map +1 -0
- package/dist/generate/dotenv-cwd.mjs +37 -0
- package/package.json +4 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { defaultLoginUrlFromCatalogUrl, fetch4DAdminSessionCookie, } from './generate/catalog-session.mjs';
|
|
4
5
|
import { writeGenerated } from './generate/codegen.mjs';
|
|
6
|
+
import { applyGenerateDotenv } from './generate/dotenv-cwd.mjs';
|
|
5
7
|
function parseArgs(argv) {
|
|
6
8
|
let command = '';
|
|
7
9
|
let url;
|
|
8
10
|
let token;
|
|
11
|
+
let accessKey;
|
|
12
|
+
let loginUrl;
|
|
9
13
|
let out = '.quadrokit/generated';
|
|
10
14
|
for (let i = 0; i < argv.length; i++) {
|
|
11
15
|
const a = argv[i];
|
|
@@ -15,6 +19,12 @@ function parseArgs(argv) {
|
|
|
15
19
|
else if (a === '--token' && argv[i + 1]) {
|
|
16
20
|
token = argv[++i];
|
|
17
21
|
}
|
|
22
|
+
else if (a === '--access-key' && argv[i + 1]) {
|
|
23
|
+
accessKey = argv[++i];
|
|
24
|
+
}
|
|
25
|
+
else if (a === '--login-url' && argv[i + 1]) {
|
|
26
|
+
loginUrl = argv[++i];
|
|
27
|
+
}
|
|
18
28
|
else if (a === '--out' && argv[i + 1]) {
|
|
19
29
|
out = argv[++i];
|
|
20
30
|
}
|
|
@@ -22,9 +32,9 @@ function parseArgs(argv) {
|
|
|
22
32
|
command = a;
|
|
23
33
|
}
|
|
24
34
|
}
|
|
25
|
-
return { command, url, token, out };
|
|
35
|
+
return { command, url, token, accessKey, loginUrl, out };
|
|
26
36
|
}
|
|
27
|
-
async function loadCatalog(url,
|
|
37
|
+
async function loadCatalog(url, auth) {
|
|
28
38
|
if (url.startsWith('file:')) {
|
|
29
39
|
const path = fileURLToPath(url);
|
|
30
40
|
const text = await readFile(path, 'utf8');
|
|
@@ -33,8 +43,21 @@ async function loadCatalog(url, token) {
|
|
|
33
43
|
const headers = {
|
|
34
44
|
Accept: 'application/json',
|
|
35
45
|
};
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
const key = auth?.accessKey?.trim();
|
|
47
|
+
if (key) {
|
|
48
|
+
let login = auth?.loginUrl?.trim();
|
|
49
|
+
if (!login) {
|
|
50
|
+
try {
|
|
51
|
+
login = defaultLoginUrlFromCatalogUrl(url);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
throw new Error('quadrokit: --access-key requires an http(s) catalog --url, or pass --login-url');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
headers.Cookie = await fetch4DAdminSessionCookie(login, key);
|
|
58
|
+
}
|
|
59
|
+
else if (auth?.token) {
|
|
60
|
+
headers.Authorization = `Bearer ${auth.token}`;
|
|
38
61
|
}
|
|
39
62
|
const res = await fetch(url, { headers });
|
|
40
63
|
if (!res.ok) {
|
|
@@ -45,21 +68,44 @@ async function loadCatalog(url, token) {
|
|
|
45
68
|
}
|
|
46
69
|
async function main() {
|
|
47
70
|
const argv = process.argv.slice(2);
|
|
48
|
-
const
|
|
71
|
+
const parsed = parseArgs(argv);
|
|
72
|
+
const { command, token, accessKey: accessKeyArg, loginUrl: loginUrlArg, out } = parsed;
|
|
49
73
|
if (command !== 'generate') {
|
|
50
|
-
console.error(`Usage: quadrokit-client generate --url <catalog_url> [
|
|
74
|
+
console.error(`Usage: quadrokit-client generate [--url <catalog_url>] [auth] [--out <dir>]
|
|
75
|
+
|
|
76
|
+
--url defaults to \${VITE_4D_ORIGIN}/rest/\\$catalog (from env or .env in cwd), else http://127.0.0.1:7080/rest/\\$catalog
|
|
77
|
+
|
|
78
|
+
Auth (generator only; not used by the app runtime):
|
|
79
|
+
--access-key <key> POST multipart accessKey to /api/login, then use 4DAdminSID for the catalog
|
|
80
|
+
--login-url <url> Full login URL (default: {catalog origin}/api/login)
|
|
81
|
+
--token <secret> Authorization: Bearer (when you do not use --access-key)
|
|
82
|
+
|
|
83
|
+
Env / .env: VITE_4D_ORIGIN, QUADROKIT_ACCESS_KEY, QUADROKIT_LOGIN_URL, QUADROKIT_CATALOG_TOKEN
|
|
51
84
|
|
|
52
85
|
Examples:
|
|
86
|
+
quadrokit-client generate
|
|
87
|
+
quadrokit-client generate --url https://localhost:7443/rest/\\$catalog --access-key MY_TOKEN
|
|
53
88
|
quadrokit-client generate --url http://localhost:7080/rest/\\$catalog --token secret
|
|
54
89
|
quadrokit-client generate --url file://./assets/catalog.json --out .quadrokit/generated
|
|
90
|
+
|
|
91
|
+
bunx -p @quadrokit/client quadrokit-client generate
|
|
55
92
|
`);
|
|
56
93
|
process.exit(1);
|
|
57
94
|
}
|
|
95
|
+
applyGenerateDotenv();
|
|
96
|
+
let url = parsed.url;
|
|
58
97
|
if (!url) {
|
|
59
|
-
|
|
60
|
-
|
|
98
|
+
const origin = (process.env.VITE_4D_ORIGIN || 'http://127.0.0.1:7080').replace(/\/$/, '');
|
|
99
|
+
url = `${origin}/rest/$catalog`;
|
|
61
100
|
}
|
|
62
|
-
const
|
|
101
|
+
const accessKey = accessKeyArg?.trim() || process.env.QUADROKIT_ACCESS_KEY?.trim() || undefined;
|
|
102
|
+
const loginUrl = loginUrlArg?.trim() || process.env.QUADROKIT_LOGIN_URL?.trim() || undefined;
|
|
103
|
+
const bearer = token?.trim() || process.env.QUADROKIT_CATALOG_TOKEN?.trim() || undefined;
|
|
104
|
+
const catalog = await loadCatalog(url, {
|
|
105
|
+
accessKey,
|
|
106
|
+
loginUrl,
|
|
107
|
+
token: accessKey ? undefined : bearer,
|
|
108
|
+
});
|
|
63
109
|
await writeGenerated(out, catalog);
|
|
64
110
|
console.error(`Wrote generated client to ${out}`);
|
|
65
111
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator-only: obtain `4DAdminSID` via POST `/api/login` (multipart `accessKey`).
|
|
3
|
+
* Not used by the runtime REST client.
|
|
4
|
+
*/
|
|
5
|
+
export declare function defaultLoginUrlFromCatalogUrl(catalogUrl: string): string;
|
|
6
|
+
export declare function fetch4DAdminSessionCookie(loginUrl: string, accessKey: string): Promise<string>;
|
|
7
|
+
//# sourceMappingURL=catalog-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-session.d.ts","sourceRoot":"","sources":["../../src/generate/catalog-session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAcjB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator-only: obtain `4DAdminSID` via POST `/api/login` (multipart `accessKey`).
|
|
3
|
+
* Not used by the runtime REST client.
|
|
4
|
+
*/
|
|
5
|
+
export function defaultLoginUrlFromCatalogUrl(catalogUrl) {
|
|
6
|
+
const u = new URL(catalogUrl);
|
|
7
|
+
return `${u.origin}/api/login`;
|
|
8
|
+
}
|
|
9
|
+
export async function fetch4DAdminSessionCookie(loginUrl, accessKey) {
|
|
10
|
+
const form = new FormData();
|
|
11
|
+
form.append('accessKey', accessKey);
|
|
12
|
+
const res = await fetch(loginUrl, { method: 'POST', body: form });
|
|
13
|
+
if (!res.ok) {
|
|
14
|
+
const t = await res.text();
|
|
15
|
+
throw new Error(`quadrokit login failed (${res.status}): ${t.slice(0, 500)}`);
|
|
16
|
+
}
|
|
17
|
+
const lines = readSetCookieLines(res.headers);
|
|
18
|
+
const value = parse4DAdminSidValue(lines);
|
|
19
|
+
if (!value) {
|
|
20
|
+
throw new Error('quadrokit login: response had no 4DAdminSID Set-Cookie');
|
|
21
|
+
}
|
|
22
|
+
return `4DAdminSID=${value}`;
|
|
23
|
+
}
|
|
24
|
+
function readSetCookieLines(headers) {
|
|
25
|
+
const ext = headers;
|
|
26
|
+
if (typeof ext.getSetCookie === 'function') {
|
|
27
|
+
return ext.getSetCookie();
|
|
28
|
+
}
|
|
29
|
+
const c = headers.get('set-cookie');
|
|
30
|
+
return c ? [c] : [];
|
|
31
|
+
}
|
|
32
|
+
function parse4DAdminSidValue(setCookieLines) {
|
|
33
|
+
for (const line of setCookieLines) {
|
|
34
|
+
const firstPart = line.split(';')[0]?.trim();
|
|
35
|
+
if (!firstPart)
|
|
36
|
+
continue;
|
|
37
|
+
const eq = firstPart.indexOf('=');
|
|
38
|
+
if (eq <= 0)
|
|
39
|
+
continue;
|
|
40
|
+
const name = firstPart.slice(0, eq);
|
|
41
|
+
const val = firstPart.slice(eq + 1);
|
|
42
|
+
if (name === '4DAdminSID' && val)
|
|
43
|
+
return val;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dotenv-cwd.d.ts","sourceRoot":"","sources":["../../src/generate/dotenv-cwd.ts"],"names":[],"mappings":"AAUA,sGAAsG;AACtG,wBAAgB,mBAAmB,IAAI,IAAI,CAuB1C"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const KEYS = [
|
|
4
|
+
'VITE_4D_ORIGIN',
|
|
5
|
+
'QUADROKIT_ACCESS_KEY',
|
|
6
|
+
'QUADROKIT_LOGIN_URL',
|
|
7
|
+
'QUADROKIT_CATALOG_TOKEN',
|
|
8
|
+
];
|
|
9
|
+
/** Merge project `.env` into `process.env` for keys that are not already set (generator CLI only). */
|
|
10
|
+
export function applyGenerateDotenv() {
|
|
11
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
12
|
+
if (!existsSync(envPath))
|
|
13
|
+
return;
|
|
14
|
+
const wanted = new Set(KEYS.filter((k) => !process.env[k]));
|
|
15
|
+
if (wanted.size === 0)
|
|
16
|
+
return;
|
|
17
|
+
const raw = readFileSync(envPath, 'utf8');
|
|
18
|
+
for (const line of raw.split('\n')) {
|
|
19
|
+
const t = line.trim();
|
|
20
|
+
if (!t || t.startsWith('#'))
|
|
21
|
+
continue;
|
|
22
|
+
const eq = t.indexOf('=');
|
|
23
|
+
if (eq <= 0)
|
|
24
|
+
continue;
|
|
25
|
+
const key = t.slice(0, eq).trim();
|
|
26
|
+
if (!wanted.has(key))
|
|
27
|
+
continue;
|
|
28
|
+
let v = t.slice(eq + 1).trim();
|
|
29
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) {
|
|
30
|
+
v = v.slice(1, -1);
|
|
31
|
+
}
|
|
32
|
+
process.env[key] = v;
|
|
33
|
+
wanted.delete(key);
|
|
34
|
+
if (wanted.size === 0)
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quadrokit/client",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Typed 4D REST client and catalog code generator for QuadroKit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"quadrokit-client": "./dist/cli.mjs"
|
|
9
|
+
"quadrokit-client": "./dist/cli.mjs",
|
|
10
|
+
"quadrokit": "./dist/cli.mjs"
|
|
10
11
|
},
|
|
11
12
|
"exports": {
|
|
12
13
|
".": {
|
|
@@ -28,7 +29,7 @@
|
|
|
28
29
|
"generate:fixture": "bun run src/cli.ts generate --url file://../../assets/catalog.json --out ../../.quadrokit/generated-demo"
|
|
29
30
|
},
|
|
30
31
|
"dependencies": {
|
|
31
|
-
"@quadrokit/shared": "^0.2.
|
|
32
|
+
"@quadrokit/shared": "^0.2.3"
|
|
32
33
|
},
|
|
33
34
|
"peerDependencies": {
|
|
34
35
|
"typescript": ">=5.4"
|