@character-foundry/character-foundry 0.4.3 → 0.5.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.
Files changed (63) hide show
  1. package/dist/app-framework.cjs +12 -3
  2. package/dist/app-framework.cjs.map +1 -1
  3. package/dist/app-framework.d.cts +9 -1
  4. package/dist/app-framework.d.ts +9 -1
  5. package/dist/app-framework.js +12 -3
  6. package/dist/app-framework.js.map +1 -1
  7. package/dist/charx.cjs +85 -52
  8. package/dist/charx.cjs.map +1 -1
  9. package/dist/charx.d.cts +22 -22
  10. package/dist/charx.d.ts +22 -22
  11. package/dist/charx.js +85 -52
  12. package/dist/charx.js.map +1 -1
  13. package/dist/exporter.cjs +104 -54
  14. package/dist/exporter.cjs.map +1 -1
  15. package/dist/exporter.d.cts +19 -19
  16. package/dist/exporter.d.ts +19 -19
  17. package/dist/exporter.js +104 -54
  18. package/dist/exporter.js.map +1 -1
  19. package/dist/federation.cjs +104 -36
  20. package/dist/federation.cjs.map +1 -1
  21. package/dist/federation.d.cts +54 -19
  22. package/dist/federation.d.ts +54 -19
  23. package/dist/federation.js +104 -36
  24. package/dist/federation.js.map +1 -1
  25. package/dist/index.cjs +104 -54
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +29 -29
  28. package/dist/index.d.ts +29 -29
  29. package/dist/index.js +104 -54
  30. package/dist/index.js.map +1 -1
  31. package/dist/loader.cjs +171 -31
  32. package/dist/loader.cjs.map +1 -1
  33. package/dist/loader.d.cts +37 -23
  34. package/dist/loader.d.ts +37 -23
  35. package/dist/loader.js +171 -31
  36. package/dist/loader.js.map +1 -1
  37. package/dist/lorebook.d.cts +23 -23
  38. package/dist/lorebook.d.ts +23 -23
  39. package/dist/normalizer.cjs +72 -18
  40. package/dist/normalizer.cjs.map +1 -1
  41. package/dist/normalizer.d.cts +37 -37
  42. package/dist/normalizer.d.ts +37 -37
  43. package/dist/normalizer.js +72 -18
  44. package/dist/normalizer.js.map +1 -1
  45. package/dist/png.cjs +72 -18
  46. package/dist/png.cjs.map +1 -1
  47. package/dist/png.d.cts +25 -25
  48. package/dist/png.d.ts +25 -25
  49. package/dist/png.js +72 -18
  50. package/dist/png.js.map +1 -1
  51. package/dist/schemas.cjs +80 -23
  52. package/dist/schemas.cjs.map +1 -1
  53. package/dist/schemas.d.cts +85 -67
  54. package/dist/schemas.d.ts +85 -67
  55. package/dist/schemas.js +80 -23
  56. package/dist/schemas.js.map +1 -1
  57. package/dist/voxta.cjs +91 -20
  58. package/dist/voxta.cjs.map +1 -1
  59. package/dist/voxta.d.cts +23 -23
  60. package/dist/voxta.d.ts +23 -23
  61. package/dist/voxta.js +91 -20
  62. package/dist/voxta.js.map +1 -1
  63. package/package.json +24 -24
