@duckbug/js 1.0.0 → 1.1.1
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 +17 -0
- package/dist/cjs/SDK/DuckSDK.cjs +14 -3
- package/dist/cjs/SDK/duckLogPayload.cjs +86 -0
- package/dist/esm/SDK/DuckSDK.js +14 -3
- package/dist/esm/SDK/duckLogPayload.js +52 -0
- package/dist/types/SDK/DuckSDK.d.ts +6 -5
- package/dist/types/SDK/duckLogPayload.d.ts +23 -0
- package/dist/types/SDK/index.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,6 +63,23 @@ duck.warn('Slow query', { ms: 1200 });
|
|
|
63
63
|
|
|
64
64
|
Before process exit, await `duck.flush()` so queued HTTP work finishes (for example at the end of an `async main()`).
|
|
65
65
|
|
|
66
|
+
### Reserved `_duck` on log payloads
|
|
67
|
+
|
|
68
|
+
The second argument to `log` / `debug` / `warn` / `error` / `fatal` may be a plain object with a reserved **`_duck`** field for technical metadata. **`_duck` is not sent inside `context`** on the wire.
|
|
69
|
+
|
|
70
|
+
- **`_duck.dTags`** — sets top-level `dTags` on the ingest event (same as in a raw JSON body).
|
|
71
|
+
- **`_duck.scope`** — one-shot `Partial<IngestSharedMetadata>` merged into **this** log only (after global `setScope`, before fixed fields). Use e.g. `platform: "ios"` to override the default `"node"`. Optional **`_duck.scope.context`** is used as the event `context` only when there are no other keys besides `_duck`.
|
|
72
|
+
|
|
73
|
+
Any other keys on the object become **`context`**, which matches typical ingest JSON (`dTags` at the root, domain fields under `context`).
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
duck.warn("DUCKBUG_DTAGS_SMOKE_TEST", {
|
|
77
|
+
source: "initDuckBugDeviceContext",
|
|
78
|
+
platform: "ios",
|
|
79
|
+
_duck: { dTags: ["smoke-test", "dtags"] },
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
66
83
|
## Full usage example
|
|
67
84
|
|
|
68
85
|
End-to-end pattern for a Node/Bun service: DSN from env, scope (release, user, fingerprint), privacy pipeline, `beforeSend`, batched transport with retries, transport errors, console forwarding, global error handlers, structured logs, manual errors, and clean shutdown.
|
package/dist/cjs/SDK/DuckSDK.cjs
CHANGED
|
@@ -30,6 +30,7 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
30
30
|
const DuckBugHelper_cjs_namespaceObject = require("../DuckBug/DuckBugHelper.cjs");
|
|
31
31
|
const finalizeIngestEvent_cjs_namespaceObject = require("../DuckBug/finalizeIngestEvent.cjs");
|
|
32
32
|
const external_sdkIdentity_cjs_namespaceObject = require("../sdkIdentity.cjs");
|
|
33
|
+
const external_duckLogPayload_cjs_namespaceObject = require("./duckLogPayload.cjs");
|
|
33
34
|
const external_LogLevel_cjs_namespaceObject = require("./LogLevel.cjs");
|
|
34
35
|
const external_LogProvider_cjs_namespaceObject = require("./LogProvider.cjs");
|
|
35
36
|
function isPromiseLike(v) {
|
|
@@ -118,16 +119,26 @@ class DuckSDK {
|
|
|
118
119
|
return out;
|
|
119
120
|
}
|
|
120
121
|
emitLog(level, message, payload) {
|
|
122
|
+
const { scopePatch, context, dTags } = (0, external_duckLogPayload_cjs_namespaceObject.parseDuckLogPayload)(payload);
|
|
123
|
+
const platform = void 0 !== scopePatch.platform && "string" == typeof scopePatch.platform ? scopePatch.platform : "node";
|
|
124
|
+
const scopeForSpread = {
|
|
125
|
+
...scopePatch
|
|
126
|
+
};
|
|
127
|
+
delete scopeForSpread.platform;
|
|
121
128
|
const event = this.mergeScope({
|
|
129
|
+
...scopeForSpread,
|
|
122
130
|
time: Date.now(),
|
|
123
131
|
level,
|
|
124
132
|
message,
|
|
125
|
-
platform
|
|
133
|
+
platform,
|
|
126
134
|
sdk: {
|
|
127
135
|
...external_sdkIdentity_cjs_namespaceObject.SDK_IDENTITY
|
|
128
136
|
},
|
|
129
|
-
...void 0 !==
|
|
130
|
-
|
|
137
|
+
...void 0 !== dTags ? {
|
|
138
|
+
dTags
|
|
139
|
+
} : {},
|
|
140
|
+
...void 0 !== context ? {
|
|
141
|
+
context
|
|
131
142
|
} : {}
|
|
132
143
|
});
|
|
133
144
|
const finalized = (0, finalizeIngestEvent_cjs_namespaceObject.finalizeIngestEvent)(event, {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.d = (exports1, definition)=>{
|
|
5
|
+
for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: definition[key]
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
})();
|
|
11
|
+
(()=>{
|
|
12
|
+
__webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
|
|
13
|
+
})();
|
|
14
|
+
(()=>{
|
|
15
|
+
__webpack_require__.r = (exports1)=>{
|
|
16
|
+
if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
|
|
17
|
+
value: 'Module'
|
|
18
|
+
});
|
|
19
|
+
Object.defineProperty(exports1, '__esModule', {
|
|
20
|
+
value: true
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
})();
|
|
24
|
+
var __webpack_exports__ = {};
|
|
25
|
+
__webpack_require__.r(__webpack_exports__);
|
|
26
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
+
parseDuckLogPayload: ()=>parseDuckLogPayload
|
|
28
|
+
});
|
|
29
|
+
const EMIT_OWNED = [
|
|
30
|
+
"time",
|
|
31
|
+
"level",
|
|
32
|
+
"message",
|
|
33
|
+
"sdk"
|
|
34
|
+
];
|
|
35
|
+
function parseDuckLogPayload(payload) {
|
|
36
|
+
if (void 0 === payload) return {
|
|
37
|
+
scopePatch: {},
|
|
38
|
+
context: void 0,
|
|
39
|
+
dTags: void 0
|
|
40
|
+
};
|
|
41
|
+
if (null === payload || Array.isArray(payload) || "object" != typeof payload) return {
|
|
42
|
+
scopePatch: {},
|
|
43
|
+
context: payload,
|
|
44
|
+
dTags: void 0
|
|
45
|
+
};
|
|
46
|
+
const rec = {
|
|
47
|
+
...payload
|
|
48
|
+
};
|
|
49
|
+
const rawDuck = rec._duck;
|
|
50
|
+
delete rec._duck;
|
|
51
|
+
let reserved;
|
|
52
|
+
if (null !== rawDuck && "object" == typeof rawDuck && !Array.isArray(rawDuck)) reserved = rawDuck;
|
|
53
|
+
const hasRest = Object.keys(rec).length > 0;
|
|
54
|
+
const restContext = hasRest ? rec : void 0;
|
|
55
|
+
let scopeContext;
|
|
56
|
+
let scopePatch = {};
|
|
57
|
+
const inner = reserved?.scope;
|
|
58
|
+
if (void 0 !== inner && "object" == typeof inner && !Array.isArray(inner)) {
|
|
59
|
+
const s = inner;
|
|
60
|
+
scopeContext = s.context;
|
|
61
|
+
const { context: _c, ...restScope } = s;
|
|
62
|
+
scopePatch = {
|
|
63
|
+
...restScope
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
for (const k of EMIT_OWNED)delete scopePatch[k];
|
|
67
|
+
let context;
|
|
68
|
+
if (hasRest) context = restContext;
|
|
69
|
+
else if (void 0 !== scopeContext) context = scopeContext;
|
|
70
|
+
let dTags;
|
|
71
|
+
if (reserved?.dTags !== void 0) dTags = reserved.dTags;
|
|
72
|
+
else if (void 0 !== scopePatch.dTags) dTags = scopePatch.dTags;
|
|
73
|
+
const { dTags: _scopeDtags, ...scopePatchNoDtags } = scopePatch;
|
|
74
|
+
return {
|
|
75
|
+
scopePatch: scopePatchNoDtags,
|
|
76
|
+
context,
|
|
77
|
+
dTags
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
exports.parseDuckLogPayload = __webpack_exports__.parseDuckLogPayload;
|
|
81
|
+
for(var __webpack_i__ in __webpack_exports__)if (-1 === [
|
|
82
|
+
"parseDuckLogPayload"
|
|
83
|
+
].indexOf(__webpack_i__)) exports[__webpack_i__] = __webpack_exports__[__webpack_i__];
|
|
84
|
+
Object.defineProperty(exports, '__esModule', {
|
|
85
|
+
value: true
|
|
86
|
+
});
|
package/dist/esm/SDK/DuckSDK.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { processError } from "../DuckBug/DuckBugHelper.js";
|
|
2
2
|
import { finalizeIngestEvent } from "../DuckBug/finalizeIngestEvent.js";
|
|
3
3
|
import { SDK_IDENTITY } from "../sdkIdentity.js";
|
|
4
|
+
import { parseDuckLogPayload } from "./duckLogPayload.js";
|
|
4
5
|
import { logLevel } from "./LogLevel.js";
|
|
5
6
|
import { LogProvider } from "./LogProvider.js";
|
|
6
7
|
function isPromiseLike(v) {
|
|
@@ -89,16 +90,26 @@ class DuckSDK {
|
|
|
89
90
|
return out;
|
|
90
91
|
}
|
|
91
92
|
emitLog(level, message, payload) {
|
|
93
|
+
const { scopePatch, context, dTags } = parseDuckLogPayload(payload);
|
|
94
|
+
const platform = void 0 !== scopePatch.platform && "string" == typeof scopePatch.platform ? scopePatch.platform : "node";
|
|
95
|
+
const scopeForSpread = {
|
|
96
|
+
...scopePatch
|
|
97
|
+
};
|
|
98
|
+
delete scopeForSpread.platform;
|
|
92
99
|
const event = this.mergeScope({
|
|
100
|
+
...scopeForSpread,
|
|
93
101
|
time: Date.now(),
|
|
94
102
|
level,
|
|
95
103
|
message,
|
|
96
|
-
platform
|
|
104
|
+
platform,
|
|
97
105
|
sdk: {
|
|
98
106
|
...SDK_IDENTITY
|
|
99
107
|
},
|
|
100
|
-
...void 0 !==
|
|
101
|
-
|
|
108
|
+
...void 0 !== dTags ? {
|
|
109
|
+
dTags
|
|
110
|
+
} : {},
|
|
111
|
+
...void 0 !== context ? {
|
|
112
|
+
context
|
|
102
113
|
} : {}
|
|
103
114
|
});
|
|
104
115
|
const finalized = finalizeIngestEvent(event, {
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const EMIT_OWNED = [
|
|
2
|
+
"time",
|
|
3
|
+
"level",
|
|
4
|
+
"message",
|
|
5
|
+
"sdk"
|
|
6
|
+
];
|
|
7
|
+
function parseDuckLogPayload(payload) {
|
|
8
|
+
if (void 0 === payload) return {
|
|
9
|
+
scopePatch: {},
|
|
10
|
+
context: void 0,
|
|
11
|
+
dTags: void 0
|
|
12
|
+
};
|
|
13
|
+
if (null === payload || Array.isArray(payload) || "object" != typeof payload) return {
|
|
14
|
+
scopePatch: {},
|
|
15
|
+
context: payload,
|
|
16
|
+
dTags: void 0
|
|
17
|
+
};
|
|
18
|
+
const rec = {
|
|
19
|
+
...payload
|
|
20
|
+
};
|
|
21
|
+
const rawDuck = rec._duck;
|
|
22
|
+
delete rec._duck;
|
|
23
|
+
let reserved;
|
|
24
|
+
if (null !== rawDuck && "object" == typeof rawDuck && !Array.isArray(rawDuck)) reserved = rawDuck;
|
|
25
|
+
const hasRest = Object.keys(rec).length > 0;
|
|
26
|
+
const restContext = hasRest ? rec : void 0;
|
|
27
|
+
let scopeContext;
|
|
28
|
+
let scopePatch = {};
|
|
29
|
+
const inner = reserved?.scope;
|
|
30
|
+
if (void 0 !== inner && "object" == typeof inner && !Array.isArray(inner)) {
|
|
31
|
+
const s = inner;
|
|
32
|
+
scopeContext = s.context;
|
|
33
|
+
const { context: _c, ...restScope } = s;
|
|
34
|
+
scopePatch = {
|
|
35
|
+
...restScope
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
for (const k of EMIT_OWNED)delete scopePatch[k];
|
|
39
|
+
let context;
|
|
40
|
+
if (hasRest) context = restContext;
|
|
41
|
+
else if (void 0 !== scopeContext) context = scopeContext;
|
|
42
|
+
let dTags;
|
|
43
|
+
if (reserved?.dTags !== void 0) dTags = reserved.dTags;
|
|
44
|
+
else if (void 0 !== scopePatch.dTags) dTags = scopePatch.dTags;
|
|
45
|
+
const { dTags: _scopeDtags, ...scopePatchNoDtags } = scopePatch;
|
|
46
|
+
return {
|
|
47
|
+
scopePatch: scopePatchNoDtags,
|
|
48
|
+
context,
|
|
49
|
+
dTags
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export { parseDuckLogPayload };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IngestSharedMetadata } from "../contract";
|
|
2
2
|
import type { BeforeSendIngestArg, BeforeSendIngestResult } from "../DuckBug/DuckBugConfig";
|
|
3
3
|
import type { StrippableIngestSection } from "../DuckBug/stripIngestSections";
|
|
4
|
+
import { type DuckLogPayload } from "./duckLogPayload";
|
|
4
5
|
import type { LogProviderConfig } from "./LogProviderConfig";
|
|
5
6
|
import type { Provider } from "./Provider";
|
|
6
7
|
export type DuckSDKOptions = {
|
|
@@ -22,11 +23,11 @@ export declare class DuckSDK {
|
|
|
22
23
|
* (e.g. {@link DuckBugProvider}).
|
|
23
24
|
*/
|
|
24
25
|
flush(): Promise<void>;
|
|
25
|
-
log(tag: string, payload?:
|
|
26
|
-
error(tag: string, payload?:
|
|
27
|
-
debug(tag: string, payload?:
|
|
28
|
-
warn(tag: string, payload?:
|
|
29
|
-
fatal(tag: string, payload?:
|
|
26
|
+
log(tag: string, payload?: DuckLogPayload): void;
|
|
27
|
+
error(tag: string, payload?: DuckLogPayload): void;
|
|
28
|
+
debug(tag: string, payload?: DuckLogPayload): void;
|
|
29
|
+
warn(tag: string, payload?: DuckLogPayload): void;
|
|
30
|
+
fatal(tag: string, payload?: DuckLogPayload): void;
|
|
30
31
|
quack(tag: string, error: Error): void;
|
|
31
32
|
private normalizeBeforeSendLog;
|
|
32
33
|
private normalizeBeforeSendError;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IngestJsonValue, IngestSharedMetadata } from "../contract";
|
|
2
|
+
/**
|
|
3
|
+
* Reserved key on the second argument to `Duck` / `DuckSDK` log methods.
|
|
4
|
+
* Not sent inside `context` on the wire.
|
|
5
|
+
*/
|
|
6
|
+
export type DuckReservedMeta = {
|
|
7
|
+
dTags?: string[];
|
|
8
|
+
/** One-shot metadata merged into this log only (after global `setScope`, before fixed fields). */
|
|
9
|
+
scope?: Partial<IngestSharedMetadata>;
|
|
10
|
+
};
|
|
11
|
+
export type DuckLogPayload = Record<string, unknown> & {
|
|
12
|
+
_duck?: DuckReservedMeta;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Splits the optional second argument into scope-like fields, `dTags`, and `context`.
|
|
16
|
+
* - Plain object: strips `_duck`, remaining keys become `context` if non-empty.
|
|
17
|
+
* - Non-plain-object payload: entire value becomes `context` (legacy).
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseDuckLogPayload(payload: object | undefined): {
|
|
20
|
+
scopePatch: Partial<IngestSharedMetadata>;
|
|
21
|
+
context: IngestJsonValue | undefined;
|
|
22
|
+
dTags: string[] | undefined;
|
|
23
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export type { Provider, SendEventMeta } from "../SDK/Provider";
|
|
2
2
|
export type { DuckSDKOptions } from "./DuckSDK";
|
|
3
3
|
export { Duck, DuckSDK } from "./DuckSDK";
|
|
4
|
+
export type { DuckLogPayload, DuckReservedMeta, } from "./duckLogPayload";
|
|
4
5
|
export type { LogLevel } from "./LogLevel";
|
|
5
6
|
export { logLevel } from "./LogLevel";
|
|
6
7
|
export { LogProvider } from "./LogProvider";
|