@undefineds.co/xpod 0.3.15 → 0.3.17
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/config/local.json +5 -5
- package/config/xpod.json +24 -10
- package/dist/cli/commands/auth.d.ts +1 -0
- package/dist/cli/commands/auth.js +117 -37
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/login.js +16 -23
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +2 -0
- package/dist/cli/commands/logs.js +20 -5
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/obj.d.ts +44 -0
- package/dist/cli/commands/obj.js +1059 -0
- package/dist/cli/commands/obj.js.map +1 -0
- package/dist/cli/commands/rdf.d.ts +14 -0
- package/dist/cli/commands/rdf.js +235 -0
- package/dist/cli/commands/rdf.js.map +1 -0
- package/dist/cli/commands/resource.d.ts +31 -0
- package/dist/cli/commands/resource.js +191 -0
- package/dist/cli/commands/resource.js.map +1 -0
- package/dist/cli/commands/secret.d.ts +36 -0
- package/dist/cli/commands/secret.js +285 -0
- package/dist/cli/commands/secret.js.map +1 -0
- package/dist/cli/commands/server.d.ts +11 -0
- package/dist/cli/commands/server.js +168 -0
- package/dist/cli/commands/server.js.map +1 -0
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.js +5 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +21 -6
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.js +40 -6
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/index.js +23 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/auth-context.d.ts +24 -0
- package/dist/cli/lib/auth-context.js +70 -0
- package/dist/cli/lib/auth-context.js.map +1 -0
- package/dist/cli/lib/css-account.js +29 -2
- package/dist/cli/lib/css-account.js.map +1 -1
- package/dist/cli/lib/output.d.ts +23 -0
- package/dist/cli/lib/output.js +63 -0
- package/dist/cli/lib/output.js.map +1 -0
- package/dist/cli/lib/resource.d.ts +29 -0
- package/dist/cli/lib/resource.js +114 -0
- package/dist/cli/lib/resource.js.map +1 -0
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.d.ts +11 -10
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +13 -24
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld +4 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.d.ts +8 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.js +10 -6
- package/dist/identity/oidc/AutoDetectOidcHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectOidcHandler.jsonld +3 -3
- package/dist/runtime/bootstrap.js +7 -0
- package/dist/runtime/bootstrap.js.map +1 -1
- package/dist/runtime/css-process.js +7 -0
- package/dist/runtime/css-process.js.map +1 -1
- package/dist/storage/accessors/MixDataAccessor.js +3 -0
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/accessors/QuadstoreSparqlDataAccessor.js +6 -3
- package/dist/storage/accessors/QuadstoreSparqlDataAccessor.js.map +1 -1
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js +12 -4
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.d.ts +26 -1
- package/dist/storage/quint/SqliteQuintStore.js +551 -318
- package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.jsonld +102 -2
- package/dist/storage/quint/schema.d.ts +76 -0
- package/dist/storage/quint/schema.js +13 -7
- package/dist/storage/quint/schema.js.map +1 -1
- package/dist/storage/sparql/ComunicaQuintEngine.js +16 -3
- package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/config.d.ts +0 -42
- package/dist/cli/commands/config.js +0 -289
- package/dist/cli/commands/config.js.map +0 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeBaseUrl = normalizeBaseUrl;
|
|
4
|
+
exports.resolvePodRootFromWebId = resolvePodRootFromWebId;
|
|
5
|
+
exports.getStoredAuthStatus = getStoredAuthStatus;
|
|
6
|
+
exports.requireAuthContext = requireAuthContext;
|
|
7
|
+
exports.authFetch = authFetch;
|
|
8
|
+
const solid_auth_1 = require("./solid-auth");
|
|
9
|
+
const credentials_store_1 = require("./credentials-store");
|
|
10
|
+
const output_1 = require("./output");
|
|
11
|
+
function normalizeBaseUrl(url) {
|
|
12
|
+
return url.endsWith('/') ? url : `${url}/`;
|
|
13
|
+
}
|
|
14
|
+
function resolvePodRootFromWebId(webId) {
|
|
15
|
+
const webIdUrl = new URL(webId);
|
|
16
|
+
const path = webIdUrl.pathname;
|
|
17
|
+
const profileSuffix = '/profile/card';
|
|
18
|
+
if (path.endsWith(profileSuffix)) {
|
|
19
|
+
const podPath = path.slice(0, -profileSuffix.length);
|
|
20
|
+
return `${webIdUrl.origin}${podPath.endsWith('/') ? podPath : `${podPath}/`}`;
|
|
21
|
+
}
|
|
22
|
+
const pathParts = path.split('/').filter(Boolean);
|
|
23
|
+
if (pathParts.length > 0) {
|
|
24
|
+
return `${webIdUrl.origin}/${pathParts[0]}/`;
|
|
25
|
+
}
|
|
26
|
+
return `${webIdUrl.origin}/`;
|
|
27
|
+
}
|
|
28
|
+
function getStoredAuthStatus(urlOverride) {
|
|
29
|
+
const credentials = (0, credentials_store_1.loadCredentials)();
|
|
30
|
+
if (!credentials) {
|
|
31
|
+
return { authenticated: false };
|
|
32
|
+
}
|
|
33
|
+
const baseUrl = normalizeBaseUrl(urlOverride ?? credentials.url);
|
|
34
|
+
const podRoot = resolvePodRootFromWebId(credentials.webId);
|
|
35
|
+
return {
|
|
36
|
+
authenticated: true,
|
|
37
|
+
authType: credentials.authType,
|
|
38
|
+
baseUrl,
|
|
39
|
+
webId: credentials.webId,
|
|
40
|
+
podRoot,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function requireAuthContext(options = {}) {
|
|
44
|
+
const credentials = (0, credentials_store_1.loadCredentials)();
|
|
45
|
+
if (!credentials) {
|
|
46
|
+
throw new output_1.CliCommandError('auth_required', 'No credentials found. Run `xpod auth login` first.', 2);
|
|
47
|
+
}
|
|
48
|
+
const clientCredentials = (0, credentials_store_1.getClientCredentials)(credentials);
|
|
49
|
+
if (!clientCredentials) {
|
|
50
|
+
throw new output_1.CliCommandError('auth_unsupported', 'Stored OAuth credentials are not supported for CLI resource operations yet. Run `xpod auth login` to create client credentials.', 2);
|
|
51
|
+
}
|
|
52
|
+
const baseUrl = normalizeBaseUrl(options.url ?? credentials.url);
|
|
53
|
+
const tokenResult = await (0, solid_auth_1.getAccessToken)(clientCredentials.clientId, clientCredentials.clientSecret, baseUrl);
|
|
54
|
+
if (!tokenResult) {
|
|
55
|
+
throw new output_1.CliCommandError('auth_failed', 'Failed to obtain an access token. Run `xpod auth login` again.', 2);
|
|
56
|
+
}
|
|
57
|
+
const podRoot = resolvePodRootFromWebId(credentials.webId);
|
|
58
|
+
return {
|
|
59
|
+
baseUrl,
|
|
60
|
+
webId: credentials.webId,
|
|
61
|
+
podRoot,
|
|
62
|
+
baseIri: podRoot,
|
|
63
|
+
accessToken: tokenResult.accessToken,
|
|
64
|
+
credentials,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function authFetch(context, url, init) {
|
|
68
|
+
return (0, solid_auth_1.authenticatedFetch)(url, context.accessToken, init);
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=auth-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../../src/cli/lib/auth-context.ts"],"names":[],"mappings":"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","sourcesContent":["import { getAccessToken, authenticatedFetch } from './solid-auth';\nimport {\n getClientCredentials,\n loadCredentials,\n type StoredCredentials,\n} from './credentials-store';\nimport { CliCommandError } from './output';\n\nexport interface CliAuthContext {\n baseUrl: string;\n webId: string;\n podRoot: string;\n baseIri: string;\n accessToken: string;\n credentials: StoredCredentials;\n}\n\nexport interface AuthStatus {\n authenticated: boolean;\n authType?: string;\n baseUrl?: string;\n webId?: string;\n podRoot?: string;\n}\n\nexport function normalizeBaseUrl(url: string): string {\n return url.endsWith('/') ? url : `${url}/`;\n}\n\nexport function resolvePodRootFromWebId(webId: string): string {\n const webIdUrl = new URL(webId);\n const path = webIdUrl.pathname;\n const profileSuffix = '/profile/card';\n if (path.endsWith(profileSuffix)) {\n const podPath = path.slice(0, -profileSuffix.length);\n return `${webIdUrl.origin}${podPath.endsWith('/') ? podPath : `${podPath}/`}`;\n }\n\n const pathParts = path.split('/').filter(Boolean);\n if (pathParts.length > 0) {\n return `${webIdUrl.origin}/${pathParts[0]}/`;\n }\n return `${webIdUrl.origin}/`;\n}\n\nexport function getStoredAuthStatus(urlOverride?: string): AuthStatus {\n const credentials = loadCredentials();\n if (!credentials) {\n return { authenticated: false };\n }\n\n const baseUrl = normalizeBaseUrl(urlOverride ?? credentials.url);\n const podRoot = resolvePodRootFromWebId(credentials.webId);\n return {\n authenticated: true,\n authType: credentials.authType,\n baseUrl,\n webId: credentials.webId,\n podRoot,\n };\n}\n\nexport async function requireAuthContext(options: {\n url?: string;\n json?: boolean;\n} = {}): Promise<CliAuthContext> {\n const credentials = loadCredentials();\n if (!credentials) {\n throw new CliCommandError(\n 'auth_required',\n 'No credentials found. Run `xpod auth login` first.',\n 2,\n );\n }\n\n const clientCredentials = getClientCredentials(credentials);\n if (!clientCredentials) {\n throw new CliCommandError(\n 'auth_unsupported',\n 'Stored OAuth credentials are not supported for CLI resource operations yet. Run `xpod auth login` to create client credentials.',\n 2,\n );\n }\n\n const baseUrl = normalizeBaseUrl(options.url ?? credentials.url);\n const tokenResult = await getAccessToken(\n clientCredentials.clientId,\n clientCredentials.clientSecret,\n baseUrl,\n );\n if (!tokenResult) {\n throw new CliCommandError(\n 'auth_failed',\n 'Failed to obtain an access token. Run `xpod auth login` again.',\n 2,\n );\n }\n\n const podRoot = resolvePodRootFromWebId(credentials.webId);\n return {\n baseUrl,\n webId: credentials.webId,\n podRoot,\n baseIri: podRoot,\n accessToken: tokenResult.accessToken,\n credentials,\n };\n}\n\nexport async function authFetch(\n context: CliAuthContext,\n url: string,\n init?: RequestInit,\n): Promise<Response> {\n return authenticatedFetch(url, context.accessToken, init);\n}\n"]}
|
|
@@ -17,13 +17,38 @@ function resolveBaseUrl(url) {
|
|
|
17
17
|
const raw = url ?? process.env.CSS_BASE_URL ?? 'http://localhost:3000';
|
|
18
18
|
return raw.endsWith('/') ? raw : `${raw}/`;
|
|
19
19
|
}
|
|
20
|
+
function accountTokenHeaders(token) {
|
|
21
|
+
return {
|
|
22
|
+
Accept: 'application/json',
|
|
23
|
+
Authorization: `CSS-Account-Token ${token}`,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async function fetchWebIdLinks(token, webIdUrl) {
|
|
27
|
+
if (!webIdUrl) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const res = await fetch(webIdUrl, {
|
|
32
|
+
headers: accountTokenHeaders(token),
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok)
|
|
35
|
+
return {};
|
|
36
|
+
const data = (await res.json());
|
|
37
|
+
return data.webIds ?? data.webIdLinks ?? {};
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
20
43
|
/**
|
|
21
44
|
* Check if the CSS server is reachable.
|
|
22
45
|
*/
|
|
23
46
|
async function checkServer(baseUrl) {
|
|
47
|
+
const base = resolveBaseUrl(baseUrl);
|
|
24
48
|
try {
|
|
25
|
-
const res = await fetch(`${
|
|
49
|
+
const res = await fetch(`${base}.well-known/openid-configuration`, {
|
|
26
50
|
headers: { Accept: 'application/json' },
|
|
51
|
+
signal: AbortSignal.timeout(5_000),
|
|
27
52
|
});
|
|
28
53
|
return res.ok;
|
|
29
54
|
}
|
|
@@ -93,13 +118,15 @@ async function getAccountData(token, baseUrl) {
|
|
|
93
118
|
if (!res.ok)
|
|
94
119
|
return null;
|
|
95
120
|
const data = (await res.json());
|
|
121
|
+
const webIds = data.webIds ?? await fetchWebIdLinks(token, data.controls?.account?.webId);
|
|
96
122
|
return {
|
|
97
123
|
controls: {
|
|
98
124
|
pod: data.controls?.account?.pod,
|
|
99
125
|
clientCredentials: data.controls?.account?.clientCredentials,
|
|
126
|
+
webId: data.controls?.account?.webId ? [data.controls.account.webId] : undefined,
|
|
100
127
|
},
|
|
101
128
|
pods: data.pods ?? {},
|
|
102
|
-
webIds
|
|
129
|
+
webIds,
|
|
103
130
|
clientCredentials: data.clientCredentials ?? {},
|
|
104
131
|
};
|
|
105
132
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"css-account.js","sourceRoot":"","sources":["../../../src/cli/lib/css-account.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CSS Account API helpers for CLI commands.\n *\n * Wraps the CSS .account/* endpoints used by login, credential management, etc.\n */\n\nexport interface AccountControls {\n pod?: string;\n clientCredentials?: string;\n webId?: string[];\n}\n\nexport interface AccountData {\n controls: AccountControls;\n pods: Record<string, string>;\n webIds: Record<string, string>;\n clientCredentials: Record<string, string>;\n}\n\nexport interface ClientCredential {\n id: string;\n secret?: string;\n label?: string;\n webId?: string;\n}\n\nfunction resolveBaseUrl(url?: string): string {\n const raw = url ?? process.env.CSS_BASE_URL ?? 'http://localhost:3000';\n return raw.endsWith('/') ? raw : `${raw}/`;\n}\n\n/**\n * Check if the CSS server is reachable.\n */\nexport async function checkServer(baseUrl?: string): Promise<boolean> {\n try {\n const res = await fetch(`${resolveBaseUrl(baseUrl)}.account/`, {\n headers: { Accept: 'application/json' },\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Login with email/password, returns a CSS account token.\n */\nexport async function login(\n email: string,\n password: string,\n baseUrl?: string,\n): Promise<string | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/login/password/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ email, password }),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { authorization?: string };\n return data.authorization ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch account controls (endpoints for pod/credential management).\n */\nexport async function getAccountControls(\n token: string,\n baseUrl?: string,\n): Promise<AccountControls | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as {\n controls?: {\n account?: {\n pod?: string;\n clientCredentials?: string;\n webId?: string;\n };\n };\n };\n return {\n pod: data.controls?.account?.pod,\n clientCredentials: data.controls?.account?.clientCredentials,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch full account data including pods, webIds, and credentials.\n */\nexport async function getAccountData(\n token: string,\n baseUrl?: string,\n): Promise<AccountData | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as {\n controls?: {\n account?: {\n pod?: string;\n clientCredentials?: string;\n webId?: string;\n };\n };\n pods?: Record<string, string>;\n webIds?: Record<string, string>;\n clientCredentials?: Record<string, string>;\n };\n return {\n controls: {\n pod: data.controls?.account?.pod,\n clientCredentials: data.controls?.account?.clientCredentials,\n },\n pods: data.pods ?? {},\n webIds: data.webIds ?? {},\n clientCredentials: data.clientCredentials ?? {},\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new pod for the account.\n */\nexport async function createPod(\n token: string,\n podEndpoint: string,\n podName: string,\n): Promise<{ podUrl: string; webId: string } | null> {\n try {\n const res = await fetch(podEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n body: JSON.stringify({ name: podName }),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { pod?: string; webId?: string };\n if (!data.pod || !data.webId) return null;\n return { podUrl: data.pod, webId: data.webId };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new client credential bound to a WebID.\n */\nexport async function createClientCredentials(\n token: string,\n credentialsUrl: string,\n webId: string,\n name?: string,\n): Promise<ClientCredential | null> {\n try {\n const res = await fetch(credentialsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n body: JSON.stringify({\n name: name ?? `xpod-cli-${Date.now()}`,\n webId,\n }),\n });\n if (!res.ok) return null;\n return (await res.json()) as ClientCredential;\n } catch {\n return null;\n }\n}\n\n/**\n * List all client credentials for the account.\n */\nexport async function listClientCredentials(\n token: string,\n baseUrl?: string,\n): Promise<ClientCredential[]> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return [];\n const data = (await res.json()) as {\n controls?: {\n account?: {\n clientCredentials?: string;\n };\n };\n clientCredentials?: Record<string, string>;\n };\n\n // CSS returns clientCredentials as { [credentialUrl]: webId }\n const creds = data.clientCredentials;\n if (!creds || typeof creds !== 'object') return [];\n\n return Object.entries(creds).map(([url, webId]) => {\n // Extract credential ID from URL (last path segment)\n const id = url.split('/').filter(Boolean).pop() ?? url;\n return { id, webId: typeof webId === 'string' ? webId : undefined };\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Revoke (delete) a client credential by its ID.\n */\nexport async function revokeClientCredential(\n token: string,\n credentialId: string,\n baseUrl?: string,\n): Promise<boolean> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const url = `${base}.account/client-credentials/${credentialId}/`;\n const res = await fetch(url, {\n method: 'DELETE',\n headers: {\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"css-account.js","sourceRoot":"","sources":["../../../src/cli/lib/css-account.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * CSS Account API helpers for CLI commands.\n *\n * Wraps the CSS .account/* endpoints used by login, credential management, etc.\n */\n\nexport interface AccountControls {\n pod?: string;\n clientCredentials?: string;\n webId?: string[];\n}\n\nexport interface AccountData {\n controls: AccountControls;\n pods: Record<string, string>;\n webIds: Record<string, string>;\n clientCredentials: Record<string, string>;\n}\n\nexport interface ClientCredential {\n id: string;\n secret?: string;\n label?: string;\n webId?: string;\n}\n\nfunction resolveBaseUrl(url?: string): string {\n const raw = url ?? process.env.CSS_BASE_URL ?? 'http://localhost:3000';\n return raw.endsWith('/') ? raw : `${raw}/`;\n}\n\nfunction accountTokenHeaders(token: string): Record<string, string> {\n return {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n };\n}\n\nasync function fetchWebIdLinks(token: string, webIdUrl?: string): Promise<Record<string, string>> {\n if (!webIdUrl) {\n return {};\n }\n\n try {\n const res = await fetch(webIdUrl, {\n headers: accountTokenHeaders(token),\n });\n if (!res.ok) return {};\n const data = (await res.json()) as {\n webIds?: Record<string, string>;\n webIdLinks?: Record<string, string>;\n };\n return data.webIds ?? data.webIdLinks ?? {};\n } catch {\n return {};\n }\n}\n\n/**\n * Check if the CSS server is reachable.\n */\nexport async function checkServer(baseUrl?: string): Promise<boolean> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.well-known/openid-configuration`, {\n headers: { Accept: 'application/json' },\n signal: AbortSignal.timeout(5_000),\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n\n/**\n * Login with email/password, returns a CSS account token.\n */\nexport async function login(\n email: string,\n password: string,\n baseUrl?: string,\n): Promise<string | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/login/password/`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n },\n body: JSON.stringify({ email, password }),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { authorization?: string };\n return data.authorization ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch account controls (endpoints for pod/credential management).\n */\nexport async function getAccountControls(\n token: string,\n baseUrl?: string,\n): Promise<AccountControls | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as {\n controls?: {\n account?: {\n pod?: string;\n clientCredentials?: string;\n webId?: string;\n };\n };\n };\n return {\n pod: data.controls?.account?.pod,\n clientCredentials: data.controls?.account?.clientCredentials,\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Fetch full account data including pods, webIds, and credentials.\n */\nexport async function getAccountData(\n token: string,\n baseUrl?: string,\n): Promise<AccountData | null> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return null;\n const data = (await res.json()) as {\n controls?: {\n account?: {\n pod?: string;\n clientCredentials?: string;\n webId?: string;\n };\n };\n pods?: Record<string, string>;\n webIds?: Record<string, string>;\n clientCredentials?: Record<string, string>;\n };\n const webIds = data.webIds ?? await fetchWebIdLinks(token, data.controls?.account?.webId);\n return {\n controls: {\n pod: data.controls?.account?.pod,\n clientCredentials: data.controls?.account?.clientCredentials,\n webId: data.controls?.account?.webId ? [ data.controls.account.webId ] : undefined,\n },\n pods: data.pods ?? {},\n webIds,\n clientCredentials: data.clientCredentials ?? {},\n };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new pod for the account.\n */\nexport async function createPod(\n token: string,\n podEndpoint: string,\n podName: string,\n): Promise<{ podUrl: string; webId: string } | null> {\n try {\n const res = await fetch(podEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n body: JSON.stringify({ name: podName }),\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { pod?: string; webId?: string };\n if (!data.pod || !data.webId) return null;\n return { podUrl: data.pod, webId: data.webId };\n } catch {\n return null;\n }\n}\n\n/**\n * Create a new client credential bound to a WebID.\n */\nexport async function createClientCredentials(\n token: string,\n credentialsUrl: string,\n webId: string,\n name?: string,\n): Promise<ClientCredential | null> {\n try {\n const res = await fetch(credentialsUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n body: JSON.stringify({\n name: name ?? `xpod-cli-${Date.now()}`,\n webId,\n }),\n });\n if (!res.ok) return null;\n return (await res.json()) as ClientCredential;\n } catch {\n return null;\n }\n}\n\n/**\n * List all client credentials for the account.\n */\nexport async function listClientCredentials(\n token: string,\n baseUrl?: string,\n): Promise<ClientCredential[]> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const res = await fetch(`${base}.account/`, {\n headers: {\n Accept: 'application/json',\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n if (!res.ok) return [];\n const data = (await res.json()) as {\n controls?: {\n account?: {\n clientCredentials?: string;\n };\n };\n clientCredentials?: Record<string, string>;\n };\n\n // CSS returns clientCredentials as { [credentialUrl]: webId }\n const creds = data.clientCredentials;\n if (!creds || typeof creds !== 'object') return [];\n\n return Object.entries(creds).map(([url, webId]) => {\n // Extract credential ID from URL (last path segment)\n const id = url.split('/').filter(Boolean).pop() ?? url;\n return { id, webId: typeof webId === 'string' ? webId : undefined };\n });\n } catch {\n return [];\n }\n}\n\n/**\n * Revoke (delete) a client credential by its ID.\n */\nexport async function revokeClientCredential(\n token: string,\n credentialId: string,\n baseUrl?: string,\n): Promise<boolean> {\n const base = resolveBaseUrl(baseUrl);\n try {\n const url = `${base}.account/client-credentials/${credentialId}/`;\n const res = await fetch(url, {\n method: 'DELETE',\n headers: {\n Authorization: `CSS-Account-Token ${token}`,\n },\n });\n return res.ok;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface CliEnvelope<T = unknown> {
|
|
2
|
+
ok: boolean;
|
|
3
|
+
code: string;
|
|
4
|
+
data?: T;
|
|
5
|
+
message?: string;
|
|
6
|
+
warnings: string[];
|
|
7
|
+
items?: unknown[];
|
|
8
|
+
}
|
|
9
|
+
export declare class CliCommandError extends Error {
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly exitCode: number;
|
|
12
|
+
readonly data?: unknown;
|
|
13
|
+
constructor(code: string, message: string, exitCode?: number, data?: unknown);
|
|
14
|
+
}
|
|
15
|
+
export declare function ok<T>(data: T, code?: string, warnings?: string[]): CliEnvelope<T>;
|
|
16
|
+
export declare function fail(code: string, message: string, warnings?: string[], data?: unknown): CliEnvelope;
|
|
17
|
+
export declare function writeJson(result: CliEnvelope): void;
|
|
18
|
+
export declare function writeJsonItems(items: unknown[], code: string, warnings?: string[]): void;
|
|
19
|
+
export declare function writeJsonResult<T>(data: T, code?: string, warnings?: string[]): void;
|
|
20
|
+
export declare function isJsonMode(argv: {
|
|
21
|
+
json?: boolean;
|
|
22
|
+
}): boolean;
|
|
23
|
+
export declare function handleCliError(error: unknown, json: boolean, fallbackCode?: string): never;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CliCommandError = void 0;
|
|
4
|
+
exports.ok = ok;
|
|
5
|
+
exports.fail = fail;
|
|
6
|
+
exports.writeJson = writeJson;
|
|
7
|
+
exports.writeJsonItems = writeJsonItems;
|
|
8
|
+
exports.writeJsonResult = writeJsonResult;
|
|
9
|
+
exports.isJsonMode = isJsonMode;
|
|
10
|
+
exports.handleCliError = handleCliError;
|
|
11
|
+
class CliCommandError extends Error {
|
|
12
|
+
constructor(code, message, exitCode = 1, data) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'CliCommandError';
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.exitCode = exitCode;
|
|
17
|
+
this.data = data;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.CliCommandError = CliCommandError;
|
|
21
|
+
function ok(data, code = 'ok', warnings = []) {
|
|
22
|
+
return { ok: true, code, data, warnings };
|
|
23
|
+
}
|
|
24
|
+
function fail(code, message, warnings = [], data) {
|
|
25
|
+
return { ok: false, code, message, warnings, ...(data === undefined ? {} : { data }) };
|
|
26
|
+
}
|
|
27
|
+
function writeJson(result) {
|
|
28
|
+
console.log(JSON.stringify(result, null, 2));
|
|
29
|
+
}
|
|
30
|
+
function writeJsonItems(items, code, warnings = []) {
|
|
31
|
+
const ok = items.every((item) => {
|
|
32
|
+
if (!item || typeof item !== 'object' || !('ok' in item)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return item.ok === true;
|
|
36
|
+
});
|
|
37
|
+
writeJson({ ok, code, items, warnings });
|
|
38
|
+
}
|
|
39
|
+
function writeJsonResult(data, code = 'ok', warnings = []) {
|
|
40
|
+
writeJson(ok(data, code, warnings));
|
|
41
|
+
}
|
|
42
|
+
function isJsonMode(argv) {
|
|
43
|
+
return argv.json === true;
|
|
44
|
+
}
|
|
45
|
+
function shouldForceStructuredError(error) {
|
|
46
|
+
return error instanceof CliCommandError &&
|
|
47
|
+
error.code === 'auth_required' &&
|
|
48
|
+
(process.env.CI === 'true' || !process.stdout.isTTY);
|
|
49
|
+
}
|
|
50
|
+
function handleCliError(error, json, fallbackCode = 'error') {
|
|
51
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
52
|
+
if (json || shouldForceStructuredError(err)) {
|
|
53
|
+
if (err instanceof CliCommandError) {
|
|
54
|
+
writeJson(fail(err.code, err.message, [], err.data));
|
|
55
|
+
process.exit(err.exitCode);
|
|
56
|
+
}
|
|
57
|
+
writeJson(fail(fallbackCode, err.message));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
console.error(err.message);
|
|
61
|
+
process.exit(err instanceof CliCommandError ? err.exitCode : 1);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../../src/cli/lib/output.ts"],"names":[],"mappings":"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","sourcesContent":["export interface CliEnvelope<T = unknown> {\n ok: boolean;\n code: string;\n data?: T;\n message?: string;\n warnings: string[];\n items?: unknown[];\n}\n\nexport class CliCommandError extends Error {\n public readonly code: string;\n public readonly exitCode: number;\n public readonly data?: unknown;\n\n public constructor(code: string, message: string, exitCode = 1, data?: unknown) {\n super(message);\n this.name = 'CliCommandError';\n this.code = code;\n this.exitCode = exitCode;\n this.data = data;\n }\n}\n\nexport function ok<T>(data: T, code = 'ok', warnings: string[] = []): CliEnvelope<T> {\n return { ok: true, code, data, warnings };\n}\n\nexport function fail(code: string, message: string, warnings: string[] = [], data?: unknown): CliEnvelope {\n return { ok: false, code, message, warnings, ...(data === undefined ? {} : { data }) };\n}\n\nexport function writeJson(result: CliEnvelope): void {\n console.log(JSON.stringify(result, null, 2));\n}\n\nexport function writeJsonItems(items: unknown[], code: string, warnings: string[] = []): void {\n const ok = items.every((item) => {\n if (!item || typeof item !== 'object' || !('ok' in item)) {\n return false;\n }\n return (item as { ok?: unknown }).ok === true;\n });\n writeJson({ ok, code, items, warnings });\n}\n\nexport function writeJsonResult<T>(data: T, code = 'ok', warnings: string[] = []): void {\n writeJson(ok(data, code, warnings));\n}\n\nexport function isJsonMode(argv: { json?: boolean }): boolean {\n return argv.json === true;\n}\n\nfunction shouldForceStructuredError(error: Error): boolean {\n return error instanceof CliCommandError &&\n error.code === 'auth_required' &&\n (process.env.CI === 'true' || !process.stdout.isTTY);\n}\n\nexport function handleCliError(error: unknown, json: boolean, fallbackCode = 'error'): never {\n const err = error instanceof Error ? error : new Error(String(error));\n if (json || shouldForceStructuredError(err)) {\n if (err instanceof CliCommandError) {\n writeJson(fail(err.code, err.message, [], err.data));\n process.exit(err.exitCode);\n }\n writeJson(fail(fallbackCode, err.message));\n process.exit(1);\n }\n\n console.error(err.message);\n process.exit(err instanceof CliCommandError ? err.exitCode : 1);\n}\n"]}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CliAuthContext } from './auth-context';
|
|
2
|
+
export interface ResourceTarget {
|
|
3
|
+
input: string;
|
|
4
|
+
resourceUrl: string;
|
|
5
|
+
webId: string;
|
|
6
|
+
podRoot: string;
|
|
7
|
+
baseIri: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ResourceResponseData {
|
|
10
|
+
webId: string;
|
|
11
|
+
podRoot: string;
|
|
12
|
+
baseIri: string;
|
|
13
|
+
resourceUrl: string;
|
|
14
|
+
status: number;
|
|
15
|
+
statusText: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export declare function resolveResourceTarget(context: CliAuthContext, input: string): ResourceTarget;
|
|
19
|
+
export declare function responseHeaders(response: Response): Record<string, string>;
|
|
20
|
+
export declare function responseData(target: ResourceTarget, response: Response): ResourceResponseData;
|
|
21
|
+
export declare function ensureOk(response: Response, code: string, message: string): void;
|
|
22
|
+
export declare function contentTypeForPath(filePath: string): string;
|
|
23
|
+
export declare function readBodyFile(filePath: string): {
|
|
24
|
+
body: Buffer;
|
|
25
|
+
contentType: string;
|
|
26
|
+
};
|
|
27
|
+
export declare function fetchResource(context: CliAuthContext, target: ResourceTarget, init: RequestInit): Promise<Response>;
|
|
28
|
+
export declare function parseContainedResources(turtle: string, containerUrl: string): string[];
|
|
29
|
+
export declare function relativeToPodRoot(resourceUrl: string, podRoot: string): string;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveResourceTarget = resolveResourceTarget;
|
|
4
|
+
exports.responseHeaders = responseHeaders;
|
|
5
|
+
exports.responseData = responseData;
|
|
6
|
+
exports.ensureOk = ensureOk;
|
|
7
|
+
exports.contentTypeForPath = contentTypeForPath;
|
|
8
|
+
exports.readBodyFile = readBodyFile;
|
|
9
|
+
exports.fetchResource = fetchResource;
|
|
10
|
+
exports.parseContainedResources = parseContainedResources;
|
|
11
|
+
exports.relativeToPodRoot = relativeToPodRoot;
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const path_1 = require("path");
|
|
14
|
+
const auth_context_1 = require("./auth-context");
|
|
15
|
+
const output_1 = require("./output");
|
|
16
|
+
function resolveResourceTarget(context, input) {
|
|
17
|
+
const trimmed = input.trim();
|
|
18
|
+
if (!trimmed) {
|
|
19
|
+
throw new output_1.CliCommandError('invalid_path', 'Resource path is required.');
|
|
20
|
+
}
|
|
21
|
+
const resourceUrl = /^https?:\/\//i.test(trimmed)
|
|
22
|
+
? new URL(trimmed).toString()
|
|
23
|
+
: new URL(trimmed.replace(/^\/+/, ''), context.podRoot).toString();
|
|
24
|
+
return {
|
|
25
|
+
input,
|
|
26
|
+
resourceUrl,
|
|
27
|
+
webId: context.webId,
|
|
28
|
+
podRoot: context.podRoot,
|
|
29
|
+
baseIri: context.baseIri,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function responseHeaders(response) {
|
|
33
|
+
const headers = {};
|
|
34
|
+
response.headers.forEach((value, key) => {
|
|
35
|
+
const lower = key.toLowerCase();
|
|
36
|
+
if (lower === 'authorization' || lower === 'cookie' || lower === 'set-cookie') {
|
|
37
|
+
headers[key] = '[redacted]';
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
headers[key] = value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return headers;
|
|
44
|
+
}
|
|
45
|
+
function responseData(target, response) {
|
|
46
|
+
return {
|
|
47
|
+
webId: target.webId,
|
|
48
|
+
podRoot: target.podRoot,
|
|
49
|
+
baseIri: target.baseIri,
|
|
50
|
+
resourceUrl: target.resourceUrl,
|
|
51
|
+
status: response.status,
|
|
52
|
+
statusText: response.statusText,
|
|
53
|
+
headers: responseHeaders(response),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function ensureOk(response, code, message) {
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new output_1.CliCommandError(code, `${message}: HTTP ${response.status} ${response.statusText}`, 1, {
|
|
59
|
+
status: response.status,
|
|
60
|
+
statusText: response.statusText,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function contentTypeForPath(filePath) {
|
|
65
|
+
switch ((0, path_1.extname)(filePath).toLowerCase()) {
|
|
66
|
+
case '.ttl':
|
|
67
|
+
return 'text/turtle';
|
|
68
|
+
case '.json':
|
|
69
|
+
case '.jsonld':
|
|
70
|
+
return 'application/ld+json';
|
|
71
|
+
case '.txt':
|
|
72
|
+
case '.md':
|
|
73
|
+
return 'text/plain';
|
|
74
|
+
case '.html':
|
|
75
|
+
return 'text/html';
|
|
76
|
+
case '.png':
|
|
77
|
+
return 'image/png';
|
|
78
|
+
case '.jpg':
|
|
79
|
+
case '.jpeg':
|
|
80
|
+
return 'image/jpeg';
|
|
81
|
+
default:
|
|
82
|
+
return 'application/octet-stream';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function readBodyFile(filePath) {
|
|
86
|
+
return {
|
|
87
|
+
body: (0, fs_1.readFileSync)(filePath),
|
|
88
|
+
contentType: contentTypeForPath(filePath),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function fetchResource(context, target, init) {
|
|
92
|
+
return (0, auth_context_1.authFetch)(context, target.resourceUrl, init);
|
|
93
|
+
}
|
|
94
|
+
function parseContainedResources(turtle, containerUrl) {
|
|
95
|
+
const resources = new Set();
|
|
96
|
+
const containsPattern = /(?:ldp:contains|<http:\/\/www\.w3\.org\/ns\/ldp#contains>)\s+((?:<[^>]+>\s*,?\s*)+)/g;
|
|
97
|
+
let match;
|
|
98
|
+
while ((match = containsPattern.exec(turtle)) !== null) {
|
|
99
|
+
const block = match[1] ?? '';
|
|
100
|
+
const iriPattern = /<([^>]+)>/g;
|
|
101
|
+
let iriMatch;
|
|
102
|
+
while ((iriMatch = iriPattern.exec(block)) !== null) {
|
|
103
|
+
const iri = iriMatch[1];
|
|
104
|
+
if (iri && iri !== containerUrl) {
|
|
105
|
+
resources.add(new URL(iri, containerUrl).toString());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return Array.from(resources).sort();
|
|
110
|
+
}
|
|
111
|
+
function relativeToPodRoot(resourceUrl, podRoot) {
|
|
112
|
+
return resourceUrl.startsWith(podRoot) ? resourceUrl.slice(podRoot.length) : resourceUrl;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../../../src/cli/lib/resource.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from 'fs';\nimport { extname } from 'path';\nimport type { CliAuthContext } from './auth-context';\nimport { authFetch } from './auth-context';\nimport { CliCommandError } from './output';\n\nexport interface ResourceTarget {\n input: string;\n resourceUrl: string;\n webId: string;\n podRoot: string;\n baseIri: string;\n}\n\nexport interface ResourceResponseData {\n webId: string;\n podRoot: string;\n baseIri: string;\n resourceUrl: string;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nexport function resolveResourceTarget(context: CliAuthContext, input: string): ResourceTarget {\n const trimmed = input.trim();\n if (!trimmed) {\n throw new CliCommandError('invalid_path', 'Resource path is required.');\n }\n\n const resourceUrl = /^https?:\\/\\//i.test(trimmed)\n ? new URL(trimmed).toString()\n : new URL(trimmed.replace(/^\\/+/, ''), context.podRoot).toString();\n\n return {\n input,\n resourceUrl,\n webId: context.webId,\n podRoot: context.podRoot,\n baseIri: context.baseIri,\n };\n}\n\nexport function responseHeaders(response: Response): Record<string, string> {\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n const lower = key.toLowerCase();\n if (lower === 'authorization' || lower === 'cookie' || lower === 'set-cookie') {\n headers[key] = '[redacted]';\n } else {\n headers[key] = value;\n }\n });\n return headers;\n}\n\nexport function responseData(target: ResourceTarget, response: Response): ResourceResponseData {\n return {\n webId: target.webId,\n podRoot: target.podRoot,\n baseIri: target.baseIri,\n resourceUrl: target.resourceUrl,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders(response),\n };\n}\n\nexport function ensureOk(response: Response, code: string, message: string): void {\n if (!response.ok) {\n throw new CliCommandError(code, `${message}: HTTP ${response.status} ${response.statusText}`, 1, {\n status: response.status,\n statusText: response.statusText,\n });\n }\n}\n\nexport function contentTypeForPath(filePath: string): string {\n switch (extname(filePath).toLowerCase()) {\n case '.ttl':\n return 'text/turtle';\n case '.json':\n case '.jsonld':\n return 'application/ld+json';\n case '.txt':\n case '.md':\n return 'text/plain';\n case '.html':\n return 'text/html';\n case '.png':\n return 'image/png';\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg';\n default:\n return 'application/octet-stream';\n }\n}\n\nexport function readBodyFile(filePath: string): { body: Buffer; contentType: string } {\n return {\n body: readFileSync(filePath),\n contentType: contentTypeForPath(filePath),\n };\n}\n\nexport async function fetchResource(\n context: CliAuthContext,\n target: ResourceTarget,\n init: RequestInit,\n): Promise<Response> {\n return authFetch(context, target.resourceUrl, init);\n}\n\nexport function parseContainedResources(turtle: string, containerUrl: string): string[] {\n const resources = new Set<string>();\n const containsPattern = /(?:ldp:contains|<http:\\/\\/www\\.w3\\.org\\/ns\\/ldp#contains>)\\s+((?:<[^>]+>\\s*,?\\s*)+)/g;\n let match: RegExpExecArray | null;\n while ((match = containsPattern.exec(turtle)) !== null) {\n const block = match[1] ?? '';\n const iriPattern = /<([^>]+)>/g;\n let iriMatch: RegExpExecArray | null;\n while ((iriMatch = iriPattern.exec(block)) !== null) {\n const iri = iriMatch[1];\n if (iri && iri !== containerUrl) {\n resources.add(new URL(iri, containerUrl).toString());\n }\n }\n }\n return Array.from(resources).sort();\n}\n\nexport function relativeToPodRoot(resourceUrl: string, podRoot: string): string {\n return resourceUrl.startsWith(podRoot) ? resourceUrl.slice(podRoot.length) : resourceUrl;\n}\n"]}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { HttpHandler, type HttpHandlerInput } from '@solid/community-server';
|
|
2
2
|
export interface AutoDetectIdentityProviderHandlerOptions {
|
|
3
|
-
/** 外部 IdP 的基础 URL,如果提供则启用 SP
|
|
3
|
+
/** 外部 IdP 的基础 URL,如果提供则启用 Local SP mode. */
|
|
4
4
|
oidcIssuer?: string;
|
|
5
|
-
/**
|
|
5
|
+
/** Message used when no source handler is available. */
|
|
6
6
|
message?: string;
|
|
7
|
-
/** CSS
|
|
7
|
+
/** CSS IdentityProviderHandler that owns account, consent and WebID selection routes. */
|
|
8
8
|
source?: HttpHandler;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Auto-detect Identity Provider Handler
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Local SP mode still needs the local `/.account/*` surface: CSS keeps the
|
|
14
|
+
* OIDC interaction and the scoped WebID picker here, while token validation can
|
|
15
|
+
* trust the configured external issuer. Disabling this surface makes LinX fall
|
|
16
|
+
* back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.
|
|
16
17
|
*/
|
|
17
18
|
export declare class AutoDetectIdentityProviderHandler extends HttpHandler {
|
|
18
19
|
private readonly logger;
|
|
@@ -22,14 +23,14 @@ export declare class AutoDetectIdentityProviderHandler extends HttpHandler {
|
|
|
22
23
|
constructor(options?: AutoDetectIdentityProviderHandlerOptions);
|
|
23
24
|
/**
|
|
24
25
|
* 判断是否处理请求
|
|
25
|
-
* - SP
|
|
26
|
-
* -
|
|
26
|
+
* - Local SP mode: delegate local account/consent routes to source Handler
|
|
27
|
+
* - Standard mode: delegate to source Handler
|
|
27
28
|
*/
|
|
28
29
|
canHandle(input: HttpHandlerInput): Promise<void>;
|
|
29
30
|
/**
|
|
30
31
|
* 处理请求
|
|
31
|
-
* - SP
|
|
32
|
-
* -
|
|
32
|
+
* - Local SP mode: delegate to source Handler so consent remains scoped by SP
|
|
33
|
+
* - Standard mode: delegate to source Handler
|
|
33
34
|
*/
|
|
34
35
|
handle(input: HttpHandlerInput): Promise<void>;
|
|
35
36
|
/**
|
|
@@ -6,28 +6,29 @@ const community_server_1 = require("@solid/community-server");
|
|
|
6
6
|
/**
|
|
7
7
|
* Auto-detect Identity Provider Handler
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Local SP mode still needs the local `/.account/*` surface: CSS keeps the
|
|
10
|
+
* OIDC interaction and the scoped WebID picker here, while token validation can
|
|
11
|
+
* trust the configured external issuer. Disabling this surface makes LinX fall
|
|
12
|
+
* back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.
|
|
12
13
|
*/
|
|
13
14
|
class AutoDetectIdentityProviderHandler extends community_server_1.HttpHandler {
|
|
14
15
|
constructor(options = {}) {
|
|
15
16
|
super();
|
|
16
17
|
this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
|
|
17
18
|
this.oidcIssuer = options.oidcIssuer;
|
|
18
|
-
this.message = options.message ?? '
|
|
19
|
+
this.message = options.message ?? 'No source IdentityProviderHandler configured';
|
|
19
20
|
this.source = options.source;
|
|
20
21
|
if (this.oidcIssuer) {
|
|
21
|
-
this.logger.info(`SP mode enabled:
|
|
22
|
+
this.logger.info(`Local SP mode enabled: account and consent routes stay local, external issuer: ${this.oidcIssuer}`);
|
|
22
23
|
}
|
|
23
24
|
else {
|
|
24
|
-
this.logger.info('Standard mode enabled
|
|
25
|
+
this.logger.info('Standard mode enabled: delegating identity routes to source IdentityProviderHandler');
|
|
25
26
|
}
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* 判断是否处理请求
|
|
29
|
-
* - SP
|
|
30
|
-
* -
|
|
30
|
+
* - Local SP mode: delegate local account/consent routes to source Handler
|
|
31
|
+
* - Standard mode: delegate to source Handler
|
|
31
32
|
*/
|
|
32
33
|
async canHandle(input) {
|
|
33
34
|
const url = input.request.url ?? '';
|
|
@@ -35,36 +36,24 @@ class AutoDetectIdentityProviderHandler extends community_server_1.HttpHandler {
|
|
|
35
36
|
if (!this.isIdpPath(url)) {
|
|
36
37
|
throw new community_server_1.NotImplementedHttpError('Not an IdP request');
|
|
37
38
|
}
|
|
38
|
-
// SP 模式:接受 IdP 路径请求,在 handle 中返回 404
|
|
39
|
-
if (this.oidcIssuer) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// 标准模式:委托给 source Handler
|
|
43
39
|
if (this.source) {
|
|
44
40
|
await this.source.canHandle(input);
|
|
45
41
|
}
|
|
46
42
|
else {
|
|
47
|
-
throw new community_server_1.NotImplementedHttpError(
|
|
43
|
+
throw new community_server_1.NotImplementedHttpError(this.message);
|
|
48
44
|
}
|
|
49
45
|
}
|
|
50
46
|
/**
|
|
51
47
|
* 处理请求
|
|
52
|
-
* - SP
|
|
53
|
-
* -
|
|
48
|
+
* - Local SP mode: delegate to source Handler so consent remains scoped by SP
|
|
49
|
+
* - Standard mode: delegate to source Handler
|
|
54
50
|
*/
|
|
55
51
|
async handle(input) {
|
|
56
|
-
if (this.oidcIssuer) {
|
|
57
|
-
const { response } = input;
|
|
58
|
-
response.writeHead(404, { 'Content-Type': 'application/json' });
|
|
59
|
-
response.end(JSON.stringify({ error: this.message }));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
// 标准模式:委托给 source Handler
|
|
63
52
|
if (this.source) {
|
|
64
53
|
await this.source.handle(input);
|
|
65
54
|
}
|
|
66
55
|
else {
|
|
67
|
-
throw new community_server_1.NotImplementedHttpError(
|
|
56
|
+
throw new community_server_1.NotImplementedHttpError(this.message);
|
|
68
57
|
}
|
|
69
58
|
}
|
|
70
59
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutoDetectIdentityProviderHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/AutoDetectIdentityProviderHandler.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,8DAIiC;AAWjC
|
|
1
|
+
{"version":3,"file":"AutoDetectIdentityProviderHandler.js","sourceRoot":"","sources":["../../../src/identity/oidc/AutoDetectIdentityProviderHandler.ts"],"names":[],"mappings":";;;AAAA,iEAAqD;AACrD,8DAIiC;AAWjC;;;;;;;GAOG;AACH,MAAa,iCAAkC,SAAQ,8BAAW;IAMhE,YAAY,UAAoD,EAAE;QAChE,KAAK,EAAE,CAAC;QANO,WAAM,GAAG,IAAA,oCAAY,EAAC,IAAI,CAAC,CAAC;QAO3C,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,8CAA8C,CAAC;QACjF,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAE7B,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kFAAkF,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QACxH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;IAED;;;;OAIG;IACa,KAAK,CAAC,SAAS,CAAC,KAAuB;QACrD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,CAAC;QAEpC,eAAe;QACf,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,0CAAuB,CAAC,oBAAoB,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,0CAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACa,KAAK,CAAC,MAAM,CAAC,KAAuB;QAClD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,0CAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,GAAW;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,CACL,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;YAC5B,QAAQ,CAAC,UAAU,CAAC,YAAY,CAAC;YACjC,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,QAAQ;YACrB,QAAQ,KAAK,SAAS,CACvB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,WAAW,CAAC,GAAW;QAC7B,IAAI,CAAC;YACH,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;QACnD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;CACF;AA5ED,8EA4EC","sourcesContent":["import { getLoggerFor } from 'global-logger-factory';\nimport {\n HttpHandler,\n type HttpHandlerInput,\n NotImplementedHttpError,\n} from '@solid/community-server';\n\nexport interface AutoDetectIdentityProviderHandlerOptions {\n /** 外部 IdP 的基础 URL,如果提供则启用 Local SP mode. */\n oidcIssuer?: string;\n /** Message used when no source handler is available. */\n message?: string;\n /** CSS IdentityProviderHandler that owns account, consent and WebID selection routes. */\n source?: HttpHandler;\n}\n\n/**\n * Auto-detect Identity Provider Handler\n *\n * Local SP mode still needs the local `/.account/*` surface: CSS keeps the\n * OIDC interaction and the scoped WebID picker here, while token validation can\n * trust the configured external issuer. Disabling this surface makes LinX fall\n * back to the Cloud issuer and lets Cloud Pods leak into a Local login flow.\n */\nexport class AutoDetectIdentityProviderHandler extends HttpHandler {\n private readonly logger = getLoggerFor(this);\n private readonly oidcIssuer?: string;\n private readonly message: string;\n private readonly source?: HttpHandler;\n\n constructor(options: AutoDetectIdentityProviderHandlerOptions = {}) {\n super();\n this.oidcIssuer = options.oidcIssuer;\n this.message = options.message ?? 'No source IdentityProviderHandler configured';\n this.source = options.source;\n\n if (this.oidcIssuer) {\n this.logger.info(`Local SP mode enabled: account and consent routes stay local, external issuer: ${this.oidcIssuer}`);\n } else {\n this.logger.info('Standard mode enabled: delegating identity routes to source IdentityProviderHandler');\n }\n }\n\n /**\n * 判断是否处理请求\n * - Local SP mode: delegate local account/consent routes to source Handler\n * - Standard mode: delegate to source Handler\n */\n public override async canHandle(input: HttpHandlerInput): Promise<void> {\n const url = input.request.url ?? '';\n\n // 检查是否是 IdP 路径\n if (!this.isIdpPath(url)) {\n throw new NotImplementedHttpError('Not an IdP request');\n }\n\n if (this.source) {\n await this.source.canHandle(input);\n } else {\n throw new NotImplementedHttpError(this.message);\n }\n }\n\n /**\n * 处理请求\n * - Local SP mode: delegate to source Handler so consent remains scoped by SP\n * - Standard mode: delegate to source Handler\n */\n public override async handle(input: HttpHandlerInput): Promise<void> {\n if (this.source) {\n await this.source.handle(input);\n } else {\n throw new NotImplementedHttpError(this.message);\n }\n }\n\n /**\n * 检查是否是 IdP 路径\n */\n private isIdpPath(url: string): boolean {\n const pathname = this.getPathname(url);\n return (\n pathname.startsWith('/idp/') ||\n pathname.startsWith('/.account/') ||\n pathname === '/register' ||\n pathname === '/login' ||\n pathname === '/logout'\n );\n }\n\n /**\n * 从 URL 提取 pathname\n */\n private getPathname(url: string): string {\n try {\n return new URL(url, 'http://localhost').pathname;\n } catch {\n return url.split('?')[0];\n }\n }\n}\n"]}
|