proxitor 0.12.0 → 0.14.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/README.md +2 -1
- package/dist/cli.mjs +128 -29
- package/dist/cli.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -117,7 +117,8 @@ If the cache hit looks low, three levers fix it — tune them from `proxitor con
|
|
|
117
117
|
|
|
118
118
|
- **`cacheControl`** — inject `cache_control` to activate caching (Anthropic-native).
|
|
119
119
|
- **`sessionId`** — inject `session_id` so the provider pins from the first request.
|
|
120
|
-
- **`normalizeVolatileSystem`** — strip Claude Code's volatile `cch`
|
|
120
|
+
- **`normalizeVolatileSystem`** — strip Claude Code's volatile `cch`/`cc_version` hashes so the prefix cache warms on non-Anthropic providers (qwen/glm/…).
|
|
121
|
+
- **`rewriteBlockTtl`** — normalize the TTL on Claude Code's block `cache_control` breakpoints to match your `cacheControlTtl`. Enable it (`auto`/`always`) if Anthropic rejects requests where the root `ttl` is `1h` but the block breakpoints stay at `5m`.
|
|
121
122
|
|
|
122
123
|
See the [configuration reference](./docs/configuration.md#prompt-caching) for the full detail.
|
|
123
124
|
|
package/dist/cli.mjs
CHANGED
|
@@ -16251,6 +16251,7 @@ const modelOverrideSchema = object({
|
|
|
16251
16251
|
headers: record(string$1(), string$1()).optional(),
|
|
16252
16252
|
cacheControl: triStateSchema.optional(),
|
|
16253
16253
|
cacheControlTtl: ttlSchema.optional(),
|
|
16254
|
+
rewriteBlockTtl: triStateSchema.optional(),
|
|
16254
16255
|
sessionId: triStateSchema.optional(),
|
|
16255
16256
|
normalizeVolatileSystem: boolean().optional()
|
|
16256
16257
|
}).strict();
|
|
@@ -16269,6 +16270,7 @@ const proxyConfigSchema = object({
|
|
|
16269
16270
|
headers: record(string$1(), string$1()).optional(),
|
|
16270
16271
|
cacheControl: triStateSchema.default("auto"),
|
|
16271
16272
|
cacheControlTtl: ttlSchema.optional(),
|
|
16273
|
+
rewriteBlockTtl: triStateSchema.default("skip"),
|
|
16272
16274
|
sessionId: triStateSchema.default("auto"),
|
|
16273
16275
|
normalizeVolatileSystem: boolean().default(false),
|
|
16274
16276
|
modelOverrides: record(string$1().min(1), modelOverrideSchema).optional()
|
|
@@ -16398,6 +16400,7 @@ function resolveModelConfig(config, modelName) {
|
|
|
16398
16400
|
headers: config.headers ? { ...config.headers } : void 0,
|
|
16399
16401
|
cacheControl: config.cacheControl,
|
|
16400
16402
|
cacheControlTtl: config.cacheControlTtl,
|
|
16403
|
+
rewriteBlockTtl: config.rewriteBlockTtl,
|
|
16401
16404
|
sessionId: config.sessionId,
|
|
16402
16405
|
normalizeVolatileSystem: config.normalizeVolatileSystem
|
|
16403
16406
|
};
|
|
@@ -16427,6 +16430,7 @@ function applyOverride(result, override) {
|
|
|
16427
16430
|
};
|
|
16428
16431
|
if (override.cacheControl !== void 0) result.cacheControl = override.cacheControl;
|
|
16429
16432
|
if (override.cacheControlTtl !== void 0) result.cacheControlTtl = override.cacheControlTtl;
|
|
16433
|
+
if (override.rewriteBlockTtl !== void 0) result.rewriteBlockTtl = override.rewriteBlockTtl;
|
|
16430
16434
|
if (override.sessionId !== void 0) result.sessionId = override.sessionId;
|
|
16431
16435
|
if (override.normalizeVolatileSystem !== void 0) result.normalizeVolatileSystem = override.normalizeVolatileSystem;
|
|
16432
16436
|
}
|
|
@@ -17928,6 +17932,12 @@ const CACHE_HINTS = {
|
|
|
17928
17932
|
always: "All models",
|
|
17929
17933
|
skip: "Passthrough — leave client cache_control headers as-is"
|
|
17930
17934
|
};
|
|
17935
|
+
/** Shared hint texts for the rewrite-block-ttl tri-state — used in cache-control command + override editor. */
|
|
17936
|
+
const REWRITE_HINTS = {
|
|
17937
|
+
auto: "Anthropic models only — normalize blocks to match root",
|
|
17938
|
+
always: "All models",
|
|
17939
|
+
skip: "Leave client block ttl as-is (may mismatch root)"
|
|
17940
|
+
};
|
|
17931
17941
|
const NORMALIZE_HINTS = {
|
|
17932
17942
|
on: "Rewrite cch → stable prefix cache",
|
|
17933
17943
|
off: "Passthrough — rewrite nothing"
|
|
@@ -18032,8 +18042,11 @@ async function collectSessionTriState(currentSid) {
|
|
|
18032
18042
|
if (sid === "reset") return { sessionId: { remove: true } };
|
|
18033
18043
|
return { sessionId: { value: sid } };
|
|
18034
18044
|
}
|
|
18035
|
-
/**
|
|
18036
|
-
|
|
18045
|
+
/**
|
|
18046
|
+
* Collects cacheControl mode, TTL, and rewriteBlockTtl.
|
|
18047
|
+
* Mode-cancel aborts; TTL-cancel keeps existing TTL; rewrite-cancel keeps existing rewrite.
|
|
18048
|
+
*/
|
|
18049
|
+
async function collectCacheTriState(currentCc, currentTtl, globalTtl, currentRewrite) {
|
|
18037
18050
|
const cc = await askTriState("Cache control mode", currentCc, CACHE_HINTS, { removable: true });
|
|
18038
18051
|
if (typeof cc === "symbol") return null;
|
|
18039
18052
|
let cacheControl;
|
|
@@ -18043,18 +18056,23 @@ async function collectCacheTriState(currentCc, currentTtl, globalTtl) {
|
|
|
18043
18056
|
removable: true,
|
|
18044
18057
|
globalTtl
|
|
18045
18058
|
});
|
|
18046
|
-
|
|
18047
|
-
if (ttl === "
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
};
|
|
18059
|
+
let cacheControlTtl;
|
|
18060
|
+
if (typeof ttl === "symbol") cacheControlTtl = void 0;
|
|
18061
|
+
else if (ttl === "reset") cacheControlTtl = { remove: true };
|
|
18062
|
+
else cacheControlTtl = { value: ttl };
|
|
18063
|
+
const rewrite = await askTriState("Rewrite block TTLs", currentRewrite, REWRITE_HINTS, { removable: true });
|
|
18064
|
+
let rewriteBlockTtl;
|
|
18065
|
+
if (typeof rewrite === "symbol") rewriteBlockTtl = void 0;
|
|
18066
|
+
else if (rewrite === "reset") rewriteBlockTtl = { remove: true };
|
|
18067
|
+
else rewriteBlockTtl = { value: rewrite };
|
|
18051
18068
|
return {
|
|
18052
18069
|
cacheControl,
|
|
18053
|
-
cacheControlTtl
|
|
18070
|
+
cacheControlTtl,
|
|
18071
|
+
rewriteBlockTtl
|
|
18054
18072
|
};
|
|
18055
18073
|
}
|
|
18056
18074
|
async function collectNormalizeVolatileSystem(currentNvs) {
|
|
18057
|
-
const nvs = await askNormalizeVolatileSystem("Normalize volatile system (cch
|
|
18075
|
+
const nvs = await askNormalizeVolatileSystem("Normalize volatile system (cch/cc_version hashes)", currentNvs, { removable: true });
|
|
18058
18076
|
if (typeof nvs === "symbol") return null;
|
|
18059
18077
|
if (nvs === "reset") return { normalizeVolatileSystem: { remove: true } };
|
|
18060
18078
|
return { normalizeVolatileSystem: { value: nvs } };
|
|
@@ -18330,29 +18348,45 @@ async function browseModelsCommand(client) {
|
|
|
18330
18348
|
}
|
|
18331
18349
|
//#endregion
|
|
18332
18350
|
//#region src/commands/config/cache-control.ts
|
|
18351
|
+
/**
|
|
18352
|
+
* Map a ResolvedField into the `fields` object for `setGlobalConfigFields`:
|
|
18353
|
+
* {value}→assign value, {remove}→assign undefined (delete from disk),
|
|
18354
|
+
* undefined→omit the key (keep existing on disk).
|
|
18355
|
+
*
|
|
18356
|
+
* This is the file-write counterpart of `applyField`. The in-memory `applyField`
|
|
18357
|
+
* can `delete` an existing key, but a fresh `fields` object fed to
|
|
18358
|
+
* `setGlobalConfigFields` needs an explicit `undefined` value to trigger
|
|
18359
|
+
* removal — so keep-existing (undefined field) must be distinguished from
|
|
18360
|
+
* remove ({remove:true}) by omitting vs. assigning the key.
|
|
18361
|
+
*/
|
|
18362
|
+
function assignField(fields, key, field) {
|
|
18363
|
+
if (field === void 0) return;
|
|
18364
|
+
fields[key] = "remove" in field ? void 0 : field.value;
|
|
18365
|
+
}
|
|
18366
|
+
function fieldLabel(field, fallback) {
|
|
18367
|
+
if (field === void 0) return fallback;
|
|
18368
|
+
if ("remove" in field) return "(default)";
|
|
18369
|
+
return String(field.value);
|
|
18370
|
+
}
|
|
18333
18371
|
async function cacheControlCommand(opts) {
|
|
18334
18372
|
const configPath = requireConfigPath(opts?.configPath);
|
|
18335
18373
|
const cfg = readConfigFileRaw(configPath);
|
|
18336
18374
|
const rawCc = cfg.cacheControl;
|
|
18337
18375
|
const rawTtl = cfg.cacheControlTtl;
|
|
18376
|
+
const rawRewrite = cfg.rewriteBlockTtl;
|
|
18338
18377
|
const effectiveCc = rawCc ?? DEFAULTS.cacheControl;
|
|
18378
|
+
const effectiveRewrite = rawRewrite ?? DEFAULTS.rewriteBlockTtl;
|
|
18339
18379
|
log.info(`Current: cacheControl = ${rawCc === void 0 ? `(default -> ${effectiveCc})` : effectiveCc}`);
|
|
18340
18380
|
if (rawTtl) log.info(`Current: cacheControlTtl = ${rawTtl}`);
|
|
18341
|
-
|
|
18342
|
-
|
|
18381
|
+
log.info(`Current: rewriteBlockTtl = ${rawRewrite === void 0 ? `(default -> ${effectiveRewrite})` : rawRewrite}`);
|
|
18382
|
+
const result = await collectCacheTriState(rawCc, rawTtl, void 0, rawRewrite);
|
|
18383
|
+
if (result === null) return;
|
|
18343
18384
|
const fields = {};
|
|
18344
|
-
fields
|
|
18345
|
-
|
|
18346
|
-
|
|
18347
|
-
setGlobalConfigFields(configPath, fields);
|
|
18348
|
-
log.success(`cacheControl set to ${cc === "reset" ? "(default)" : cc}`);
|
|
18349
|
-
return;
|
|
18350
|
-
}
|
|
18351
|
-
fields.cacheControlTtl = ttlResult === "reset" ? void 0 : ttlResult;
|
|
18385
|
+
assignField(fields, "cacheControl", result.cacheControl);
|
|
18386
|
+
assignField(fields, "cacheControlTtl", result.cacheControlTtl);
|
|
18387
|
+
assignField(fields, "rewriteBlockTtl", result.rewriteBlockTtl);
|
|
18352
18388
|
setGlobalConfigFields(configPath, fields);
|
|
18353
|
-
|
|
18354
|
-
const ttlLabel = ttlResult === "reset" ? "(default)" : ttlResult;
|
|
18355
|
-
log.success(`cacheControl set to ${ccLabel}, TTL = ${ttlLabel}`);
|
|
18389
|
+
log.success(`cacheControl set to ${fieldLabel(result.cacheControl, effectiveCc)}, TTL = ${fieldLabel(result.cacheControlTtl, "(unchanged)")}, rewrite = ${fieldLabel(result.rewriteBlockTtl, effectiveRewrite)}`);
|
|
18356
18390
|
}
|
|
18357
18391
|
//#endregion
|
|
18358
18392
|
//#region src/commands/config/caching-summary.ts
|
|
@@ -18383,6 +18417,7 @@ function perModelTtl(current, globalCfg) {
|
|
|
18383
18417
|
}
|
|
18384
18418
|
function formatGlobalCachingSummary(cfg) {
|
|
18385
18419
|
const cc = globalTriStateLabel(cfg.cacheControl, DEFAULTS.cacheControl);
|
|
18420
|
+
const rewrite = globalTriStateLabel(cfg.rewriteBlockTtl, DEFAULTS.rewriteBlockTtl);
|
|
18386
18421
|
const sid = globalTriStateLabel(cfg.sessionId, DEFAULTS.sessionId);
|
|
18387
18422
|
const nvs = globalNvsLabel(cfg.normalizeVolatileSystem);
|
|
18388
18423
|
return [
|
|
@@ -18390,6 +18425,7 @@ function formatGlobalCachingSummary(cfg) {
|
|
|
18390
18425
|
"",
|
|
18391
18426
|
` cacheControl = ${cc} (activate cache)`,
|
|
18392
18427
|
` cacheControlTtl = ${describeTtl(cfg.cacheControlTtl)}`,
|
|
18428
|
+
` rewriteBlockTtl = ${rewrite} (normalize block ttl)`,
|
|
18393
18429
|
` sessionId = ${sid} (pin provider)`,
|
|
18394
18430
|
` normalizeVolatileSystem = ${nvs} (stable prefix)`,
|
|
18395
18431
|
"",
|
|
@@ -18398,10 +18434,12 @@ function formatGlobalCachingSummary(cfg) {
|
|
|
18398
18434
|
}
|
|
18399
18435
|
function formatPerModelCachingSummary(modelKey, current, globalCfg) {
|
|
18400
18436
|
const gCc = globalCfg.cacheControl ?? DEFAULTS.cacheControl;
|
|
18437
|
+
const gRewrite = globalCfg.rewriteBlockTtl ?? DEFAULTS.rewriteBlockTtl;
|
|
18401
18438
|
const gSid = globalCfg.sessionId ?? DEFAULTS.sessionId;
|
|
18402
18439
|
const gNvs = globalCfg.normalizeVolatileSystem ?? DEFAULTS.normalizeVolatileSystem;
|
|
18403
18440
|
const cc = current.cacheControl ?? `(inherit -> ${gCc})`;
|
|
18404
18441
|
const ttl = perModelTtl(current, globalCfg);
|
|
18442
|
+
const rewrite = current.rewriteBlockTtl ?? `(inherit -> ${gRewrite})`;
|
|
18405
18443
|
const sid = current.sessionId ?? `(inherit -> ${gSid})`;
|
|
18406
18444
|
const nvs = nvsLabel(current.normalizeVolatileSystem, gNvs);
|
|
18407
18445
|
return [
|
|
@@ -18409,6 +18447,7 @@ function formatPerModelCachingSummary(modelKey, current, globalCfg) {
|
|
|
18409
18447
|
"",
|
|
18410
18448
|
` cacheControl = ${cc}`,
|
|
18411
18449
|
` cacheControlTtl = ${ttl}`,
|
|
18450
|
+
` rewriteBlockTtl = ${rewrite}`,
|
|
18412
18451
|
` sessionId = ${sid}`,
|
|
18413
18452
|
` normalizeVolatileSystem = ${nvs}`
|
|
18414
18453
|
].join("\n");
|
|
@@ -18426,7 +18465,7 @@ async function normalizeVolatileSystemCommand(opts) {
|
|
|
18426
18465
|
const raw = readConfigFileRaw(configPath).normalizeVolatileSystem;
|
|
18427
18466
|
const effective = raw ?? DEFAULTS.normalizeVolatileSystem;
|
|
18428
18467
|
log.info(`Current: normalizeVolatileSystem = ${raw === void 0 ? `(default -> ${effective ? "on" : "off"})` : effective}`);
|
|
18429
|
-
const choice = await askNormalizeVolatileSystem("Normalize Claude Code's volatile cch
|
|
18468
|
+
const choice = await askNormalizeVolatileSystem("Normalize Claude Code's volatile cch and cc_version hashes in the system prompt? Stabilizes the prefix cache for non-Anthropic providers (qwen/glm/etc.).", raw, {
|
|
18430
18469
|
removable: true,
|
|
18431
18470
|
resetHint: `remove (default: ${DEFAULTS.normalizeVolatileSystem})`
|
|
18432
18471
|
});
|
|
@@ -18456,11 +18495,12 @@ async function editSessionId(current) {
|
|
|
18456
18495
|
}
|
|
18457
18496
|
async function editCacheControl(current, configPath) {
|
|
18458
18497
|
const globalTtl = readGlobalTtl(configPath);
|
|
18459
|
-
const result = await collectCacheTriState(current.cacheControl, current.cacheControlTtl, globalTtl);
|
|
18498
|
+
const result = await collectCacheTriState(current.cacheControl, current.cacheControlTtl, globalTtl, current.rewriteBlockTtl);
|
|
18460
18499
|
if (result === null) return current;
|
|
18461
18500
|
const next = { ...current };
|
|
18462
18501
|
applyField(next, "cacheControl", result.cacheControl);
|
|
18463
18502
|
applyField(next, "cacheControlTtl", result.cacheControlTtl);
|
|
18503
|
+
applyField(next, "rewriteBlockTtl", result.rewriteBlockTtl);
|
|
18464
18504
|
return next;
|
|
18465
18505
|
}
|
|
18466
18506
|
async function editNormalizeVolatileSystem(current) {
|
|
@@ -19353,7 +19393,7 @@ async function runConfigMenu(client) {
|
|
|
19353
19393
|
}
|
|
19354
19394
|
//#endregion
|
|
19355
19395
|
//#region src/version.ts
|
|
19356
|
-
const version = "0.
|
|
19396
|
+
const version = "0.14.0";
|
|
19357
19397
|
//#endregion
|
|
19358
19398
|
//#region src/commands/doctor.ts
|
|
19359
19399
|
const DEFAULT_TIMEOUT_MS = 3e3;
|
|
@@ -22905,6 +22945,56 @@ function buildCacheControl(existing, ttl, isAnthropic) {
|
|
|
22905
22945
|
}
|
|
22906
22946
|
return base;
|
|
22907
22947
|
}
|
|
22948
|
+
/**
|
|
22949
|
+
* Whether to normalize block-level cache_control TTLs. Mirrors the cacheControl
|
|
22950
|
+
* tri-state: `auto` rewrites only when injection is active on an Anthropic-native
|
|
22951
|
+
* endpoint; `always` rewrites everywhere; `skip` never.
|
|
22952
|
+
*/
|
|
22953
|
+
function shouldRewriteBlockTtl(mode, cacheControlMode, modelName, path) {
|
|
22954
|
+
if (mode === "skip") return false;
|
|
22955
|
+
if (mode === "always") return true;
|
|
22956
|
+
return isAnthropicEndpoint(modelName, path) && shouldInjectCacheControl(cacheControlMode, modelName, path);
|
|
22957
|
+
}
|
|
22958
|
+
/** Shallow, order-independent comparison of two cache_control objects. */
|
|
22959
|
+
function sameCacheControl(a, b) {
|
|
22960
|
+
const ka = Object.keys(a);
|
|
22961
|
+
const kb = Object.keys(b);
|
|
22962
|
+
if (ka.length !== kb.length) return false;
|
|
22963
|
+
return ka.every((k) => a[k] === b[k]);
|
|
22964
|
+
}
|
|
22965
|
+
/**
|
|
22966
|
+
* Normalize the TTL on every existing block-level cache_control breakpoint
|
|
22967
|
+
* (system, tools, messages[].content) to match the configured TTL. Reuses
|
|
22968
|
+
* buildCacheControl so block and root stay consistent. Adds no new breakpoints.
|
|
22969
|
+
* Returns whether any block was changed.
|
|
22970
|
+
*/
|
|
22971
|
+
function rewriteBlockTtls(body, ttl, isAnthropic) {
|
|
22972
|
+
let mutated = false;
|
|
22973
|
+
const rewriteNode = (obj) => {
|
|
22974
|
+
const cc = obj.cache_control;
|
|
22975
|
+
if (cc !== null && typeof cc === "object" && !Array.isArray(cc)) {
|
|
22976
|
+
const next = buildCacheControl(cc, ttl, isAnthropic);
|
|
22977
|
+
if (!sameCacheControl(cc, next)) {
|
|
22978
|
+
obj.cache_control = next;
|
|
22979
|
+
mutated = true;
|
|
22980
|
+
}
|
|
22981
|
+
}
|
|
22982
|
+
};
|
|
22983
|
+
const visitBlocks = (arr) => {
|
|
22984
|
+
if (!Array.isArray(arr)) return;
|
|
22985
|
+
for (const item of arr) {
|
|
22986
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) continue;
|
|
22987
|
+
const obj = item;
|
|
22988
|
+
rewriteNode(obj);
|
|
22989
|
+
visitBlocks(obj.content);
|
|
22990
|
+
}
|
|
22991
|
+
};
|
|
22992
|
+
visitBlocks(body.system);
|
|
22993
|
+
visitBlocks(body.tools);
|
|
22994
|
+
const messages = body.messages;
|
|
22995
|
+
for (const m of messages ?? []) visitBlocks(m?.content);
|
|
22996
|
+
return mutated;
|
|
22997
|
+
}
|
|
22908
22998
|
//#endregion
|
|
22909
22999
|
//#region src/proxy/middleware/inject-cache-control.ts
|
|
22910
23000
|
const injectCacheControl = createMiddleware(async (c, next) => {
|
|
@@ -22914,11 +23004,16 @@ const injectCacheControl = createMiddleware(async (c, next) => {
|
|
|
22914
23004
|
await next();
|
|
22915
23005
|
return;
|
|
22916
23006
|
}
|
|
23007
|
+
const isAnthropic = isAnthropicEndpoint(c.var.modelName, c.req.path);
|
|
23008
|
+
let mutated = false;
|
|
22917
23009
|
if (shouldInjectCacheControl(resolved.cacheControl, c.var.modelName, c.req.path)) {
|
|
22918
|
-
const isAnthropic = isAnthropicEndpoint(c.var.modelName, c.req.path);
|
|
22919
23010
|
parsedBody.cache_control = buildCacheControl(parsedBody.cache_control, resolved.cacheControlTtl, isAnthropic);
|
|
22920
|
-
|
|
23011
|
+
mutated = true;
|
|
23012
|
+
}
|
|
23013
|
+
if (shouldRewriteBlockTtl(resolved.rewriteBlockTtl, resolved.cacheControl, c.var.modelName, c.req.path)) {
|
|
23014
|
+
if (rewriteBlockTtls(parsedBody, resolved.cacheControlTtl, isAnthropic)) mutated = true;
|
|
22921
23015
|
}
|
|
23016
|
+
if (mutated) c.set("bodyMutated", true);
|
|
22922
23017
|
await next();
|
|
22923
23018
|
});
|
|
22924
23019
|
//#endregion
|
|
@@ -22999,11 +23094,15 @@ const injectSessionId = createMiddleware(async (c, next) => {
|
|
|
22999
23094
|
});
|
|
23000
23095
|
//#endregion
|
|
23001
23096
|
//#region src/proxy/middleware/normalize-volatile-system.ts
|
|
23002
|
-
/** Normalize Claude Code's per-request
|
|
23097
|
+
/** Normalize Claude Code's per-request volatile hashes in system[0] to constants; they drift every turn and break prefix-cache for non-Anthropic providers:
|
|
23098
|
+
* - `cch=…` per-turn hash
|
|
23099
|
+
* - `cc_version=<semver>.<hash>` — the trailing build hash drifts; the readable semver is preserved. */
|
|
23003
23100
|
const CCH_PATTERN = /cch=[0-9a-f]+/g;
|
|
23004
23101
|
const CCH_REPLACEMENT = "cch=00000";
|
|
23102
|
+
const CC_VERSION_PATTERN = /cc_version=(\d+\.\d+\.\d+)\.[0-9a-f]+/g;
|
|
23103
|
+
const CC_VERSION_REPLACEMENT = "cc_version=$1.0";
|
|
23005
23104
|
function normalizeText(text) {
|
|
23006
|
-
return text.replace(CCH_PATTERN, CCH_REPLACEMENT);
|
|
23105
|
+
return text.replace(CCH_PATTERN, CCH_REPLACEMENT).replace(CC_VERSION_PATTERN, CC_VERSION_REPLACEMENT);
|
|
23007
23106
|
}
|
|
23008
23107
|
function textOf(block) {
|
|
23009
23108
|
if (block !== null && typeof block === "object" && typeof block.text === "string") return block.text;
|