@@ -50,6 +50,7 @@ __export(federation_exports, {
50
50
  createAnnounceActivity: () => createAnnounceActivity,
51
51
  createArchiveAdapter: () => createArchiveAdapter,
52
52
  createBlockActivity: () => createBlockActivity,
53
+ createConsoleLogger: () => createConsoleLogger,
53
54
  createCreateActivity: () => createCreateActivity,
54
55
  createDeleteActivity: () => createDeleteActivity,
55
56
  createFlagActivity: () => createFlagActivity,
@@ -64,6 +65,7 @@ __export(federation_exports, {
64
65
  enableFederation: () => enableFederation,
65
66
  generateActivityId: () => generateActivityId,
66
67
  generateCardId: () => generateCardId,
68
+ getFederationLogger: () => getLogger,
67
69
  handleActor: () => handleActor,
68
70
  handleInbox: () => handleInbox,
69
71
  handleNodeInfo: () => handleNodeInfo,
@@ -76,6 +78,8 @@ __export(federation_exports, {
76
78
  parseForkActivity: () => parseForkActivity,
77
79
  parseInstallActivity: () => parseInstallActivity,
78
80
  parseSignatureHeader: () => parseSignatureHeader,
81
+ setFederationLogLevel: () => setLogLevel,
82
+ setFederationLogger: () => setLogger,
79
83
  signRequest: () => signRequest,
80
84
  stCharacterToCCv3: () => stCharacterToCCv3,
81
85
  validateBlockActivityFields: () => validateBlockActivity,
@@ -133,6 +137,50 @@ function generateUUID() {
133
137
  }
134
138
 
135
139
  // ../federation/dist/index.js
140
+ var LEVELS = {
141
+ silent: 0,
142
+ error: 1,
143
+ warn: 2,
144
+ info: 3,
145
+ debug: 4
146
+ };
147
+ var noop = () => {
148
+ };
149
+ function createConsoleLogger(level = "warn") {
150
+ const severity = LEVELS[level] ?? LEVELS.warn;
151
+ const hasConsole = typeof console !== "undefined";
152
+ const c = hasConsole ? console : void 0;
153
+ const debugImpl = c?.debug ? c.debug.bind(c) : c?.log ? c.log.bind(c) : noop;
154
+ const infoImpl = c?.info ? c.info.bind(c) : c?.log ? c.log.bind(c) : noop;
155
+ const warnImpl = c?.warn ? c.warn.bind(c) : c?.log ? c.log.bind(c) : noop;
156
+ const errorImpl = c?.error ? c.error.bind(c) : c?.log ? c.log.bind(c) : noop;
157
+ return {
158
+ debug: severity >= LEVELS.debug ? debugImpl : noop,
159
+ info: severity >= LEVELS.info ? infoImpl : noop,
160
+ warn: severity >= LEVELS.warn ? warnImpl : noop,
161
+ error: severity >= LEVELS.error ? errorImpl : noop
162
+ };
163
+ }
164
+ var federationLogger = createConsoleLogger("warn");
165
+ function getLogger() {
166
+ return federationLogger;
167
+ }
168
+ function setLogger(logger) {
169
+ federationLogger = logger;
170
+ }
171
+ function setLogLevel(level) {
172
+ federationLogger = createConsoleLogger(level);
173
+ }
174
+ function configureLogger(options) {
175
+ if (!options) return;
176
+ if (options.logger) {
177
+ setLogger(options.logger);
178
+ return;
179
+ }
180
+ if (options.logLevel) {
181
+ setLogLevel(options.logLevel);
182
+ }
183
+ }
136
184
  var ACTIVITY_CONTEXT = [
137
185
  "https://www.w3.org/ns/activitystreams",
138
186
  {
@@ -491,7 +539,7 @@ var SyncEngine = class {
491
539
  try {
492
540
  listener(event);
493
541
  } catch (err) {
494
- console.error(`Event listener error:`, err);
542
+ getLogger().error("[federation] Event listener error:", err);
495
543
  }
496
544
  }
497
545
  }
@@ -1620,7 +1668,7 @@ var HttpPlatformAdapter = class extends BasePlatformAdapter {
1620
1668
  const data = await response.json();
1621
1669
  return this.config.transformers?.get ? this.config.transformers.get(data) : data;
1622
1670
  } catch (err) {
1623
- console.error(`Failed to get card ${localId}:`, err);
1671
+ getLogger().error(`[federation] Failed to get card ${localId}:`, err);
1624
1672
  return null;
1625
1673
  }
1626
1674
  }
@@ -2172,27 +2220,14 @@ function parseSignatureHeader(header) {
2172
2220
  return {
2173
2221
  keyId: params.keyId,
2174
2222
  algorithm: params.algorithm || "rsa-sha256",
2175
- headers: (params.headers || "(request-target) host date").split(" "),
2223
+ headers: (params.headers || "(request-target) host date").trim().split(/\s+/).filter(Boolean).map((h) => h.toLowerCase()),
2176
2224
  signature: params.signature
2177
2225
  };
2178
2226
  }
2179
2227
  function buildSigningString(method, path, headers, headerNames) {
2180
2228
  const result = buildSigningStringStrict(method, path, headers, headerNames);
2181
2229
  if (!result.success) {
2182
- console.warn(`[federation] Signature verification may fail: ${result.error}`);
2183
- const lines = [];
2184
- for (const name of headerNames) {
2185
- if (name === "(request-target)") {
2186
- lines.push(`(request-target): ${method.toLowerCase()} ${path}`);
2187
- } else if (name === "(created)" || name === "(expires)") {
2188
- } else {
2189
- const value = headers.get(name);
2190
- if (value !== null) {
2191
- lines.push(`${name.toLowerCase()}: ${value}`);
2192
- }
2193
- }
2194
- }
2195
- return lines.join("\n");
2230
+ throw new Error(result.error);
2196
2231
  }
2197
2232
  return result.signingString;
2198
2233
  }
@@ -2201,16 +2236,17 @@ function buildSigningStringStrict(method, path, headers, headerNames) {
2201
2236
  const missingHeaders = [];
2202
2237
  const syntheticHeaders = /* @__PURE__ */ new Set(["(request-target)", "(created)", "(expires)"]);
2203
2238
  for (const name of headerNames) {
2204
- if (name === "(request-target)") {
2239
+ const normalizedName = name.toLowerCase();
2240
+ if (normalizedName === "(request-target)") {
2205
2241
  lines.push(`(request-target): ${method.toLowerCase()} ${path}`);
2206
- } else if (name === "(created)") {
2207
- } else if (name === "(expires)") {
2242
+ } else if (normalizedName === "(created)") {
2243
+ } else if (normalizedName === "(expires)") {
2208
2244
  } else {
2209
- const value = headers.get(name);
2245
+ const value = headers.get(normalizedName);
2210
2246
  if (value !== null) {
2211
- lines.push(`${name.toLowerCase()}: ${value}`);
2212
- } else if (!syntheticHeaders.has(name)) {
2213
- missingHeaders.push(name);
2247
+ lines.push(`${normalizedName}: ${value}`);
2248
+ } else if (!syntheticHeaders.has(normalizedName)) {
2249
+ missingHeaders.push(normalizedName);
2214
2250
  }
2215
2251
  }
2216
2252
  }
@@ -2231,7 +2267,7 @@ async function verifyHttpSignature(parsed, publicKeyPem, method, path, headers,
2231
2267
  if (options.strictHeaders) {
2232
2268
  const strictResult = buildSigningStringStrict(method, path, headers, parsed.headers);
2233
2269
  if (!strictResult.success) {
2234
- console.warn(`[federation] Strict header verification failed: ${strictResult.error}`);
2270
+ getLogger().warn(`[federation] Strict header verification failed: ${strictResult.error}`);
2235
2271
  return false;
2236
2272
  }
2237
2273
  }
@@ -2251,7 +2287,7 @@ async function verifyHttpSignature(parsed, publicKeyPem, method, path, headers,
2251
2287
  data
2252
2288
  );
2253
2289
  } catch (error) {
2254
- console.error("Signature verification failed:", error);
2290
+ getLogger().error("[federation] Signature verification failed:", error);
2255
2291
  return false;
2256
2292
  }
2257
2293
  }
@@ -2389,7 +2425,7 @@ async function importPublicKey(pem) {
2389
2425
  ["verify"]
2390
2426
  );
2391
2427
  } catch (error) {
2392
- console.error("Failed to import public key:", error);
2428
+ getLogger().error("[federation] Failed to import public key:", error);
2393
2429
  return null;
2394
2430
  }
2395
2431
  }
@@ -2405,7 +2441,7 @@ async function importPrivateKey(pem) {
2405
2441
  ["sign"]
2406
2442
  );
2407
2443
  } catch (error) {
2408
- console.error("Failed to import private key:", error);
2444
+ getLogger().error("[federation] Failed to import private key:", error);
2409
2445
  return null;
2410
2446
  }
2411
2447
  }
@@ -2434,9 +2470,31 @@ function extractHostFromActorId(actorId) {
2434
2470
  return null;
2435
2471
  }
2436
2472
  }
2473
+ function timingSafeEqualString(a, b) {
2474
+ let mismatch = a.length === b.length ? 0 : 1;
2475
+ const maxLen = Math.max(a.length, b.length);
2476
+ for (let i = 0; i < maxLen; i++) {
2477
+ const aCode = a.charCodeAt(i) || 0;
2478
+ const bCode = b.charCodeAt(i) || 0;
2479
+ mismatch |= aCode ^ bCode;
2480
+ }
2481
+ return mismatch === 0;
2482
+ }
2437
2483
  async function handleInbox(body, headers, options) {
2438
2484
  assertFederationEnabled("handleInbox");
2439
2485
  try {
2486
+ const normalizedHeaders = headers instanceof Headers ? headers : new Headers(headers);
2487
+ const networkKey = typeof options.networkKey === "string" ? options.networkKey : void 0;
2488
+ const networkKeyHeader = options.networkKeyHeader ?? "X-Foundry-Network-Key";
2489
+ if (networkKey && networkKey.length > 0) {
2490
+ const provided = normalizedHeaders.get(networkKeyHeader);
2491
+ if (!provided || !timingSafeEqualString(provided, networkKey)) {
2492
+ return {
2493
+ accepted: false,
2494
+ error: "Unauthorized: invalid or missing network key"
2495
+ };
2496
+ }
2497
+ }
2440
2498
  if (options.moderationStore) {
2441
2499
  const actorId = typeof body === "object" && body !== null && "actor" in body ? String(body.actor) : null;
2442
2500
  if (actorId) {
@@ -2459,7 +2517,7 @@ async function handleInbox(body, headers, options) {
2459
2517
  };
2460
2518
  }
2461
2519
  if (options.strictMode) {
2462
- const signatureHeader = headers instanceof Headers ? headers.get("signature") : headers["signature"] || headers["Signature"];
2520
+ const signatureHeader = normalizedHeaders.get("signature");
2463
2521
  if (!signatureHeader) {
2464
2522
  return {
2465
2523
  accepted: false,
@@ -2473,6 +2531,15 @@ async function handleInbox(body, headers, options) {
2473
2531
  error: "Invalid Signature header format"
2474
2532
  };
2475
2533
  }
2534
+ if (networkKey && networkKey.length > 0) {
2535
+ const requiredSigned = networkKeyHeader.toLowerCase();
2536
+ if (!parsedSig.headers.includes(requiredSigned)) {
2537
+ return {
2538
+ accepted: false,
2539
+ error: `Strict mode: signature missing required header: ${requiredSigned}`
2540
+ };
2541
+ }
2542
+ }
2476
2543
  const missingSignedHeaders = REQUIRED_SIGNED_HEADERS.filter(
2477
2544
  (h) => !parsedSig.headers.includes(h)
2478
2545
  );
@@ -2482,14 +2549,14 @@ async function handleInbox(body, headers, options) {
2482
2549
  error: `Strict mode: signature missing required headers: ${missingSignedHeaders.join(", ")}`
2483
2550
  };
2484
2551
  }
2485
- const dateHeader = headers instanceof Headers ? headers.get("date") : headers["date"] || headers["Date"];
2552
+ const dateHeader = normalizedHeaders.get("date");
2486
2553
  if (!dateHeader) {
2487
2554
  return {
2488
2555
  accepted: false,
2489
2556
  error: "Strict mode: Date header required"
2490
2557
  };
2491
2558
  }
2492
- const hostHeader = headers instanceof Headers ? headers.get("host") : headers["host"] || headers["Host"];
2559
+ const hostHeader = normalizedHeaders.get("host");
2493
2560
  if (!hostHeader) {
2494
2561
  return {
2495
2562
  accepted: false,
@@ -2532,7 +2599,7 @@ async function handleInbox(body, headers, options) {
2532
2599
  error: `Invalid key ID or actor URL`
2533
2600
  };
2534
2601
  }
2535
- const digestHeader = headers instanceof Headers ? headers.get("digest") : headers["digest"] || headers["Digest"];
2602
+ const digestHeader = normalizedHeaders.get("digest");
2536
2603
  if (digestHeader) {
2537
2604
  if (!options.rawBody) {
2538
2605
  return {
@@ -2563,13 +2630,13 @@ async function handleInbox(body, headers, options) {
2563
2630
  }
2564
2631
  const method = options.method || "POST";
2565
2632
  const path = options.path || "/inbox";
2566
- const normalizedHeaders = headers instanceof Headers ? headers : new Headers(headers);
2567
2633
  const isValid = await verifyHttpSignature(
2568
2634
  parsedSig,
2569
2635
  actor.publicKey.publicKeyPem,
2570
2636
  method,
2571
2637
  path,
2572
- normalizedHeaders
2638
+ normalizedHeaders,
2639
+ { strictHeaders: true }
2573
2640
  );
2574
2641
  if (!isValid) {
2575
2642
  return {
@@ -3648,7 +3715,7 @@ var PolicyEngine = class {
3648
3715
  if (!regex) {
3649
3716
  const safetyWarning = checkRegexSafety(rule.pattern);
3650
3717
  if (safetyWarning) {
3651
- console.warn(`[moderation] Rule "${rule.name}": ${safetyWarning}`);
3718
+ getLogger().warn(`[moderation] Rule "${rule.name}": ${safetyWarning}`);
3652
3719
  }
3653
3720
  try {
3654
3721
  regex = new RegExp(rule.pattern, "i");
@@ -3888,11 +3955,12 @@ function getEnvVar(name) {
3888
3955
  return void 0;
3889
3956
  }
3890
3957
  function enableFederation(options) {
3958
+ configureLogger(options);
3891
3959
  explicitlyEnabled = true;
3892
3960
  envCheckSkipped = options?.skipEnvCheck ?? false;
3893
3961
  const nodeEnv = getEnvVar("NODE_ENV");
3894
3962
  if (nodeEnv === "development" || nodeEnv === "test") {
3895
- console.warn(
3963
+ getLogger().warn(
3896
3964
  "[character-foundry/federation] Federation enabled. WARNING: Verify HTTP signatures in production. Do NOT use in production with untrusted inputs without signature validation."
3897
3965
  );
3898
3966
  }