@launchdarkly/js-sdk-common 2.19.0 → 2.21.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 +14 -0
- package/dist/cjs/index.cjs +261 -160
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/internal/fdv2/FDv1PayloadAdaptor.d.ts +43 -0
- package/dist/cjs/internal/fdv2/FDv1PayloadAdaptor.d.ts.map +1 -0
- package/dist/cjs/internal/fdv2/index.d.ts +7 -2
- package/dist/cjs/internal/fdv2/index.d.ts.map +1 -1
- package/dist/cjs/internal/fdv2/payloadProcessor.d.ts +7 -56
- package/dist/cjs/internal/fdv2/payloadProcessor.d.ts.map +1 -1
- package/dist/cjs/internal/fdv2/payloadStreamReader.d.ts +1 -1
- package/dist/cjs/internal/fdv2/payloadStreamReader.d.ts.map +1 -1
- package/dist/cjs/internal/fdv2/proto.d.ts +29 -6
- package/dist/cjs/internal/fdv2/proto.d.ts.map +1 -1
- package/dist/cjs/internal/fdv2/protocolHandler.d.ts +76 -0
- package/dist/cjs/internal/fdv2/protocolHandler.d.ts.map +1 -0
- package/dist/esm/index.mjs +261 -160
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/internal/fdv2/FDv1PayloadAdaptor.d.ts +43 -0
- package/dist/esm/internal/fdv2/FDv1PayloadAdaptor.d.ts.map +1 -0
- package/dist/esm/internal/fdv2/index.d.ts +7 -2
- package/dist/esm/internal/fdv2/index.d.ts.map +1 -1
- package/dist/esm/internal/fdv2/payloadProcessor.d.ts +7 -56
- package/dist/esm/internal/fdv2/payloadProcessor.d.ts.map +1 -1
- package/dist/esm/internal/fdv2/payloadStreamReader.d.ts +1 -1
- package/dist/esm/internal/fdv2/payloadStreamReader.d.ts.map +1 -1
- package/dist/esm/internal/fdv2/proto.d.ts +29 -6
- package/dist/esm/internal/fdv2/proto.d.ts.map +1 -1
- package/dist/esm/internal/fdv2/protocolHandler.d.ts +76 -0
- package/dist/esm/internal/fdv2/protocolHandler.d.ts.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payloadStreamReader.d.ts","sourceRoot":"","sources":["../../../src/internal/fdv2/payloadStreamReader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"payloadStreamReader.d.ts","sourceRoot":"","sources":["../../../src/internal/fdv2/payloadStreamReader.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;CAClE;AAED;;;GAGG;AACH,qBAAa,mBAAmB;IAc5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAd3B,OAAO,CAAC,iBAAiB,CAAmB;IAE5C;;;;;;;OAOG;gBAED,WAAW,EAAE,WAAW,EACxB,cAAc,EAAE,aAAa,EACZ,aAAa,CAAC,eAAc,mBAAmB,WAAW,MAAM,KAAK,IAAI,aAAA,EACzE,OAAO,CAAC,sBAAU;IAWrC,kBAAkB,CAAC,QAAQ,EAAE,eAAe;IAI5C,qBAAqB,CAAC,QAAQ,EAAE,eAAe;IAI/C,OAAO,CAAC,cAAc;CAoBvB"}
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export type EventType = 'server-intent' | 'put-object' | 'delete-object' | 'payload-transferred' | 'goodbye' | 'error' | 'heart-beat';
|
|
2
|
+
export type IntentCode = 'xfer-full' | 'xfer-changes' | 'none';
|
|
3
|
+
export type ObjectKind = 'flag' | 'segment';
|
|
4
|
+
export interface FDv2Event {
|
|
5
|
+
/**
|
|
6
|
+
* The event type could be one we know, or it could be any string.
|
|
7
|
+
* This is for forward compatibility and to make it clear the protocol may send us types we don't recognize.
|
|
8
|
+
*/
|
|
9
|
+
event: EventType | string;
|
|
10
|
+
/**
|
|
11
|
+
* Could be one of many known types, or an unknown type.
|
|
12
|
+
* The unknown type is to handle forward compatibility.
|
|
13
|
+
*/
|
|
14
|
+
data: ServerIntentData | PutObject | DeleteObject | PayloadTransferred | GoodbyeObject | ErrorObject | unknown;
|
|
15
|
+
}
|
|
16
|
+
export interface FDv2EventsCollection {
|
|
17
|
+
events: FDv2Event[];
|
|
4
18
|
}
|
|
5
19
|
export interface ServerIntentData {
|
|
6
20
|
payloads: PayloadIntent[];
|
|
7
21
|
}
|
|
8
|
-
export type IntentCode = 'xfer-full' | 'xfer-changes' | 'none';
|
|
9
22
|
export interface PayloadIntent {
|
|
10
23
|
id: string;
|
|
11
24
|
target: number;
|
|
@@ -13,18 +26,28 @@ export interface PayloadIntent {
|
|
|
13
26
|
reason: string;
|
|
14
27
|
}
|
|
15
28
|
export interface PutObject {
|
|
16
|
-
kind:
|
|
29
|
+
kind: ObjectKind;
|
|
17
30
|
key: string;
|
|
18
31
|
version: number;
|
|
19
32
|
object: any;
|
|
20
33
|
}
|
|
21
34
|
export interface DeleteObject {
|
|
22
|
-
kind:
|
|
35
|
+
kind: ObjectKind;
|
|
23
36
|
key: string;
|
|
24
37
|
version: number;
|
|
25
38
|
}
|
|
39
|
+
export interface GoodbyeObject {
|
|
40
|
+
reason: string;
|
|
41
|
+
silent: boolean;
|
|
42
|
+
catastrophe: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ErrorObject {
|
|
45
|
+
payload_id: string;
|
|
46
|
+
reason: string;
|
|
47
|
+
}
|
|
26
48
|
export interface PayloadTransferred {
|
|
27
49
|
state: string;
|
|
28
50
|
version: number;
|
|
51
|
+
id?: string;
|
|
29
52
|
}
|
|
30
53
|
//# sourceMappingURL=proto.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proto.d.ts","sourceRoot":"","sources":["../../../src/internal/fdv2/proto.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"proto.d.ts","sourceRoot":"","sources":["../../../src/internal/fdv2/proto.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GACjB,eAAe,GACf,YAAY,GACZ,eAAe,GACf,qBAAqB,GACrB,SAAS,GACT,OAAO,GACP,YAAY,CAAC;AAEjB,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,cAAc,GAAG,MAAM,CAAC;AAC/D,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAE5C,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B;;;OAGG;IACH,IAAI,EACA,gBAAgB,GAChB,SAAS,GACT,YAAY,GACZ,kBAAkB,GAClB,aAAa,GACb,WAAW,GACX,OAAO,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,GAAG,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LDLogger } from '../../api';
|
|
2
|
+
import { FDv2Event } from './proto';
|
|
3
|
+
/**
|
|
4
|
+
* Defines object processing between deserialization and payload emission.
|
|
5
|
+
* Can be used to provide object sanitization logic per kind.
|
|
6
|
+
*/
|
|
7
|
+
export interface ObjProcessors {
|
|
8
|
+
[kind: string]: (object: any) => any;
|
|
9
|
+
}
|
|
10
|
+
export interface Update {
|
|
11
|
+
kind: string;
|
|
12
|
+
key: string;
|
|
13
|
+
version: number;
|
|
14
|
+
object?: any;
|
|
15
|
+
deleted?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export type PayloadType = 'full' | 'partial' | 'none';
|
|
18
|
+
/**
|
|
19
|
+
* A collection of updates from the FDv2 services.
|
|
20
|
+
*
|
|
21
|
+
* - `full`: the updates represent the complete state and replace everything.
|
|
22
|
+
* - `partial`: the updates are incremental changes to apply.
|
|
23
|
+
* - `none`: no changes are needed; the SDK is up-to-date.
|
|
24
|
+
*/
|
|
25
|
+
export interface Payload {
|
|
26
|
+
id: string;
|
|
27
|
+
version: number;
|
|
28
|
+
state?: string;
|
|
29
|
+
type: PayloadType;
|
|
30
|
+
updates: Update[];
|
|
31
|
+
}
|
|
32
|
+
export type PayloadListener = (payload: Payload) => void;
|
|
33
|
+
/**
|
|
34
|
+
* - `inactive`: No server intent has been expressed (initial state).
|
|
35
|
+
* - `changes`: Currently receiving incremental changes.
|
|
36
|
+
* - `full`: Currently receiving a full transfer.
|
|
37
|
+
*/
|
|
38
|
+
export type ProtocolState = 'inactive' | 'changes' | 'full';
|
|
39
|
+
export type ProtocolErrorKind = 'UNKNOWN_EVENT' | 'MISSING_PAYLOAD' | 'PROTOCOL_ERROR';
|
|
40
|
+
/**
|
|
41
|
+
* - `none`: No special action should be taken.
|
|
42
|
+
* - `payload`: A changeset should be applied.
|
|
43
|
+
* - `error`: An internal protocol error was encountered.
|
|
44
|
+
* - `goodbye`: The server intends to disconnect.
|
|
45
|
+
* - `serverError`: A server-side application error was encountered.
|
|
46
|
+
*/
|
|
47
|
+
export type ProtocolAction = {
|
|
48
|
+
type: 'none';
|
|
49
|
+
} | {
|
|
50
|
+
type: 'payload';
|
|
51
|
+
payload: Payload;
|
|
52
|
+
} | {
|
|
53
|
+
type: 'error';
|
|
54
|
+
kind: ProtocolErrorKind;
|
|
55
|
+
message: string;
|
|
56
|
+
} | {
|
|
57
|
+
type: 'goodbye';
|
|
58
|
+
reason: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'serverError';
|
|
61
|
+
id?: string;
|
|
62
|
+
reason: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Pure FDv2 protocol state machine. Processes a single event at a time and
|
|
66
|
+
* returns an action describing what the caller should do. Contains no I/O
|
|
67
|
+
* or callbacks.
|
|
68
|
+
*/
|
|
69
|
+
export interface ProtocolHandler {
|
|
70
|
+
readonly state: ProtocolState;
|
|
71
|
+
processEvent(event: FDv2Event): ProtocolAction;
|
|
72
|
+
/** Resets the handler to inactive. Should be called when a connection is reset. */
|
|
73
|
+
reset(): void;
|
|
74
|
+
}
|
|
75
|
+
export declare function createProtocolHandler(objProcessors: ObjProcessors, logger?: LDLogger): ProtocolHandler;
|
|
76
|
+
//# sourceMappingURL=protocolHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocolHandler.d.ts","sourceRoot":"","sources":["../../../src/internal/fdv2/protocolHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAEL,SAAS,EAKV,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,CAAC,IAAI,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,CAAC;CACtC;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,iBAAiB,GAAG,gBAAgB,CAAC;AAEvF;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAIzD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,cAAc,CAAC;IAC/C,mFAAmF;IACnF,KAAK,IAAI,IAAI,CAAC;CACf;AAED,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,aAAa,EAC5B,MAAM,CAAC,EAAE,QAAQ,GAChB,eAAe,CAyMjB"}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -2954,131 +2954,262 @@ class EventFactoryBase {
|
|
|
2954
2954
|
}
|
|
2955
2955
|
}
|
|
2956
2956
|
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
this._tempBasis = false;
|
|
2993
|
-
break;
|
|
2994
|
-
case 'none':
|
|
2995
|
-
this._tempBasis = false;
|
|
2996
|
-
this._processIntentNone(payload);
|
|
2997
|
-
break;
|
|
2998
|
-
default:
|
|
2999
|
-
// unrecognized intent code, return
|
|
3000
|
-
this._logger?.warn(`Unable to process intent code '${payload?.intentCode}'.`);
|
|
3001
|
-
return;
|
|
3002
|
-
}
|
|
3003
|
-
this._tempId = payload?.id;
|
|
3004
|
-
};
|
|
3005
|
-
this._processPutObject = (data) => {
|
|
3006
|
-
// if the following properties haven't been provided by now, we should ignore the event
|
|
3007
|
-
if (!this._tempId || // server intent hasn't been received yet.
|
|
3008
|
-
!data.kind ||
|
|
3009
|
-
!data.key ||
|
|
3010
|
-
!data.version ||
|
|
3011
|
-
!data.object) {
|
|
3012
|
-
return;
|
|
3013
|
-
}
|
|
3014
|
-
const obj = this._processObj(data.kind, data.object);
|
|
3015
|
-
if (!obj) {
|
|
3016
|
-
this._logger?.warn(`Unable to process object for kind: '${data.kind}'`);
|
|
3017
|
-
// ignore unrecognized kinds
|
|
3018
|
-
return;
|
|
3019
|
-
}
|
|
3020
|
-
this._tempUpdates.push({
|
|
3021
|
-
kind: data.kind,
|
|
3022
|
-
key: data.key,
|
|
3023
|
-
version: data.version,
|
|
3024
|
-
object: obj,
|
|
3025
|
-
// intentionally omit deleted for this put
|
|
2957
|
+
const PAYLOAD_ID = 'FDv1Fallback';
|
|
2958
|
+
function fdv1PayloadAdaptor(processor) {
|
|
2959
|
+
return {
|
|
2960
|
+
_processor: processor,
|
|
2961
|
+
_selector: '',
|
|
2962
|
+
useSelector(selector) {
|
|
2963
|
+
this._selector = selector;
|
|
2964
|
+
return this;
|
|
2965
|
+
},
|
|
2966
|
+
processFullTransfer(data) {
|
|
2967
|
+
const events = [
|
|
2968
|
+
{
|
|
2969
|
+
event: 'server-intent',
|
|
2970
|
+
data: {
|
|
2971
|
+
payloads: [
|
|
2972
|
+
{
|
|
2973
|
+
id: PAYLOAD_ID,
|
|
2974
|
+
target: 1,
|
|
2975
|
+
intentCode: 'xfer-full',
|
|
2976
|
+
reason: 'payload-missing',
|
|
2977
|
+
},
|
|
2978
|
+
],
|
|
2979
|
+
},
|
|
2980
|
+
},
|
|
2981
|
+
];
|
|
2982
|
+
Object.entries(data?.flags || []).forEach(([key, flag]) => {
|
|
2983
|
+
events.push({
|
|
2984
|
+
event: 'put-object',
|
|
2985
|
+
data: {
|
|
2986
|
+
kind: 'flag',
|
|
2987
|
+
key,
|
|
2988
|
+
version: flag.version || 1,
|
|
2989
|
+
object: flag,
|
|
2990
|
+
},
|
|
2991
|
+
});
|
|
3026
2992
|
});
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
// intentionally omit object for this delete
|
|
3038
|
-
deleted: true,
|
|
2993
|
+
Object.entries(data?.segments || []).forEach(([key, segment]) => {
|
|
2994
|
+
events.push({
|
|
2995
|
+
event: 'put-object',
|
|
2996
|
+
data: {
|
|
2997
|
+
kind: 'segment',
|
|
2998
|
+
key,
|
|
2999
|
+
version: segment.version || 1,
|
|
3000
|
+
object: segment,
|
|
3001
|
+
},
|
|
3002
|
+
});
|
|
3039
3003
|
});
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3004
|
+
events.push({
|
|
3005
|
+
event: 'payload-transferred',
|
|
3006
|
+
data: {
|
|
3007
|
+
// IMPORTANT: the selector MUST be empty or "live" data synchronizers
|
|
3008
|
+
// will not work as it would try to resume from a bogus state.
|
|
3009
|
+
state: this._selector,
|
|
3010
|
+
version: 1,
|
|
3011
|
+
id: PAYLOAD_ID,
|
|
3012
|
+
},
|
|
3013
|
+
});
|
|
3014
|
+
this._processor.processEvents(events);
|
|
3015
|
+
},
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
|
|
3019
|
+
const ACTION_NONE = { type: 'none' };
|
|
3020
|
+
function createProtocolHandler(objProcessors, logger) {
|
|
3021
|
+
let protocolState = 'inactive';
|
|
3022
|
+
let tempId;
|
|
3023
|
+
let tempType = 'partial';
|
|
3024
|
+
let tempUpdates = [];
|
|
3025
|
+
function processObj(kind, jsonObj) {
|
|
3026
|
+
return objProcessors[kind]?.(jsonObj);
|
|
3027
|
+
}
|
|
3028
|
+
function resetAll() {
|
|
3029
|
+
protocolState = 'inactive';
|
|
3030
|
+
tempId = undefined;
|
|
3031
|
+
tempType = 'partial';
|
|
3032
|
+
tempUpdates = [];
|
|
3033
|
+
}
|
|
3034
|
+
function resetAfterEmission() {
|
|
3035
|
+
protocolState = 'changes';
|
|
3036
|
+
tempType = 'partial';
|
|
3037
|
+
tempUpdates = [];
|
|
3038
|
+
}
|
|
3039
|
+
function resetAfterError() {
|
|
3040
|
+
tempUpdates = [];
|
|
3041
|
+
}
|
|
3042
|
+
function processIntentNone(intent) {
|
|
3043
|
+
if (!intent.id || !intent.target) {
|
|
3044
|
+
return ACTION_NONE;
|
|
3045
|
+
}
|
|
3046
|
+
return {
|
|
3047
|
+
type: 'payload',
|
|
3048
|
+
payload: {
|
|
3047
3049
|
id: intent.id,
|
|
3048
3050
|
version: intent.target,
|
|
3049
|
-
|
|
3050
|
-
updates: [],
|
|
3051
|
-
|
|
3052
|
-
};
|
|
3053
|
-
this._listeners.forEach((it) => it(payload));
|
|
3054
|
-
this._resetAfterEmission();
|
|
3051
|
+
type: 'none',
|
|
3052
|
+
updates: [],
|
|
3053
|
+
},
|
|
3055
3054
|
};
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3055
|
+
}
|
|
3056
|
+
function processServerIntent(data) {
|
|
3057
|
+
if (!data.payloads?.length) {
|
|
3058
|
+
return {
|
|
3059
|
+
type: 'error',
|
|
3060
|
+
kind: 'MISSING_PAYLOAD',
|
|
3061
|
+
message: 'No payload present in server-intent',
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
// Per spec 3.4.2: SDK uses only the first payload.
|
|
3065
|
+
const payload = data.payloads[0];
|
|
3066
|
+
switch (payload?.intentCode) {
|
|
3067
|
+
case 'xfer-full':
|
|
3068
|
+
protocolState = 'full';
|
|
3069
|
+
tempUpdates = [];
|
|
3070
|
+
tempType = 'full';
|
|
3071
|
+
tempId = payload.id;
|
|
3072
|
+
return ACTION_NONE;
|
|
3073
|
+
case 'xfer-changes':
|
|
3074
|
+
protocolState = 'changes';
|
|
3075
|
+
tempUpdates = [];
|
|
3076
|
+
tempType = 'partial';
|
|
3077
|
+
tempId = payload.id;
|
|
3078
|
+
return ACTION_NONE;
|
|
3079
|
+
case 'none':
|
|
3080
|
+
protocolState = 'changes';
|
|
3081
|
+
tempUpdates = [];
|
|
3082
|
+
tempType = 'partial';
|
|
3083
|
+
tempId = payload.id;
|
|
3084
|
+
return processIntentNone(payload);
|
|
3085
|
+
default:
|
|
3086
|
+
logger?.warn(`Unable to process intent code '${payload?.intentCode}'.`);
|
|
3087
|
+
return ACTION_NONE;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
function processPutObject(data) {
|
|
3091
|
+
if (protocolState === 'inactive' ||
|
|
3092
|
+
!tempId ||
|
|
3093
|
+
!data.kind ||
|
|
3094
|
+
!data.key ||
|
|
3095
|
+
!data.version ||
|
|
3096
|
+
!data.object) {
|
|
3097
|
+
return ACTION_NONE;
|
|
3098
|
+
}
|
|
3099
|
+
const obj = processObj(data.kind, data.object);
|
|
3100
|
+
if (!obj) {
|
|
3101
|
+
logger?.warn(`Unable to process object for kind: '${data.kind}'`);
|
|
3102
|
+
return ACTION_NONE;
|
|
3103
|
+
}
|
|
3104
|
+
tempUpdates.push({
|
|
3105
|
+
kind: data.kind,
|
|
3106
|
+
key: data.key,
|
|
3107
|
+
version: data.version,
|
|
3108
|
+
object: obj,
|
|
3109
|
+
});
|
|
3110
|
+
return ACTION_NONE;
|
|
3111
|
+
}
|
|
3112
|
+
function processDeleteObject(data) {
|
|
3113
|
+
if (protocolState === 'inactive' || !tempId || !data.kind || !data.key || !data.version) {
|
|
3114
|
+
return ACTION_NONE;
|
|
3115
|
+
}
|
|
3116
|
+
tempUpdates.push({
|
|
3117
|
+
kind: data.kind,
|
|
3118
|
+
key: data.key,
|
|
3119
|
+
version: data.version,
|
|
3120
|
+
deleted: true,
|
|
3121
|
+
});
|
|
3122
|
+
return ACTION_NONE;
|
|
3123
|
+
}
|
|
3124
|
+
function processPayloadTransferred(data) {
|
|
3125
|
+
if (protocolState === 'inactive') {
|
|
3126
|
+
return {
|
|
3127
|
+
type: 'error',
|
|
3128
|
+
kind: 'PROTOCOL_ERROR',
|
|
3129
|
+
message: 'A payload transferred has been received without an intent having been established.',
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
if (!tempId || data.state === null || data.state === undefined || !data.version) {
|
|
3133
|
+
resetAll();
|
|
3134
|
+
return ACTION_NONE;
|
|
3135
|
+
}
|
|
3136
|
+
const result = {
|
|
3137
|
+
type: 'payload',
|
|
3138
|
+
payload: {
|
|
3139
|
+
id: tempId,
|
|
3066
3140
|
version: data.version,
|
|
3067
3141
|
state: data.state,
|
|
3068
|
-
|
|
3069
|
-
updates:
|
|
3070
|
-
}
|
|
3071
|
-
this._listeners.forEach((it) => it(payload));
|
|
3072
|
-
this._resetAfterEmission();
|
|
3073
|
-
};
|
|
3074
|
-
this._processGoodbye = (data) => {
|
|
3075
|
-
this._logger?.info(`Goodbye was received from the LaunchDarkly connection with reason: ${data.reason}.`);
|
|
3076
|
-
this._resetAll();
|
|
3077
|
-
};
|
|
3078
|
-
this._processError = (data) => {
|
|
3079
|
-
this._logger?.info(`An issue was encountered receiving updates for payload ${this._tempId} with reason: ${data.reason}.`);
|
|
3080
|
-
this._resetAfterError();
|
|
3142
|
+
type: tempType,
|
|
3143
|
+
updates: tempUpdates,
|
|
3144
|
+
},
|
|
3081
3145
|
};
|
|
3146
|
+
resetAfterEmission();
|
|
3147
|
+
return result;
|
|
3148
|
+
}
|
|
3149
|
+
function processGoodbye(data) {
|
|
3150
|
+
logger?.info(`Goodbye was received from the LaunchDarkly connection with reason: ${data.reason}.`);
|
|
3151
|
+
resetAll();
|
|
3152
|
+
return { type: 'goodbye', reason: data.reason };
|
|
3153
|
+
}
|
|
3154
|
+
function processError(data) {
|
|
3155
|
+
logger?.info(`An issue was encountered receiving updates for payload ${tempId} with reason: ${data.reason}.`);
|
|
3156
|
+
resetAfterError();
|
|
3157
|
+
return { type: 'serverError', id: data.payload_id, reason: data.reason };
|
|
3158
|
+
}
|
|
3159
|
+
return {
|
|
3160
|
+
get state() {
|
|
3161
|
+
return protocolState;
|
|
3162
|
+
},
|
|
3163
|
+
processEvent(event) {
|
|
3164
|
+
switch (event.event) {
|
|
3165
|
+
case 'server-intent':
|
|
3166
|
+
return processServerIntent(event.data);
|
|
3167
|
+
case 'put-object':
|
|
3168
|
+
return processPutObject(event.data);
|
|
3169
|
+
case 'delete-object':
|
|
3170
|
+
return processDeleteObject(event.data);
|
|
3171
|
+
case 'payload-transferred':
|
|
3172
|
+
return processPayloadTransferred(event.data);
|
|
3173
|
+
case 'goodbye':
|
|
3174
|
+
return processGoodbye(event.data);
|
|
3175
|
+
case 'error':
|
|
3176
|
+
return processError(event.data);
|
|
3177
|
+
case 'heart-beat':
|
|
3178
|
+
return ACTION_NONE;
|
|
3179
|
+
default:
|
|
3180
|
+
return {
|
|
3181
|
+
type: 'error',
|
|
3182
|
+
kind: 'UNKNOWN_EVENT',
|
|
3183
|
+
message: `Received an unknown event of type '${event.event}'`,
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
},
|
|
3187
|
+
reset() {
|
|
3188
|
+
resetAll();
|
|
3189
|
+
},
|
|
3190
|
+
};
|
|
3191
|
+
}
|
|
3192
|
+
|
|
3193
|
+
/**
|
|
3194
|
+
* Errors that indicate a problem with the data or protocol flow and should
|
|
3195
|
+
* be reported to the error handler. Informational errors like UNKNOWN_EVENT
|
|
3196
|
+
* are intentionally excluded to preserve forward compatibility — older SDKs
|
|
3197
|
+
* should silently ignore new event types added to the protocol.
|
|
3198
|
+
*/
|
|
3199
|
+
function isActionableError(kind) {
|
|
3200
|
+
return kind === 'MISSING_PAYLOAD' || kind === 'PROTOCOL_ERROR';
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Parses payloads from a stream of FDv2 events by delegating to a protocol handler.
|
|
3204
|
+
* Sends completed payloads to registered listeners.
|
|
3205
|
+
* Invalid event sequences may be dropped silently, but the processor will continue to operate.
|
|
3206
|
+
*/
|
|
3207
|
+
class PayloadProcessor {
|
|
3208
|
+
constructor(objProcessors, _errorHandler, _logger) {
|
|
3209
|
+
this._errorHandler = _errorHandler;
|
|
3210
|
+
this._logger = _logger;
|
|
3211
|
+
this._listeners = [];
|
|
3212
|
+
this._handler = createProtocolHandler(objProcessors, _logger);
|
|
3082
3213
|
}
|
|
3083
3214
|
addPayloadListener(listener) {
|
|
3084
3215
|
this._listeners.push(listener);
|
|
@@ -3089,56 +3220,24 @@ class PayloadProcessor {
|
|
|
3089
3220
|
this._listeners.splice(index, 1);
|
|
3090
3221
|
}
|
|
3091
3222
|
}
|
|
3092
|
-
/**
|
|
3093
|
-
* Gives the {@link PayloadProcessor} a series of events that it will statefully, incrementally process.
|
|
3094
|
-
* This may lead to listeners being invoked as necessary.
|
|
3095
|
-
* @param events to be processed (can be a single element)
|
|
3096
|
-
*/
|
|
3097
3223
|
processEvents(events) {
|
|
3098
3224
|
events.forEach((event) => {
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
}
|
|
3104
|
-
case 'put-object': {
|
|
3105
|
-
this._processPutObject(event.data);
|
|
3106
|
-
break;
|
|
3107
|
-
}
|
|
3108
|
-
case 'delete-object': {
|
|
3109
|
-
this._processDeleteObject(event.data);
|
|
3110
|
-
break;
|
|
3111
|
-
}
|
|
3112
|
-
case 'payload-transferred': {
|
|
3113
|
-
this._processPayloadTransferred(event.data);
|
|
3225
|
+
const action = this._handler.processEvent(event);
|
|
3226
|
+
switch (action.type) {
|
|
3227
|
+
case 'payload':
|
|
3228
|
+
this._listeners.forEach((it) => it(action.payload));
|
|
3114
3229
|
break;
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3230
|
+
case 'error':
|
|
3231
|
+
if (isActionableError(action.kind)) {
|
|
3232
|
+
this._errorHandler?.(DataSourceErrorKind.InvalidData, action.message);
|
|
3233
|
+
}
|
|
3234
|
+
else {
|
|
3235
|
+
this._logger?.warn(action.message);
|
|
3236
|
+
}
|
|
3122
3237
|
break;
|
|
3123
|
-
}
|
|
3124
3238
|
}
|
|
3125
3239
|
});
|
|
3126
3240
|
}
|
|
3127
|
-
_processObj(kind, jsonObj) {
|
|
3128
|
-
return this._objProcessors[kind]?.(jsonObj);
|
|
3129
|
-
}
|
|
3130
|
-
_resetAfterEmission() {
|
|
3131
|
-
this._tempBasis = false;
|
|
3132
|
-
this._tempUpdates = [];
|
|
3133
|
-
}
|
|
3134
|
-
_resetAfterError() {
|
|
3135
|
-
this._tempUpdates = [];
|
|
3136
|
-
}
|
|
3137
|
-
_resetAll() {
|
|
3138
|
-
this._tempId = undefined;
|
|
3139
|
-
this._tempBasis = false;
|
|
3140
|
-
this._tempUpdates = [];
|
|
3141
|
-
}
|
|
3142
3241
|
}
|
|
3143
3242
|
|
|
3144
3243
|
/**
|
|
@@ -3259,6 +3358,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3259
3358
|
ErrorKinds: ErrorKinds$1,
|
|
3260
3359
|
EventFactoryBase: EventFactoryBase,
|
|
3261
3360
|
EventProcessor: EventProcessor,
|
|
3361
|
+
FDv1PayloadAdaptor: fdv1PayloadAdaptor,
|
|
3262
3362
|
InputCustomEvent: InputCustomEvent,
|
|
3263
3363
|
InputEvalEvent: InputEvalEvent,
|
|
3264
3364
|
InputIdentifyEvent: InputIdentifyEvent,
|
|
@@ -3266,6 +3366,7 @@ var index = /*#__PURE__*/Object.freeze({
|
|
|
3266
3366
|
PayloadProcessor: PayloadProcessor,
|
|
3267
3367
|
PayloadStreamReader: PayloadStreamReader,
|
|
3268
3368
|
canonicalize: canonicalize,
|
|
3369
|
+
createProtocolHandler: createProtocolHandler,
|
|
3269
3370
|
initMetadataFromHeaders: initMetadataFromHeaders,
|
|
3270
3371
|
isLegacyUser: isLegacyUser,
|
|
3271
3372
|
isMultiKind: isMultiKind,
|