@datasynx/agentic-ai-cartography 2.3.0 → 2.4.0
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/api-bin.js +3 -3
- package/dist/{chunk-B2AKONVW.js → chunk-B4QWX7CP.js} +201 -48
- package/dist/chunk-B4QWX7CP.js.map +1 -0
- package/dist/{chunk-7VZH5PFV.js → chunk-L4OSL7I6.js} +3 -3
- package/dist/{chunk-WCR47QA2.js → chunk-QQOQBE2A.js} +16 -5
- package/dist/chunk-QQOQBE2A.js.map +1 -0
- package/dist/{chunk-7QEBFMN4.js → chunk-X5JA2UDT.js} +14 -5
- package/dist/chunk-X5JA2UDT.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/index.cjs +241 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +168 -9
- package/dist/index.d.ts +168 -9
- package/dist/index.js +232 -50
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +3 -3
- package/dist/{types-TJWXAQ2L.js → types-5L3AGZLG.js} +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-7QEBFMN4.js.map +0 -1
- package/dist/chunk-B2AKONVW.js.map +0 -1
- package/dist/chunk-WCR47QA2.js.map +0 -1
- /package/dist/{chunk-7VZH5PFV.js.map → chunk-L4OSL7I6.js.map} +0 -0
- /package/dist/{types-TJWXAQ2L.js.map → types-5L3AGZLG.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -156,15 +156,26 @@ var SECURITY_METADATA_KEYS = [
|
|
|
156
156
|
var DriftConfigSchema = z.object({
|
|
157
157
|
minSeverity: z.enum(SEVERITIES).default("info"),
|
|
158
158
|
sinks: z.array(z.object({
|
|
159
|
-
type: z.enum(["stdout", "webhook"]),
|
|
159
|
+
type: z.enum(["stdout", "webhook", "slack", "pagerduty", "jira"]),
|
|
160
160
|
url: z.string().url().optional(),
|
|
161
161
|
token: z.string().optional(),
|
|
162
|
-
timeoutMs: z.number().int().positive().optional()
|
|
162
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
163
|
+
routingKey: z.string().optional(),
|
|
164
|
+
email: z.string().optional(),
|
|
165
|
+
project: z.string().optional(),
|
|
166
|
+
issueType: z.string().optional()
|
|
163
167
|
})).default([{ type: "stdout" }])
|
|
164
168
|
}).superRefine((cfg, ctx) => {
|
|
165
169
|
for (const [i, s] of cfg.sinks.entries()) {
|
|
166
|
-
|
|
167
|
-
ctx.addIssue({ code: "custom", path: ["sinks", i, "url"], message:
|
|
170
|
+
const requireUrl = (msg) => {
|
|
171
|
+
if (!s.url) ctx.addIssue({ code: "custom", path: ["sinks", i, "url"], message: msg });
|
|
172
|
+
};
|
|
173
|
+
if (s.type === "webhook") requireUrl("webhook sink requires a url");
|
|
174
|
+
if (s.type === "slack") requireUrl("slack sink requires a webhook url");
|
|
175
|
+
if (s.type === "jira") {
|
|
176
|
+
requireUrl("jira sink requires a base url");
|
|
177
|
+
if (!s.email) ctx.addIssue({ code: "custom", path: ["sinks", i, "email"], message: "jira sink requires an email" });
|
|
178
|
+
if (!s.project) ctx.addIssue({ code: "custom", path: ["sinks", i, "project"], message: "jira sink requires a project key" });
|
|
168
179
|
}
|
|
169
180
|
}
|
|
170
181
|
});
|
|
@@ -1778,8 +1789,17 @@ function stripSensitive(target) {
|
|
|
1778
1789
|
const stripped = `${url.hostname}${url.port ? ":" + url.port : ""}`;
|
|
1779
1790
|
return stripped || raw;
|
|
1780
1791
|
} catch {
|
|
1781
|
-
|
|
1782
|
-
|
|
1792
|
+
let s = raw;
|
|
1793
|
+
const slash = s.indexOf("/");
|
|
1794
|
+
if (slash >= 0) s = s.slice(0, slash);
|
|
1795
|
+
const q = s.indexOf("?");
|
|
1796
|
+
if (q >= 0) s = s.slice(0, q);
|
|
1797
|
+
const at = s.indexOf("@");
|
|
1798
|
+
if (at >= 0) {
|
|
1799
|
+
const colon = s.lastIndexOf(":");
|
|
1800
|
+
if (colon > at) s = s.slice(0, at) + ":" + s.slice(colon + 1);
|
|
1801
|
+
}
|
|
1802
|
+
return s || raw;
|
|
1783
1803
|
}
|
|
1784
1804
|
}
|
|
1785
1805
|
var SCAN_ARG_PATTERNS = {
|
|
@@ -1797,7 +1817,7 @@ function assertSafeScanArg(kind, value) {
|
|
|
1797
1817
|
return value;
|
|
1798
1818
|
}
|
|
1799
1819
|
function redactSecrets(value) {
|
|
1800
|
-
return value.replace(/([a-z][a-z0-9+.-]
|
|
1820
|
+
return value.replace(/([a-z][a-z0-9+.-]{0,63}:\/\/[^:@/\s]{1,256}):[^@/\s]{1,256}@/gi, "$1:***@");
|
|
1801
1821
|
}
|
|
1802
1822
|
function redactValue(value) {
|
|
1803
1823
|
if (typeof value === "string") return redactSecrets(value);
|
|
@@ -4895,6 +4915,41 @@ var StdoutSink = class {
|
|
|
4895
4915
|
|
|
4896
4916
|
// src/sinks/webhook.ts
|
|
4897
4917
|
var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]", "::1"]);
|
|
4918
|
+
async function postJson(opts) {
|
|
4919
|
+
const doFetch = opts.fetchImpl ?? (typeof fetch === "function" ? fetch : void 0);
|
|
4920
|
+
if (!doFetch) {
|
|
4921
|
+
logWarn("sink unavailable: global fetch missing", { sink: opts.sinkName });
|
|
4922
|
+
return;
|
|
4923
|
+
}
|
|
4924
|
+
if (!opts.url) {
|
|
4925
|
+
logWarn("sink unavailable: no url configured", { sink: opts.sinkName });
|
|
4926
|
+
return;
|
|
4927
|
+
}
|
|
4928
|
+
if (!isSecureWebhookUrl(opts.url)) {
|
|
4929
|
+
logWarn("sink refused: insecure scheme (use https:// or a loopback host)", {
|
|
4930
|
+
sink: opts.sinkName,
|
|
4931
|
+
host: stripSensitive(opts.url)
|
|
4932
|
+
});
|
|
4933
|
+
return;
|
|
4934
|
+
}
|
|
4935
|
+
try {
|
|
4936
|
+
const res = await doFetch(opts.url, {
|
|
4937
|
+
method: "POST",
|
|
4938
|
+
headers: { "content-type": "application/json", ...opts.headers ?? {} },
|
|
4939
|
+
body: JSON.stringify(opts.body),
|
|
4940
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 1e4)
|
|
4941
|
+
});
|
|
4942
|
+
if (!res.ok) {
|
|
4943
|
+
logError("sink delivery failed", { sink: opts.sinkName, host: stripSensitive(opts.url), status: res.status });
|
|
4944
|
+
}
|
|
4945
|
+
} catch (err) {
|
|
4946
|
+
logError("sink delivery failed", {
|
|
4947
|
+
sink: opts.sinkName,
|
|
4948
|
+
host: stripSensitive(opts.url),
|
|
4949
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
4950
|
+
});
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4898
4953
|
function isSecureWebhookUrl(url, env = process.env) {
|
|
4899
4954
|
if (env.CARTOGRAPHY_ALLOW_INSECURE_SYNC === "1") return true;
|
|
4900
4955
|
let parsed;
|
|
@@ -4913,59 +4968,177 @@ var WebhookSink = class {
|
|
|
4913
4968
|
}
|
|
4914
4969
|
name = "webhook";
|
|
4915
4970
|
async emit(alert) {
|
|
4916
|
-
if (typeof fetch !== "function") {
|
|
4917
|
-
logWarn("webhook sink unavailable: global fetch missing", { sink: this.name });
|
|
4918
|
-
return;
|
|
4919
|
-
}
|
|
4920
4971
|
const { url, token, timeoutMs } = this.opts;
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4972
|
+
await postJson({
|
|
4973
|
+
url,
|
|
4974
|
+
body: redactValue(alert),
|
|
4975
|
+
...token ? { headers: { authorization: `Bearer ${token}` } } : {},
|
|
4976
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
4977
|
+
sinkName: this.name
|
|
4978
|
+
});
|
|
4979
|
+
}
|
|
4980
|
+
};
|
|
4981
|
+
|
|
4982
|
+
// src/sinks/providers.ts
|
|
4983
|
+
var MAX_ITEMS2 = 20;
|
|
4984
|
+
var SEVERITY_EMOJI = { info: "\u{1F7E2}", warning: "\u{1F7E1}", critical: "\u{1F534}" };
|
|
4985
|
+
function headline(alert) {
|
|
4986
|
+
const s = alert.summary;
|
|
4987
|
+
return `${s.nodesAdded}+ / ${s.nodesRemoved}- / ${s.nodesChanged}~ nodes, ${s.edgesAdded}+ / ${s.edgesRemoved}- edges`;
|
|
4988
|
+
}
|
|
4989
|
+
function itemLine(it) {
|
|
4990
|
+
const sec = it.securityFields?.length ? ` [security: ${it.securityFields.join(", ")}]` : "";
|
|
4991
|
+
const fields = it.changedFields?.length ? ` (${it.changedFields.join(", ")})` : "";
|
|
4992
|
+
return `${it.severity.toUpperCase()} \xB7 ${it.kind} \xB7 ${it.label}${fields}${sec}`;
|
|
4993
|
+
}
|
|
4994
|
+
function bodyText(alert) {
|
|
4995
|
+
const lines = alert.items.slice(0, MAX_ITEMS2).map(itemLine);
|
|
4996
|
+
const more = alert.items.length > MAX_ITEMS2 ? [`\u2026and ${alert.items.length - MAX_ITEMS2} more`] : [];
|
|
4997
|
+
return [headline(alert), "", ...lines, ...more].join("\n");
|
|
4998
|
+
}
|
|
4999
|
+
function formatSlack(alert) {
|
|
5000
|
+
const title = `${SEVERITY_EMOJI[alert.severity]} Topology drift \u2014 ${alert.severity}`;
|
|
5001
|
+
return {
|
|
5002
|
+
text: `${title}: ${headline(alert)}`,
|
|
5003
|
+
blocks: [
|
|
5004
|
+
{ type: "header", text: { type: "plain_text", text: title, emoji: true } },
|
|
5005
|
+
{ type: "section", text: { type: "mrkdwn", text: "```" + bodyText(alert) + "```" } },
|
|
5006
|
+
{ type: "context", elements: [{ type: "mrkdwn", text: `base ${alert.base.sessionId} \u2192 current ${alert.current.sessionId} \xB7 ${alert.generatedAt}` }] }
|
|
5007
|
+
]
|
|
5008
|
+
};
|
|
5009
|
+
}
|
|
5010
|
+
var PD_SEVERITY = {
|
|
5011
|
+
info: "info",
|
|
5012
|
+
warning: "warning",
|
|
5013
|
+
critical: "critical"
|
|
5014
|
+
};
|
|
5015
|
+
function formatPagerDuty(alert, routingKey) {
|
|
5016
|
+
return {
|
|
5017
|
+
routing_key: routingKey,
|
|
5018
|
+
event_action: "trigger",
|
|
5019
|
+
// Stable per base→current pair so repeated alerts for the same delta de-duplicate.
|
|
5020
|
+
dedup_key: `cartograph-drift:${alert.base.sessionId}:${alert.current.sessionId}`,
|
|
5021
|
+
payload: {
|
|
5022
|
+
summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,
|
|
5023
|
+
source: "cartograph",
|
|
5024
|
+
severity: PD_SEVERITY[alert.severity],
|
|
5025
|
+
timestamp: alert.generatedAt,
|
|
5026
|
+
custom_details: {
|
|
5027
|
+
summary: alert.summary,
|
|
5028
|
+
items: alert.items.slice(0, MAX_ITEMS2).map((it) => ({
|
|
5029
|
+
kind: it.kind,
|
|
5030
|
+
ref: it.ref,
|
|
5031
|
+
severity: it.severity,
|
|
5032
|
+
...it.changedFields ? { changedFields: it.changedFields } : {},
|
|
5033
|
+
...it.securityFields ? { securityFields: it.securityFields } : {}
|
|
5034
|
+
}))
|
|
4944
5035
|
}
|
|
4945
|
-
} catch (err) {
|
|
4946
|
-
logError("webhook sink failed", {
|
|
4947
|
-
sink: this.name,
|
|
4948
|
-
host: stripSensitive(url),
|
|
4949
|
-
reason: err instanceof Error ? err.message : String(err)
|
|
4950
|
-
});
|
|
4951
5036
|
}
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
function formatJira(alert, opts) {
|
|
5040
|
+
return {
|
|
5041
|
+
fields: {
|
|
5042
|
+
project: { key: opts.project },
|
|
5043
|
+
issuetype: { name: opts.issueType ?? "Task" },
|
|
5044
|
+
summary: `Cartograph topology drift (${alert.severity}): ${headline(alert)}`,
|
|
5045
|
+
description: bodyText(alert) + `
|
|
5046
|
+
|
|
5047
|
+
base ${alert.base.sessionId} \u2192 current ${alert.current.sessionId}
|
|
5048
|
+
generated ${alert.generatedAt}`
|
|
5049
|
+
}
|
|
5050
|
+
};
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
// src/sinks/provider-sink.ts
|
|
5054
|
+
var PAGERDUTY_ENQUEUE_URL = "https://events.pagerduty.com/v2/enqueue";
|
|
5055
|
+
function deliver(name, url, body, opts, headers) {
|
|
5056
|
+
return postJson({
|
|
5057
|
+
url,
|
|
5058
|
+
body,
|
|
5059
|
+
sinkName: name,
|
|
5060
|
+
...headers ? { headers } : {},
|
|
5061
|
+
...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
|
|
5062
|
+
...opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}
|
|
5063
|
+
});
|
|
5064
|
+
}
|
|
5065
|
+
var SlackSink = class {
|
|
5066
|
+
constructor(opts) {
|
|
5067
|
+
this.opts = opts;
|
|
5068
|
+
}
|
|
5069
|
+
name = "slack";
|
|
5070
|
+
async emit(alert) {
|
|
5071
|
+
await deliver(this.name, this.opts.url, formatSlack(redactValue(alert)), this.opts);
|
|
5072
|
+
}
|
|
5073
|
+
};
|
|
5074
|
+
var PagerDutySink = class {
|
|
5075
|
+
constructor(opts) {
|
|
5076
|
+
this.opts = opts;
|
|
5077
|
+
}
|
|
5078
|
+
name = "pagerduty";
|
|
5079
|
+
async emit(alert) {
|
|
5080
|
+
const body = formatPagerDuty(redactValue(alert), this.opts.routingKey);
|
|
5081
|
+
await deliver(this.name, this.opts.url || PAGERDUTY_ENQUEUE_URL, body, this.opts);
|
|
5082
|
+
}
|
|
5083
|
+
};
|
|
5084
|
+
var JiraSink = class {
|
|
5085
|
+
constructor(opts) {
|
|
5086
|
+
this.opts = opts;
|
|
5087
|
+
}
|
|
5088
|
+
name = "jira";
|
|
5089
|
+
async emit(alert) {
|
|
5090
|
+
const body = formatJira(redactValue(alert), {
|
|
5091
|
+
project: this.opts.project,
|
|
5092
|
+
...this.opts.issueType ? { issueType: this.opts.issueType } : {}
|
|
5093
|
+
});
|
|
5094
|
+
const auth = Buffer.from(`${this.opts.email}:${this.opts.token}`).toString("base64");
|
|
5095
|
+
const base = this.opts.url.replace(/\/+$/, "");
|
|
5096
|
+
await deliver(this.name, `${base}/rest/api/2/issue`, body, this.opts, { authorization: `Basic ${auth}` });
|
|
4952
5097
|
}
|
|
4953
5098
|
};
|
|
4954
5099
|
|
|
4955
5100
|
// src/sinks/index.ts
|
|
4956
5101
|
function buildSinks(drift) {
|
|
4957
5102
|
const configs = drift?.sinks && drift.sinks.length > 0 ? drift.sinks : [{ type: "stdout" }];
|
|
5103
|
+
const envSecret = process.env.CARTOGRAPHY_DRIFT_TOKEN;
|
|
4958
5104
|
const sinks = [];
|
|
4959
5105
|
for (const s of configs) {
|
|
4960
|
-
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
|
|
4968
|
-
|
|
5106
|
+
const timeoutMs = s.timeoutMs;
|
|
5107
|
+
switch (s.type) {
|
|
5108
|
+
case "webhook":
|
|
5109
|
+
if (!s.url) {
|
|
5110
|
+
logWarn("drift sink skipped: webhook requires a url", { sink: s.type });
|
|
5111
|
+
break;
|
|
5112
|
+
}
|
|
5113
|
+
sinks.push(new WebhookSink({ url: s.url, token: s.token ?? envSecret, timeoutMs }));
|
|
5114
|
+
break;
|
|
5115
|
+
case "slack":
|
|
5116
|
+
if (!s.url) {
|
|
5117
|
+
logWarn("drift sink skipped: slack requires a webhook url", { sink: s.type });
|
|
5118
|
+
break;
|
|
5119
|
+
}
|
|
5120
|
+
sinks.push(new SlackSink({ url: s.url, timeoutMs }));
|
|
5121
|
+
break;
|
|
5122
|
+
case "pagerduty": {
|
|
5123
|
+
const routingKey = s.routingKey ?? s.token ?? envSecret;
|
|
5124
|
+
if (!routingKey) {
|
|
5125
|
+
logWarn("drift sink skipped: pagerduty requires a routingKey (or CARTOGRAPHY_DRIFT_TOKEN)", { sink: s.type });
|
|
5126
|
+
break;
|
|
5127
|
+
}
|
|
5128
|
+
sinks.push(new PagerDutySink({ url: s.url ?? PAGERDUTY_ENQUEUE_URL, routingKey, timeoutMs }));
|
|
5129
|
+
break;
|
|
5130
|
+
}
|
|
5131
|
+
case "jira": {
|
|
5132
|
+
const token = s.token ?? envSecret;
|
|
5133
|
+
if (!s.url || !s.email || !s.project || !token) {
|
|
5134
|
+
logWarn("drift sink skipped: jira requires url, email, project and a token", { sink: s.type });
|
|
5135
|
+
break;
|
|
5136
|
+
}
|
|
5137
|
+
sinks.push(new JiraSink({ url: s.url, email: s.email, token, project: s.project, issueType: s.issueType, timeoutMs }));
|
|
5138
|
+
break;
|
|
5139
|
+
}
|
|
5140
|
+
default:
|
|
5141
|
+
sinks.push(new StdoutSink());
|
|
4969
5142
|
}
|
|
4970
5143
|
}
|
|
4971
5144
|
return sinks.length > 0 ? sinks : [new StdoutSink()];
|
|
@@ -5374,7 +5547,7 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
|
|
|
5374
5547
|
|
|
5375
5548
|
// src/mcp/server.ts
|
|
5376
5549
|
var SERVER_NAME = "cartography";
|
|
5377
|
-
var SERVER_VERSION = "2.
|
|
5550
|
+
var SERVER_VERSION = "2.4.0";
|
|
5378
5551
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
5379
5552
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
5380
5553
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -11130,14 +11303,17 @@ export {
|
|
|
11130
11303
|
INGEST_SCHEMA_VERSION,
|
|
11131
11304
|
IngestEnvelopeSchema,
|
|
11132
11305
|
InvalidTenantError,
|
|
11306
|
+
JiraSink,
|
|
11133
11307
|
LOOPBACK_HOSTS2 as LOOPBACK_HOSTS,
|
|
11134
11308
|
MCP_BIN,
|
|
11135
11309
|
NotFoundError,
|
|
11136
11310
|
PACKAGE_NAME,
|
|
11311
|
+
PAGERDUTY_ENQUEUE_URL,
|
|
11137
11312
|
PERSONAL,
|
|
11138
11313
|
PORT_MAP,
|
|
11139
11314
|
PRIVATE_IP,
|
|
11140
11315
|
PUSH_SCHEMA_VERSION,
|
|
11316
|
+
PagerDutySink,
|
|
11141
11317
|
ProviderRegistry,
|
|
11142
11318
|
RELATION_TO_DIRECTION,
|
|
11143
11319
|
RuleCheckSchema,
|
|
@@ -11149,6 +11325,7 @@ export {
|
|
|
11149
11325
|
ScannerRegistry,
|
|
11150
11326
|
ScannerShape,
|
|
11151
11327
|
SharingLevelSchema,
|
|
11328
|
+
SlackSink,
|
|
11152
11329
|
SqliteQueryBackend,
|
|
11153
11330
|
SqliteStoreBackend,
|
|
11154
11331
|
StdoutSink,
|
|
@@ -11233,6 +11410,9 @@ export {
|
|
|
11233
11410
|
filterBySeverity,
|
|
11234
11411
|
findAnonViolations,
|
|
11235
11412
|
formatComplianceText,
|
|
11413
|
+
formatJira,
|
|
11414
|
+
formatPagerDuty,
|
|
11415
|
+
formatSlack,
|
|
11236
11416
|
generateDependencyMermaid,
|
|
11237
11417
|
generateDiffMermaid,
|
|
11238
11418
|
generateTopologyMermaid,
|
|
@@ -11255,6 +11435,7 @@ export {
|
|
|
11255
11435
|
isPersonalHost,
|
|
11256
11436
|
isReadOnlyCommand,
|
|
11257
11437
|
isRemembered,
|
|
11438
|
+
isSecureWebhookUrl,
|
|
11258
11439
|
k8sScanner,
|
|
11259
11440
|
keyMetaOf,
|
|
11260
11441
|
layoutClusters,
|
|
@@ -11293,6 +11474,7 @@ export {
|
|
|
11293
11474
|
pixelToHex,
|
|
11294
11475
|
planInstall,
|
|
11295
11476
|
portsScanner,
|
|
11477
|
+
postJson,
|
|
11296
11478
|
previewShare,
|
|
11297
11479
|
pseudonymize,
|
|
11298
11480
|
pseudonymizeFragment,
|