@bazariodev/docket 0.0.0-stage → 1.0.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/CHANGELOG.md +37 -0
- package/LICENSE +21 -0
- package/README.md +985 -2
- package/dist/browser/constants.d.cts +14 -0
- package/dist/browser/constants.d.ts +14 -0
- package/dist/browser/index.cjs +192 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +29 -0
- package/dist/browser/index.d.ts +29 -0
- package/dist/browser/index.js +187 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/chunk-7ld72rnl.js +376 -0
- package/dist/chunk-7ld72rnl.js.map +1 -0
- package/dist/chunk-BLf-eCDO.cjs +53 -0
- package/dist/chunk-BLf-eCDO.cjs.map +1 -0
- package/dist/chunk-CEhpwtwJ.cjs +379 -0
- package/dist/chunk-CEhpwtwJ.cjs.map +1 -0
- package/dist/chunk-CSS3f4_M.js +46 -0
- package/dist/chunk-CSS3f4_M.js.map +1 -0
- package/dist/chunk-CXZYNALm.cjs +124 -0
- package/dist/chunk-CXZYNALm.cjs.map +1 -0
- package/dist/chunk-DYcNnkOV.js +50 -0
- package/dist/chunk-DYcNnkOV.js.map +1 -0
- package/dist/chunk-DkvWLziD.js +437 -0
- package/dist/chunk-DkvWLziD.js.map +1 -0
- package/dist/chunk-DqK_3eFr.cjs +53 -0
- package/dist/chunk-DqK_3eFr.cjs.map +1 -0
- package/dist/chunk-LRCHwVuE.js +110 -0
- package/dist/chunk-LRCHwVuE.js.map +1 -0
- package/dist/chunk-ieB8yTEn.cjs +439 -0
- package/dist/chunk-ieB8yTEn.cjs.map +1 -0
- package/dist/client/index.cjs +167 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +40 -0
- package/dist/client/index.d.ts +40 -0
- package/dist/client/index.js +165 -0
- package/dist/client/index.js.map +1 -0
- package/dist/constants.d.cts +4 -0
- package/dist/constants.d.ts +4 -0
- package/dist/core/constants.d.cts +43 -0
- package/dist/core/constants.d.ts +43 -0
- package/dist/devtools/index.cjs +37 -0
- package/dist/devtools/index.cjs.map +1 -0
- package/dist/devtools/index.d.cts +11 -0
- package/dist/devtools/index.d.ts +11 -0
- package/dist/devtools/index.js +35 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/export/index.cjs +139 -0
- package/dist/export/index.cjs.map +1 -0
- package/dist/export/index.d.cts +49 -0
- package/dist/export/index.d.ts +49 -0
- package/dist/export/index.js +137 -0
- package/dist/export/index.js.map +1 -0
- package/dist/history/index.cjs +30 -0
- package/dist/history/index.cjs.map +1 -0
- package/dist/history/index.d.cts +11 -0
- package/dist/history/index.d.ts +11 -0
- package/dist/history/index.js +28 -0
- package/dist/history/index.js.map +1 -0
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +159 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/indexeddb/index.cjs +26 -0
- package/dist/indexeddb/index.cjs.map +1 -0
- package/dist/indexeddb/index.d.cts +61 -0
- package/dist/indexeddb/index.d.ts +61 -0
- package/dist/indexeddb/index.js +23 -0
- package/dist/indexeddb/index.js.map +1 -0
- package/dist/redaction/index.cjs +154 -0
- package/dist/redaction/index.cjs.map +1 -0
- package/dist/redaction/index.d.cts +70 -0
- package/dist/redaction/index.d.ts +70 -0
- package/dist/redaction/index.js +152 -0
- package/dist/redaction/index.js.map +1 -0
- package/dist/sip/index.cjs +336 -0
- package/dist/sip/index.cjs.map +1 -0
- package/dist/sip/index.d.cts +34 -0
- package/dist/sip/index.d.ts +34 -0
- package/dist/sip/index.js +333 -0
- package/dist/sip/index.js.map +1 -0
- package/dist/storage/constants.d.cts +11 -0
- package/dist/storage/constants.d.ts +11 -0
- package/dist/storage/contracts.d.cts +31 -0
- package/dist/storage/contracts.d.ts +31 -0
- package/dist/upload/constants.d.cts +17 -0
- package/dist/upload/constants.d.ts +17 -0
- package/dist/upload/index.cjs +246 -0
- package/dist/upload/index.cjs.map +1 -0
- package/dist/upload/index.d.cts +51 -0
- package/dist/upload/index.d.ts +51 -0
- package/dist/upload/index.js +244 -0
- package/dist/upload/index.js.map +1 -0
- package/package.json +150 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/redaction/index.js"],"sourcesContent":["import { define, isError, own, resolveLimits } from '../core/common.js';\nimport { MARKERS } from '../core/constants.js';\n\nexport function createRedactor(\n policy = {},\n { createRedactor: createValueRedactor, TEXT_RULES: providerTextRules } = {},\n) {\n if (typeof createValueRedactor !== 'function')\n throw new TypeError('A value redactor factory is required');\n if (!Array.isArray(policy.text) && !providerTextRules)\n throw new TypeError('Default text rules are required');\n const limits = resolveLimits(policy.limits);\n const version = policy.version ?? '1';\n if (\n typeof version !== 'string' ||\n !version ||\n version.length > limits.maxStringLength\n )\n throw new TypeError('Invalid redaction version');\n const { fields, unknownFields, hash, text } = policy;\n const textRules = text ?? Object.values(providerTextRules);\n const engine = createValueRedactor({\n fields,\n unknownFields,\n hash:\n typeof hash === 'function' ? (value) => hash.call(policy, value) : hash,\n limits: { ...limits, maxOutputSize: limits.maxRecordSize },\n text: text\n ? textRules.map(({ name, pattern, requiresHash, replace }) => ({\n name,\n pattern,\n requiresHash,\n replace:\n typeof replace === 'function'\n ? function (match) {\n return replace.call(this, match, api);\n }\n : replace,\n }))\n : textRules,\n });\n let active;\n const api = {\n version,\n limits,\n mask: engine.mask,\n redactText(value) {\n return (active?.walk ?? engine.createSession()).redactText(value);\n },\n\n redactValue(value) {\n const walk = engine.createSession();\n const result = walk.redactValue(value);\n if (active && walk.truncated) active.truncated = true;\n return JSON.stringify(result).length <= limits.maxRecordSize\n ? result\n : MARKERS.LIMIT;\n },\n\n redact(draft) {\n const previous = active;\n const walk = engine.createSession();\n active = { walk, truncated: false };\n try {\n let didTruncate = false;\n const record = {};\n for (const key of [\n 'runId',\n 'seq',\n 'at',\n 'level',\n 'area',\n 'scope',\n 'message',\n ])\n define(\n record,\n key,\n typeof draft[key] === 'string' && !['runId', 'level'].includes(key)\n ? walk.redactText(draft[key])\n : draft[key],\n );\n record.redactionVersion = version;\n if (draft.context) record.context = walk.redactValue(draft.context);\n const data = draft.data;\n if (isError(data)) {\n const converted = walk.redactValue(data);\n record.error =\n typeof converted === 'string'\n ? { name: 'Error', message: converted }\n : converted;\n } else if (data !== undefined) {\n if (\n data &&\n typeof data === 'object' &&\n !Array.isArray(data) &&\n [Object.prototype, null].includes(Object.getPrototypeOf(data))\n ) {\n const error =\n walk.fieldRule('error') === 'drop'\n ? undefined\n : own(data, 'error');\n const promoted = isError(error);\n if (promoted) {\n const converted = walk.redactError(error, 'error');\n if (converted !== undefined) record.error = converted;\n }\n const values = {};\n let count = 0;\n for (const key of Reflect.ownKeys(data)) {\n if (++count > limits.maxKeys) {\n define(values, '[truncated]', MARKERS.LIMIT);\n didTruncate = true;\n break;\n }\n if (key === 'error' && promoted) continue;\n const descriptor = Object.getOwnPropertyDescriptor(data, key);\n if (typeof key === 'string' && descriptor?.enumerable)\n Object.defineProperty(values, key, descriptor);\n }\n const converted = walk.redactValue(values);\n record.fields =\n converted &&\n typeof converted === 'object' &&\n !Array.isArray(converted)\n ? converted\n : { value: converted };\n } else record.fields = { value: walk.redactValue(data) };\n }\n if (walk.truncated || active.truncated || didTruncate)\n record.truncated = true;\n if (JSON.stringify(record).length > limits.maxRecordSize) {\n delete record.fields;\n delete record.context;\n delete record.error;\n record.fields = { value: MARKERS.LIMIT };\n record.truncated = true;\n if (JSON.stringify(record).length > limits.maxRecordSize)\n throw new TypeError('Record headers exceed size limit');\n }\n return record;\n } finally {\n active = previous;\n }\n },\n };\n if (engine.hash) api.hash = engine.hash;\n return Object.freeze(api);\n}\n"],"names":["resolveLimits","MARKERS","define","isError","own"],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { LogValue, Redactor, SerializedError } from "../index.cjs";
|
|
2
|
+
|
|
3
|
+
export type FieldRule = 'keep' | 'drop' | 'mask' | 'hash';
|
|
4
|
+
export type RedactionLimits = Readonly<Partial<Redactor['limits']>>;
|
|
5
|
+
export type TextRule = Readonly<{
|
|
6
|
+
name: string;
|
|
7
|
+
pattern: RegExp;
|
|
8
|
+
requiresHash?: boolean;
|
|
9
|
+
replace(this: TextRule, match: string, redactor: Redactor): string;
|
|
10
|
+
}>;
|
|
11
|
+
export type RedactionPolicy = Readonly<{
|
|
12
|
+
version?: string;
|
|
13
|
+
hash?: (this: RedactionPolicy, value: string) => string;
|
|
14
|
+
fields?: Readonly<Record<string, FieldRule>>;
|
|
15
|
+
unknownFields?: 'keep' | 'drop';
|
|
16
|
+
text?: readonly TextRule[];
|
|
17
|
+
limits?: RedactionLimits;
|
|
18
|
+
}>;
|
|
19
|
+
export type ValueRedactionSession = Readonly<{
|
|
20
|
+
redactValue: (this: ValueRedactionSession, value: unknown) => LogValue;
|
|
21
|
+
redactText: (this: ValueRedactionSession, text: string) => string;
|
|
22
|
+
redactError: (
|
|
23
|
+
this: ValueRedactionSession,
|
|
24
|
+
value: unknown,
|
|
25
|
+
field: string,
|
|
26
|
+
) => SerializedError | undefined;
|
|
27
|
+
fieldRule: (
|
|
28
|
+
this: ValueRedactionSession,
|
|
29
|
+
name: string,
|
|
30
|
+
) => FieldRule | undefined;
|
|
31
|
+
truncated: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
export type ValueRedactor = Readonly<{
|
|
34
|
+
limits: Readonly<{
|
|
35
|
+
maxDepth: number;
|
|
36
|
+
maxStringLength: number;
|
|
37
|
+
maxArrayLength: number;
|
|
38
|
+
maxKeys: number;
|
|
39
|
+
maxNodes: number;
|
|
40
|
+
maxOutputSize: number;
|
|
41
|
+
}>;
|
|
42
|
+
mask: (value: string) => string;
|
|
43
|
+
redactValue: (value: unknown) => LogValue;
|
|
44
|
+
redactText: (text: string) => string;
|
|
45
|
+
hash?: (value: string) => string;
|
|
46
|
+
createSession: () => ValueRedactionSession;
|
|
47
|
+
}>;
|
|
48
|
+
export type ValueTextRule = Readonly<{
|
|
49
|
+
name: string;
|
|
50
|
+
pattern: RegExp;
|
|
51
|
+
requiresHash?: boolean;
|
|
52
|
+
replace: (match: string, redactor: ValueRedactor) => string;
|
|
53
|
+
}>;
|
|
54
|
+
export type CreateValueRedactor = (
|
|
55
|
+
policy: Readonly<{
|
|
56
|
+
fields?: Readonly<Record<string, FieldRule>>;
|
|
57
|
+
unknownFields?: 'keep' | 'drop';
|
|
58
|
+
hash?: (value: string) => string;
|
|
59
|
+
limits?: Readonly<RedactionLimits & { maxOutputSize?: number }>;
|
|
60
|
+
text?: readonly ValueTextRule[];
|
|
61
|
+
}>,
|
|
62
|
+
) => ValueRedactor;
|
|
63
|
+
export type RedactorProviders = Readonly<{
|
|
64
|
+
createRedactor: CreateValueRedactor;
|
|
65
|
+
TEXT_RULES?: Readonly<Record<string, ValueTextRule>>;
|
|
66
|
+
}>;
|
|
67
|
+
export declare function createRedactor(
|
|
68
|
+
policy: RedactionPolicy | undefined,
|
|
69
|
+
providers: RedactorProviders,
|
|
70
|
+
): Redactor;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { LogValue, Redactor, SerializedError } from '../index.js';
|
|
2
|
+
|
|
3
|
+
export type FieldRule = 'keep' | 'drop' | 'mask' | 'hash';
|
|
4
|
+
export type RedactionLimits = Readonly<Partial<Redactor['limits']>>;
|
|
5
|
+
export type TextRule = Readonly<{
|
|
6
|
+
name: string;
|
|
7
|
+
pattern: RegExp;
|
|
8
|
+
requiresHash?: boolean;
|
|
9
|
+
replace(this: TextRule, match: string, redactor: Redactor): string;
|
|
10
|
+
}>;
|
|
11
|
+
export type RedactionPolicy = Readonly<{
|
|
12
|
+
version?: string;
|
|
13
|
+
hash?: (this: RedactionPolicy, value: string) => string;
|
|
14
|
+
fields?: Readonly<Record<string, FieldRule>>;
|
|
15
|
+
unknownFields?: 'keep' | 'drop';
|
|
16
|
+
text?: readonly TextRule[];
|
|
17
|
+
limits?: RedactionLimits;
|
|
18
|
+
}>;
|
|
19
|
+
export type ValueRedactionSession = Readonly<{
|
|
20
|
+
redactValue: (this: ValueRedactionSession, value: unknown) => LogValue;
|
|
21
|
+
redactText: (this: ValueRedactionSession, text: string) => string;
|
|
22
|
+
redactError: (
|
|
23
|
+
this: ValueRedactionSession,
|
|
24
|
+
value: unknown,
|
|
25
|
+
field: string,
|
|
26
|
+
) => SerializedError | undefined;
|
|
27
|
+
fieldRule: (
|
|
28
|
+
this: ValueRedactionSession,
|
|
29
|
+
name: string,
|
|
30
|
+
) => FieldRule | undefined;
|
|
31
|
+
truncated: boolean;
|
|
32
|
+
}>;
|
|
33
|
+
export type ValueRedactor = Readonly<{
|
|
34
|
+
limits: Readonly<{
|
|
35
|
+
maxDepth: number;
|
|
36
|
+
maxStringLength: number;
|
|
37
|
+
maxArrayLength: number;
|
|
38
|
+
maxKeys: number;
|
|
39
|
+
maxNodes: number;
|
|
40
|
+
maxOutputSize: number;
|
|
41
|
+
}>;
|
|
42
|
+
mask: (value: string) => string;
|
|
43
|
+
redactValue: (value: unknown) => LogValue;
|
|
44
|
+
redactText: (text: string) => string;
|
|
45
|
+
hash?: (value: string) => string;
|
|
46
|
+
createSession: () => ValueRedactionSession;
|
|
47
|
+
}>;
|
|
48
|
+
export type ValueTextRule = Readonly<{
|
|
49
|
+
name: string;
|
|
50
|
+
pattern: RegExp;
|
|
51
|
+
requiresHash?: boolean;
|
|
52
|
+
replace: (match: string, redactor: ValueRedactor) => string;
|
|
53
|
+
}>;
|
|
54
|
+
export type CreateValueRedactor = (
|
|
55
|
+
policy: Readonly<{
|
|
56
|
+
fields?: Readonly<Record<string, FieldRule>>;
|
|
57
|
+
unknownFields?: 'keep' | 'drop';
|
|
58
|
+
hash?: (value: string) => string;
|
|
59
|
+
limits?: Readonly<RedactionLimits & { maxOutputSize?: number }>;
|
|
60
|
+
text?: readonly ValueTextRule[];
|
|
61
|
+
}>,
|
|
62
|
+
) => ValueRedactor;
|
|
63
|
+
export type RedactorProviders = Readonly<{
|
|
64
|
+
createRedactor: CreateValueRedactor;
|
|
65
|
+
TEXT_RULES?: Readonly<Record<string, ValueTextRule>>;
|
|
66
|
+
}>;
|
|
67
|
+
export declare function createRedactor(
|
|
68
|
+
policy: RedactionPolicy | undefined,
|
|
69
|
+
providers: RedactorProviders,
|
|
70
|
+
): Redactor;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { r as resolveLimits, d as define, i as isError, o as own } from '../chunk-LRCHwVuE.js';
|
|
2
|
+
import { M as MARKERS } from '../chunk-CSS3f4_M.js';
|
|
3
|
+
|
|
4
|
+
function createRedactor(
|
|
5
|
+
policy = {},
|
|
6
|
+
{ createRedactor: createValueRedactor, TEXT_RULES: providerTextRules } = {},
|
|
7
|
+
) {
|
|
8
|
+
if (typeof createValueRedactor !== 'function')
|
|
9
|
+
throw new TypeError('A value redactor factory is required');
|
|
10
|
+
if (!Array.isArray(policy.text) && !providerTextRules)
|
|
11
|
+
throw new TypeError('Default text rules are required');
|
|
12
|
+
const limits = resolveLimits(policy.limits);
|
|
13
|
+
const version = policy.version ?? '1';
|
|
14
|
+
if (
|
|
15
|
+
typeof version !== 'string' ||
|
|
16
|
+
!version ||
|
|
17
|
+
version.length > limits.maxStringLength
|
|
18
|
+
)
|
|
19
|
+
throw new TypeError('Invalid redaction version');
|
|
20
|
+
const { fields, unknownFields, hash, text } = policy;
|
|
21
|
+
const textRules = text ?? Object.values(providerTextRules);
|
|
22
|
+
const engine = createValueRedactor({
|
|
23
|
+
fields,
|
|
24
|
+
unknownFields,
|
|
25
|
+
hash:
|
|
26
|
+
typeof hash === 'function' ? (value) => hash.call(policy, value) : hash,
|
|
27
|
+
limits: { ...limits, maxOutputSize: limits.maxRecordSize },
|
|
28
|
+
text: text
|
|
29
|
+
? textRules.map(({ name, pattern, requiresHash, replace }) => ({
|
|
30
|
+
name,
|
|
31
|
+
pattern,
|
|
32
|
+
requiresHash,
|
|
33
|
+
replace:
|
|
34
|
+
typeof replace === 'function'
|
|
35
|
+
? function (match) {
|
|
36
|
+
return replace.call(this, match, api);
|
|
37
|
+
}
|
|
38
|
+
: replace,
|
|
39
|
+
}))
|
|
40
|
+
: textRules,
|
|
41
|
+
});
|
|
42
|
+
let active;
|
|
43
|
+
const api = {
|
|
44
|
+
version,
|
|
45
|
+
limits,
|
|
46
|
+
mask: engine.mask,
|
|
47
|
+
redactText(value) {
|
|
48
|
+
return (active?.walk ?? engine.createSession()).redactText(value);
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
redactValue(value) {
|
|
52
|
+
const walk = engine.createSession();
|
|
53
|
+
const result = walk.redactValue(value);
|
|
54
|
+
if (active && walk.truncated) active.truncated = true;
|
|
55
|
+
return JSON.stringify(result).length <= limits.maxRecordSize
|
|
56
|
+
? result
|
|
57
|
+
: MARKERS.LIMIT;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
redact(draft) {
|
|
61
|
+
const previous = active;
|
|
62
|
+
const walk = engine.createSession();
|
|
63
|
+
active = { walk, truncated: false };
|
|
64
|
+
try {
|
|
65
|
+
let didTruncate = false;
|
|
66
|
+
const record = {};
|
|
67
|
+
for (const key of [
|
|
68
|
+
'runId',
|
|
69
|
+
'seq',
|
|
70
|
+
'at',
|
|
71
|
+
'level',
|
|
72
|
+
'area',
|
|
73
|
+
'scope',
|
|
74
|
+
'message',
|
|
75
|
+
])
|
|
76
|
+
define(
|
|
77
|
+
record,
|
|
78
|
+
key,
|
|
79
|
+
typeof draft[key] === 'string' && !['runId', 'level'].includes(key)
|
|
80
|
+
? walk.redactText(draft[key])
|
|
81
|
+
: draft[key],
|
|
82
|
+
);
|
|
83
|
+
record.redactionVersion = version;
|
|
84
|
+
if (draft.context) record.context = walk.redactValue(draft.context);
|
|
85
|
+
const data = draft.data;
|
|
86
|
+
if (isError(data)) {
|
|
87
|
+
const converted = walk.redactValue(data);
|
|
88
|
+
record.error =
|
|
89
|
+
typeof converted === 'string'
|
|
90
|
+
? { name: 'Error', message: converted }
|
|
91
|
+
: converted;
|
|
92
|
+
} else if (data !== undefined) {
|
|
93
|
+
if (
|
|
94
|
+
data &&
|
|
95
|
+
typeof data === 'object' &&
|
|
96
|
+
!Array.isArray(data) &&
|
|
97
|
+
[Object.prototype, null].includes(Object.getPrototypeOf(data))
|
|
98
|
+
) {
|
|
99
|
+
const error =
|
|
100
|
+
walk.fieldRule('error') === 'drop'
|
|
101
|
+
? undefined
|
|
102
|
+
: own(data, 'error');
|
|
103
|
+
const promoted = isError(error);
|
|
104
|
+
if (promoted) {
|
|
105
|
+
const converted = walk.redactError(error, 'error');
|
|
106
|
+
if (converted !== undefined) record.error = converted;
|
|
107
|
+
}
|
|
108
|
+
const values = {};
|
|
109
|
+
let count = 0;
|
|
110
|
+
for (const key of Reflect.ownKeys(data)) {
|
|
111
|
+
if (++count > limits.maxKeys) {
|
|
112
|
+
define(values, '[truncated]', MARKERS.LIMIT);
|
|
113
|
+
didTruncate = true;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
if (key === 'error' && promoted) continue;
|
|
117
|
+
const descriptor = Object.getOwnPropertyDescriptor(data, key);
|
|
118
|
+
if (typeof key === 'string' && descriptor?.enumerable)
|
|
119
|
+
Object.defineProperty(values, key, descriptor);
|
|
120
|
+
}
|
|
121
|
+
const converted = walk.redactValue(values);
|
|
122
|
+
record.fields =
|
|
123
|
+
converted &&
|
|
124
|
+
typeof converted === 'object' &&
|
|
125
|
+
!Array.isArray(converted)
|
|
126
|
+
? converted
|
|
127
|
+
: { value: converted };
|
|
128
|
+
} else record.fields = { value: walk.redactValue(data) };
|
|
129
|
+
}
|
|
130
|
+
if (walk.truncated || active.truncated || didTruncate)
|
|
131
|
+
record.truncated = true;
|
|
132
|
+
if (JSON.stringify(record).length > limits.maxRecordSize) {
|
|
133
|
+
delete record.fields;
|
|
134
|
+
delete record.context;
|
|
135
|
+
delete record.error;
|
|
136
|
+
record.fields = { value: MARKERS.LIMIT };
|
|
137
|
+
record.truncated = true;
|
|
138
|
+
if (JSON.stringify(record).length > limits.maxRecordSize)
|
|
139
|
+
throw new TypeError('Record headers exceed size limit');
|
|
140
|
+
}
|
|
141
|
+
return record;
|
|
142
|
+
} finally {
|
|
143
|
+
active = previous;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
if (engine.hash) api.hash = engine.hash;
|
|
148
|
+
return Object.freeze(api);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { createRedactor };
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/redaction/index.js"],"sourcesContent":["import { define, isError, own, resolveLimits } from '../core/common.js';\nimport { MARKERS } from '../core/constants.js';\n\nexport function createRedactor(\n policy = {},\n { createRedactor: createValueRedactor, TEXT_RULES: providerTextRules } = {},\n) {\n if (typeof createValueRedactor !== 'function')\n throw new TypeError('A value redactor factory is required');\n if (!Array.isArray(policy.text) && !providerTextRules)\n throw new TypeError('Default text rules are required');\n const limits = resolveLimits(policy.limits);\n const version = policy.version ?? '1';\n if (\n typeof version !== 'string' ||\n !version ||\n version.length > limits.maxStringLength\n )\n throw new TypeError('Invalid redaction version');\n const { fields, unknownFields, hash, text } = policy;\n const textRules = text ?? Object.values(providerTextRules);\n const engine = createValueRedactor({\n fields,\n unknownFields,\n hash:\n typeof hash === 'function' ? (value) => hash.call(policy, value) : hash,\n limits: { ...limits, maxOutputSize: limits.maxRecordSize },\n text: text\n ? textRules.map(({ name, pattern, requiresHash, replace }) => ({\n name,\n pattern,\n requiresHash,\n replace:\n typeof replace === 'function'\n ? function (match) {\n return replace.call(this, match, api);\n }\n : replace,\n }))\n : textRules,\n });\n let active;\n const api = {\n version,\n limits,\n mask: engine.mask,\n redactText(value) {\n return (active?.walk ?? engine.createSession()).redactText(value);\n },\n\n redactValue(value) {\n const walk = engine.createSession();\n const result = walk.redactValue(value);\n if (active && walk.truncated) active.truncated = true;\n return JSON.stringify(result).length <= limits.maxRecordSize\n ? result\n : MARKERS.LIMIT;\n },\n\n redact(draft) {\n const previous = active;\n const walk = engine.createSession();\n active = { walk, truncated: false };\n try {\n let didTruncate = false;\n const record = {};\n for (const key of [\n 'runId',\n 'seq',\n 'at',\n 'level',\n 'area',\n 'scope',\n 'message',\n ])\n define(\n record,\n key,\n typeof draft[key] === 'string' && !['runId', 'level'].includes(key)\n ? walk.redactText(draft[key])\n : draft[key],\n );\n record.redactionVersion = version;\n if (draft.context) record.context = walk.redactValue(draft.context);\n const data = draft.data;\n if (isError(data)) {\n const converted = walk.redactValue(data);\n record.error =\n typeof converted === 'string'\n ? { name: 'Error', message: converted }\n : converted;\n } else if (data !== undefined) {\n if (\n data &&\n typeof data === 'object' &&\n !Array.isArray(data) &&\n [Object.prototype, null].includes(Object.getPrototypeOf(data))\n ) {\n const error =\n walk.fieldRule('error') === 'drop'\n ? undefined\n : own(data, 'error');\n const promoted = isError(error);\n if (promoted) {\n const converted = walk.redactError(error, 'error');\n if (converted !== undefined) record.error = converted;\n }\n const values = {};\n let count = 0;\n for (const key of Reflect.ownKeys(data)) {\n if (++count > limits.maxKeys) {\n define(values, '[truncated]', MARKERS.LIMIT);\n didTruncate = true;\n break;\n }\n if (key === 'error' && promoted) continue;\n const descriptor = Object.getOwnPropertyDescriptor(data, key);\n if (typeof key === 'string' && descriptor?.enumerable)\n Object.defineProperty(values, key, descriptor);\n }\n const converted = walk.redactValue(values);\n record.fields =\n converted &&\n typeof converted === 'object' &&\n !Array.isArray(converted)\n ? converted\n : { value: converted };\n } else record.fields = { value: walk.redactValue(data) };\n }\n if (walk.truncated || active.truncated || didTruncate)\n record.truncated = true;\n if (JSON.stringify(record).length > limits.maxRecordSize) {\n delete record.fields;\n delete record.context;\n delete record.error;\n record.fields = { value: MARKERS.LIMIT };\n record.truncated = true;\n if (JSON.stringify(record).length > limits.maxRecordSize)\n throw new TypeError('Record headers exceed size limit');\n }\n return record;\n } finally {\n active = previous;\n }\n },\n };\n if (engine.hash) api.hash = engine.hash;\n return Object.freeze(api);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var constants = require('../chunk-BLf-eCDO.cjs');
|
|
4
|
+
|
|
5
|
+
const SIP_ERRORS = /* @__PURE__ */ Object.freeze({
|
|
6
|
+
CALLBACK: 'SIP display-name callback must return a string without controls',
|
|
7
|
+
RESULT_LIMIT: 'SIP display name exceeds maxStringLength',
|
|
8
|
+
OUTPUT_LIMIT: 'SIP output exceeds its size limit',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const sdpRule = (name, pattern, replace) =>
|
|
12
|
+
Object.freeze({ name, pattern, replace });
|
|
13
|
+
|
|
14
|
+
function hasUnclosedQuote(text) {
|
|
15
|
+
let quoted = false;
|
|
16
|
+
let escaped = false;
|
|
17
|
+
for (const character of text) {
|
|
18
|
+
if (escaped) {
|
|
19
|
+
escaped = false;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (character === '\\') {
|
|
23
|
+
escaped = true;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (character === '"') quoted = !quoted;
|
|
27
|
+
}
|
|
28
|
+
return quoted;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function resize(budget, change) {
|
|
32
|
+
budget.size += change;
|
|
33
|
+
if (budget.size > budget.limit) throw new RangeError(SIP_ERRORS.OUTPUT_LIMIT);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readDisplayName(value) {
|
|
37
|
+
const name = value.trim();
|
|
38
|
+
for (const character of name) {
|
|
39
|
+
const code = character.charCodeAt(0);
|
|
40
|
+
if ((code < 32 && code !== 9) || (code >= 127 && code <= 159)) return null;
|
|
41
|
+
}
|
|
42
|
+
if (name.startsWith('"')) {
|
|
43
|
+
if (!/^"(?:\\[\s\S]|[^"\\])*"$/.test(name)) return null;
|
|
44
|
+
return name.slice(1, -1).replace(/\\([\s\S])/g, '$1');
|
|
45
|
+
}
|
|
46
|
+
return /["\\]/.test(name) ? null : name;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function hasValidParameters(value) {
|
|
50
|
+
return /^(?:[ \t]*;[ \t]*[\w.!%*+`'~-]+(?:[ \t]*=[ \t]*(?:[\w.!%*+`'~-]+|\[[\da-f:.]+\]|"(?:[^"\\\r\n]|\\[^\r\n])*"))?)*[ \t]*$/i.test(
|
|
51
|
+
value,
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isBareIdentity(value) {
|
|
56
|
+
if (/^\s*(?:(?:sips?:|tel:)[^\s<>"\\]+|\*)\s*$/i.test(value)) return true;
|
|
57
|
+
const address = /^[ \t]*(?:sips?:|tel:)[^\s<>;?"\\]+/i.exec(value);
|
|
58
|
+
return Boolean(address) && hasValidParameters(value.slice(address[0].length));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseIdentity(value) {
|
|
62
|
+
const parts = [];
|
|
63
|
+
let start = 0;
|
|
64
|
+
let angle = -1;
|
|
65
|
+
let close = -1;
|
|
66
|
+
let inAddress = false;
|
|
67
|
+
let quoted = false;
|
|
68
|
+
let escaped = false;
|
|
69
|
+
const append = (end) => {
|
|
70
|
+
const segment = value.slice(start, end);
|
|
71
|
+
let name = null;
|
|
72
|
+
if (angle < 0) {
|
|
73
|
+
if (!isBareIdentity(segment)) return false;
|
|
74
|
+
} else {
|
|
75
|
+
if (!hasValidParameters(value.slice(close + 1, end))) return false;
|
|
76
|
+
name = readDisplayName(value.slice(start, angle));
|
|
77
|
+
if (name === null) return false;
|
|
78
|
+
}
|
|
79
|
+
parts.push({ start, end, angle, name });
|
|
80
|
+
return true;
|
|
81
|
+
};
|
|
82
|
+
for (let index = 0; index < value.length; index++) {
|
|
83
|
+
const character = value[index];
|
|
84
|
+
if (escaped) {
|
|
85
|
+
escaped = false;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (quoted && character === '\\') {
|
|
89
|
+
escaped = true;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (character === '"') quoted = !quoted;
|
|
93
|
+
if (quoted) continue;
|
|
94
|
+
if (character === '<') {
|
|
95
|
+
if (
|
|
96
|
+
angle >= 0 ||
|
|
97
|
+
!/^(?:sips?:|tel:)/i.test(value.slice(index + 1, index + 6))
|
|
98
|
+
)
|
|
99
|
+
return null;
|
|
100
|
+
angle = index;
|
|
101
|
+
inAddress = true;
|
|
102
|
+
} else if (character === '>') {
|
|
103
|
+
if (
|
|
104
|
+
!inAddress ||
|
|
105
|
+
!/^(?:sips?:|tel:)[^\s<>"\\]+$/i.test(value.slice(angle + 1, index))
|
|
106
|
+
)
|
|
107
|
+
return null;
|
|
108
|
+
inAddress = false;
|
|
109
|
+
close = index;
|
|
110
|
+
} else if (character === ',' && !inAddress) {
|
|
111
|
+
if (!append(index)) return null;
|
|
112
|
+
start = index + 1;
|
|
113
|
+
angle = -1;
|
|
114
|
+
close = -1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return quoted || inAddress || !append(value.length) ? null : parts;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function redactIdentity(value, redactor, redactDisplayName, budget) {
|
|
121
|
+
const segments = parseIdentity(value);
|
|
122
|
+
if (!segments) {
|
|
123
|
+
const replacement = ` ${constants.MARKERS.MASK}`;
|
|
124
|
+
resize(budget, replacement.length - value.length);
|
|
125
|
+
return replacement;
|
|
126
|
+
}
|
|
127
|
+
const parts = [];
|
|
128
|
+
for (const { start, end, angle, name } of segments) {
|
|
129
|
+
if (name === null) {
|
|
130
|
+
parts.push(value.slice(start, end));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
let result = '';
|
|
134
|
+
if (name)
|
|
135
|
+
result = redactDisplayName ? redactDisplayName(name) : constants.MARKERS.MASK;
|
|
136
|
+
if (typeof result !== 'string') throw new TypeError(SIP_ERRORS.CALLBACK);
|
|
137
|
+
if (result.length > redactor.limits.maxStringLength)
|
|
138
|
+
throw new RangeError(SIP_ERRORS.RESULT_LIMIT);
|
|
139
|
+
let length = result ? 4 + result.length : 1;
|
|
140
|
+
for (const character of result) {
|
|
141
|
+
const code = character.charCodeAt(0);
|
|
142
|
+
if (code < 32 || (code >= 127 && code <= 159))
|
|
143
|
+
throw new TypeError(SIP_ERRORS.CALLBACK);
|
|
144
|
+
if (character === '"' || character === '\\') length++;
|
|
145
|
+
}
|
|
146
|
+
resize(budget, length - (angle - start));
|
|
147
|
+
const prefix = result ? ` "${result.replace(/["\\]/g, '\\$&')}" ` : ' ';
|
|
148
|
+
parts.push(`${prefix}${value.slice(angle, end)}`);
|
|
149
|
+
}
|
|
150
|
+
return parts.join(',');
|
|
151
|
+
}
|
|
152
|
+
function createSdpTextRules(TEXT_RULES) {
|
|
153
|
+
return Object.freeze([
|
|
154
|
+
sdpRule(
|
|
155
|
+
'ice',
|
|
156
|
+
/a=ice-(?:pwd|ufrag):[^\r\n\\"]*/gi,
|
|
157
|
+
(match) => `${match.slice(0, match.indexOf(':') + 1)}[redacted]`,
|
|
158
|
+
),
|
|
159
|
+
sdpRule('sdes', /a=crypto:[^\r\n\\"]*/gi, () => 'a=crypto:[redacted]'),
|
|
160
|
+
sdpRule(
|
|
161
|
+
'fingerprint',
|
|
162
|
+
/a=fingerprint:[^\r\n\\"]*/gi,
|
|
163
|
+
() => 'a=fingerprint:[redacted]',
|
|
164
|
+
),
|
|
165
|
+
sdpRule(
|
|
166
|
+
'origin',
|
|
167
|
+
/(?:^|[\r\n]|\\[rn])o=[^\s]+/g,
|
|
168
|
+
(match) => `${match.slice(0, match.indexOf('o=') + 2)}[redacted]`,
|
|
169
|
+
),
|
|
170
|
+
sdpRule(
|
|
171
|
+
'mediaIdentity',
|
|
172
|
+
/a=(?:msid|identity):[^\r\n\\"]*/gi,
|
|
173
|
+
(match) => `${match.slice(0, match.indexOf(':') + 1)}[redacted]`,
|
|
174
|
+
),
|
|
175
|
+
sdpRule(
|
|
176
|
+
'sourceIdentity',
|
|
177
|
+
/(?:^|[\r\n])a=ssrc:\d+[ \t]+(?:cname|msid|mslabel|label):[^\r\n]*/gi,
|
|
178
|
+
(match) =>
|
|
179
|
+
`${match.slice(0, match.indexOf(':', match.indexOf(':') + 1) + 1)}[redacted]`,
|
|
180
|
+
),
|
|
181
|
+
sdpRule(
|
|
182
|
+
'serializedSourceIdentity',
|
|
183
|
+
/(?:\\[rn]|(?<="))a=ssrc:\d+[ \t]+(?:cname|msid|mslabel|label):(?:\\(?![rn])[^\r\n]|[^\r\n\\"])*/gi,
|
|
184
|
+
(match) =>
|
|
185
|
+
`${match.slice(0, match.indexOf(':', match.indexOf(':') + 1) + 1)}[redacted]`,
|
|
186
|
+
),
|
|
187
|
+
sdpRule(
|
|
188
|
+
'localAddress',
|
|
189
|
+
/(?<![\w-])[\w-]+\.local\b/gi,
|
|
190
|
+
() => '[local-address]',
|
|
191
|
+
),
|
|
192
|
+
sdpRule(
|
|
193
|
+
'turn',
|
|
194
|
+
/(?:"(?:credential|password|username)"\s*:\s*(?:"(?:\\.|[^"\\])*"|[^,}\s]+)|(?:credential|password|username)\s*=\s*(?:"(?:\\.|[^"\\])*"|[^,}\s]+))/gi,
|
|
195
|
+
(match) => `${match.slice(0, match.search(/[:=]/) + 1)}"[redacted]"`,
|
|
196
|
+
),
|
|
197
|
+
TEXT_RULES.ipv4,
|
|
198
|
+
TEXT_RULES.ipv6,
|
|
199
|
+
TEXT_RULES.urlCredentials,
|
|
200
|
+
TEXT_RULES.sipUri,
|
|
201
|
+
TEXT_RULES.telUri,
|
|
202
|
+
sdpRule('dtmf', /Signal\s*=\s*[^\r\n]+/gi, () => 'Signal=*'),
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function describeSipMessage(text) {
|
|
207
|
+
const first = text.split(/\r?\n/, 1)[0];
|
|
208
|
+
const result = {};
|
|
209
|
+
if (/^SIP\/2\.0\s+\d{3}/i.test(first))
|
|
210
|
+
result.status = Number(first.split(/\s+/)[1]);
|
|
211
|
+
else {
|
|
212
|
+
const match = /^([A-Z]+)\s+\S+\s+SIP\/2\.0/i.exec(first);
|
|
213
|
+
if (match) result.method = match[1];
|
|
214
|
+
}
|
|
215
|
+
const callId = /^(?:Call-ID|i)\s*:\s*([^\r\n]+)/im.exec(text);
|
|
216
|
+
const cseq = /^CSeq\s*:\s*([^\r\n]+)/im.exec(text);
|
|
217
|
+
if (callId) result.callId = callId[1].trim();
|
|
218
|
+
if (cseq) result.cseq = cseq[1].trim();
|
|
219
|
+
return Object.freeze(result);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function redactSipMessage(
|
|
223
|
+
text,
|
|
224
|
+
{ redactor, redactDisplayName },
|
|
225
|
+
TEXT_RULES,
|
|
226
|
+
SDP_TEXT_RULES,
|
|
227
|
+
) {
|
|
228
|
+
if (
|
|
229
|
+
redactDisplayName !== undefined &&
|
|
230
|
+
typeof redactDisplayName !== 'function'
|
|
231
|
+
)
|
|
232
|
+
throw new TypeError(SIP_ERRORS.CALLBACK);
|
|
233
|
+
const limit = redactor.limits.maxStringLength * 4;
|
|
234
|
+
if (text.length > limit) return constants.MARKERS.LIMIT;
|
|
235
|
+
const unfolded = text.replace(/\r?\n[ \t]+/g, ' ');
|
|
236
|
+
const separator = /\r?\n\r?\n/.exec(unfolded);
|
|
237
|
+
let headers = separator ? unfolded.slice(0, separator.index) : unfolded;
|
|
238
|
+
let body = separator
|
|
239
|
+
? unfolded.slice(separator.index + separator[0].length)
|
|
240
|
+
: '';
|
|
241
|
+
headers = headers.split(/\r?\n/).join('\r\n');
|
|
242
|
+
const budget = {
|
|
243
|
+
size: headers.length + body.length + (separator ? 4 : 0),
|
|
244
|
+
limit,
|
|
245
|
+
};
|
|
246
|
+
resize(budget, 0);
|
|
247
|
+
const replace = (value, pattern, render) =>
|
|
248
|
+
value.replace(pattern, (match) => {
|
|
249
|
+
const replacement = render(match);
|
|
250
|
+
resize(budget, replacement.length - match.length);
|
|
251
|
+
return replacement;
|
|
252
|
+
});
|
|
253
|
+
const contentType =
|
|
254
|
+
/^(?:Content-Type|c)\s*:\s*([^;\r\n]+)/im
|
|
255
|
+
.exec(headers)?.[1]
|
|
256
|
+
.trim()
|
|
257
|
+
.toLowerCase() ?? 'unknown';
|
|
258
|
+
headers = replace(
|
|
259
|
+
headers,
|
|
260
|
+
/^(?:(?:Proxy-)?Authorization|WWW-Authenticate|Proxy-Authenticate)\s*:[^\r\n]*/gim,
|
|
261
|
+
(header) =>
|
|
262
|
+
hasUnclosedQuote(header)
|
|
263
|
+
? `${header.slice(0, header.indexOf(':'))}: [redacted]`
|
|
264
|
+
: header.replace(
|
|
265
|
+
/\b(username|response|nonce|cnonce|opaque)\s*=\s*(?:"(?:\\.|[^"\\])*"|[^,\s]+)/gi,
|
|
266
|
+
(_, key) => `${key}="[redacted]"`,
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
headers = headers
|
|
270
|
+
.split(/\r?\n/)
|
|
271
|
+
.map((line) => {
|
|
272
|
+
const header =
|
|
273
|
+
/^((?:From|To|Contact|P-Asserted-Identity|P-Preferred-Identity|Refer-To|Referred-By|Diversion|History-Info|Remote-Party-ID|P-Called-Party-ID|Reply-To|f|t|m|r|b)\s*:)(.*)$/i.exec(
|
|
274
|
+
line,
|
|
275
|
+
);
|
|
276
|
+
if (!header) return line;
|
|
277
|
+
const value = redactIdentity(
|
|
278
|
+
header[2],
|
|
279
|
+
redactor,
|
|
280
|
+
redactDisplayName,
|
|
281
|
+
budget,
|
|
282
|
+
);
|
|
283
|
+
return `${header[1]}${value}`;
|
|
284
|
+
})
|
|
285
|
+
.join('\r\n');
|
|
286
|
+
for (const rule of [
|
|
287
|
+
TEXT_RULES.urlCredentials,
|
|
288
|
+
TEXT_RULES.sipUri,
|
|
289
|
+
TEXT_RULES.telUri,
|
|
290
|
+
])
|
|
291
|
+
headers = replace(headers, rule.pattern, (match) => rule.replace(match));
|
|
292
|
+
if (body) {
|
|
293
|
+
if (
|
|
294
|
+
contentType === 'application/sdp' ||
|
|
295
|
+
contentType === 'application/dtmf-relay'
|
|
296
|
+
) {
|
|
297
|
+
for (const rule of SDP_TEXT_RULES)
|
|
298
|
+
body = replace(body, rule.pattern, (match) => rule.replace(match));
|
|
299
|
+
} else {
|
|
300
|
+
const replacement = `[${contentType} ${new TextEncoder().encode(body).length} bytes]`;
|
|
301
|
+
resize(budget, replacement.length - body.length);
|
|
302
|
+
body = replacement;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const addresses = (value) => {
|
|
306
|
+
const ipv4 = replace(value, TEXT_RULES.ipv4.pattern, (match) =>
|
|
307
|
+
TEXT_RULES.ipv4.replace(match),
|
|
308
|
+
);
|
|
309
|
+
return replace(ipv4, TEXT_RULES.ipv6.pattern, (match) =>
|
|
310
|
+
TEXT_RULES.ipv6.replace(match),
|
|
311
|
+
);
|
|
312
|
+
};
|
|
313
|
+
return `${addresses(headers)}${separator ? `\r\n\r\n${body}` : ''}`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function createSipTools(textRules) {
|
|
317
|
+
if (
|
|
318
|
+
!textRules?.ipv4 ||
|
|
319
|
+
!textRules.ipv6 ||
|
|
320
|
+
!textRules.urlCredentials ||
|
|
321
|
+
!textRules.sipUri ||
|
|
322
|
+
!textRules.telUri
|
|
323
|
+
)
|
|
324
|
+
throw new TypeError('SIP text rules are required');
|
|
325
|
+
const SDP_TEXT_RULES = createSdpTextRules(textRules);
|
|
326
|
+
return Object.freeze({
|
|
327
|
+
SDP_TEXT_RULES,
|
|
328
|
+
redactSipMessage: (text, options) =>
|
|
329
|
+
redactSipMessage(text, options, textRules, SDP_TEXT_RULES),
|
|
330
|
+
describeSipMessage,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
exports.createSipTools = createSipTools;
|
|
335
|
+
exports.describeSipMessage = describeSipMessage;
|
|
336
|
+
//# sourceMappingURL=index.cjs.map
|