@frankieone/one-sdk 0.6.5-rc → 0.6.6-rc.7
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 +1 -483
- package/dist/commit.txt +2 -2
- package/dist/esm/modules/biometrics/wrappers/OcrLabs/index.js +53 -39
- package/dist/esm/modules/biometrics/wrappers/OcrLabs/index.js.map +1 -1
- package/dist/esm/modules/common/shared/models/TelemetryEvent.d.ts +3 -1
- package/dist/esm/modules/common/shared/models/api.d.ts +5 -3
- package/dist/esm/modules/meta.json +1 -1
- package/dist/esm/modules/sdk/initialisation/configurationParser.d.ts +1 -0
- package/dist/esm/modules/sdk/initialisation/configurationParser.js +4 -1
- package/dist/esm/modules/sdk/initialisation/configurationParser.js.map +1 -1
- package/dist/esm/modules/sdk/initialisation/sharedDependencies.js +31 -9
- package/dist/esm/modules/sdk/initialisation/sharedDependencies.js.map +1 -1
- package/dist/esm/modules/session/SessionContext.js +0 -2
- package/dist/esm/modules/session/SessionContext.js.map +1 -1
- package/dist/release.txt +1 -1
- package/dist/umd/biometrics-ocrlabs.js +1 -1
- package/dist/umd/biometrics-ocrlabs.js.map +1 -1
- package/dist/umd/oneSdk.umd.js +1 -1
- package/dist/umd/oneSdk.umd.js.LICENSE.txt +0 -9
- package/dist/umd/oneSdk.umd.js.map +1 -1
- package/dist/version.txt +2 -2
- package/package.json +147 -146
- package/dist/umd/commit.txt +0 -1
- package/dist/umd/release.txt +0 -1
- package/dist/umd/version.txt +0 -1
package/README.md
CHANGED
|
@@ -1,483 +1 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
This is the Typescript codebase for OneSDK
|
|
4
|
-
|
|
5
|
-
## System requirements
|
|
6
|
-
|
|
7
|
-
This list is incomplete.
|
|
8
|
-
|
|
9
|
-
- NodeJS 17.1, to allow NODE json imports in a few of the build modules (Dode's --experimental-json-modules flag and import asserts)
|
|
10
|
-
- NPM 8.1
|
|
11
|
-
|
|
12
|
-
## Project structure
|
|
13
|
-
|
|
14
|
-
This is not a monorepo. This codebase is split in directories, but has no concept of npm workspaces, or any sort of monorepo utilities to use
|
|
15
|
-
|
|
16
|
-
- `modules`, where the codebase for the OneSDK is decoupled in "modules".
|
|
17
|
-
- **The entry file for the OneSDK build is the module `modules/index.ts`**
|
|
18
|
-
- `apps`, where subdirectories contain the codebase for applications related to OneSDK. These Apps are mailny used for testing and validating the OneSDK in different environments and build systems. These are the application packages.
|
|
19
|
-
- `playground` A React application used for executing the local OneSDK source code, used for the development of the OneSDK.
|
|
20
|
-
Given this project imports the OneSDK modules directly, it uses the `modules` folder as a [TypeScript reference](https://www.typescriptlang.org/docs/handbook/project-references.html). Read more in "OneSDK Playground".
|
|
21
|
-
- `raw-js` A very basic Javascript project which uses the CDN release of the OneSDK, executed as a `UMD` module and injected in the browser as `window.oneSdk`.
|
|
22
|
-
- `raw-ts` A very basic TypeScript project which uses the NPM release of the OneSDK.
|
|
23
|
-
- `npm-import` An attempt to reproduce a non standard build system using Rollup. This is experimental and aims to reproduce Westpacs build system used with the OneSDK.
|
|
24
|
-
- `tests` Where jest tests go. Try to keep the file structure as similar to the `modules` directory as possible.
|
|
25
|
-
|
|
26
|
-
## Modules
|
|
27
|
-
|
|
28
|
-
OneSDK modules are a way to easily increase the OneSDK functionalities. They are defined as objects exposing an `initialise` method that returns a `context` object.
|
|
29
|
-
Once the module is initialised, the returned context object offers access to the internal state and functionalities of that instance of the module.
|
|
30
|
-
|
|
31
|
-
Module definition:
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
export interface Module<
|
|
35
|
-
Params extends BaseModuleOptions = BaseModuleOptions,
|
|
36
|
-
Context extends ModuleContext = Omit<EventHub, "emit">
|
|
37
|
-
> {
|
|
38
|
-
initialise(p: Params): Context;
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
BaseModuleOptions is the set of options that all modules will receive, besides any specific options defined by the module itself:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
export type BaseModuleOptions<InstanceName extends string = string> = SharedState & { instanceName: InstanceName };
|
|
46
|
-
|
|
47
|
-
export type SharedState = {
|
|
48
|
-
globalEventHub: EventHub<GlobalEvents>;
|
|
49
|
-
frankieClient: FrankieApiClient;
|
|
50
|
-
oneSdkInstance: OneSDKContext;
|
|
51
|
-
recipe: RecipeParsed;
|
|
52
|
-
session: SessionMeta;
|
|
53
|
-
} & Required<Omit<OneSDKRootParameters, "session">>;
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
A Module Context always exposes at minimum the event hub methods "on" and "off":
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
export type ModuleContext<
|
|
60
|
-
Context extends object = Record<string, unknown>,
|
|
61
|
-
Events extends EventsDictionary = Record<string, unknown[]>
|
|
62
|
-
> = Context & Omit<EventHub<Events>, "emit">;
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### Components
|
|
66
|
-
|
|
67
|
-
The OneSDK exposes a method called `component(moduleName, options)`, which will 1) retrieve the module from the dictionary of modules present in `@common/constants`, 2) initialise/instantiate it with the provided options and 3) store the context object and the instance name in a dictionary of instances. The instance name is provided as the parameter `options.instanceName` and defaults to the parameter `moduleName`.
|
|
68
|
-
|
|
69
|
-
> A component is the combination of an instantiated module's context object and the instance name assigned to it. Check the file `@module/sdk/types/components.ts`.
|
|
70
|
-
|
|
71
|
-
### Instances
|
|
72
|
-
|
|
73
|
-
Only one instance is created per each combination of `moduleName` and `options.instanceName`. Multiple calls to the `component` method with the same two values will always return the same instance, which keeps its state alive internally.
|
|
74
|
-
|
|
75
|
-
### Lazy loading
|
|
76
|
-
|
|
77
|
-
Components are supposed to lighten the OneSDK loading time by loading any necessary extra code asynchronously. Although the interface of a component isn't asynchronous, as its context object is provided immediately to the developer, the bulk of its source code is loaded as soon as the module identifies which vendor code it's supposed to load. In most cases it will then load a wrapper for that vendor containing any vendor source code or logic associated with initialising the integration with their service.
|
|
78
|
-
|
|
79
|
-
- A `module` directory is expected to have the following structure
|
|
80
|
-
|
|
81
|
-
- `index.ts` exposing the modules interface.
|
|
82
|
-
This file will export an object called `{itsName}Module` with the method `initialise(BaseModuleOptions & LocalModuleOptions): ModuleContext`. Check the interface `Module` in `sdk/types/components.ts`
|
|
83
|
-
- `constants.ts`
|
|
84
|
-
- `parseConfiguration.ts` where options passed to the module are validated, which could lead to a hard failure (thrown exception).
|
|
85
|
-
|
|
86
|
-
<b style="color:red">TODO</b>: What does a hard failure look like for asynchronous code that isn't triggered by the developer?
|
|
87
|
-
|
|
88
|
-
- `vendors` directory for vendor specific wrappers
|
|
89
|
-
- `types` directory for module specific types
|
|
90
|
-
- `index.ts` barrel exports file, which reexports all types from a single module. It is just a helpful abstraction to the codebase
|
|
91
|
-
- `module.ts` types used by module's interfaces. Here will be declared the input options and the context object
|
|
92
|
-
- `wrapper.ts` types used to abstract different vendor wrappers. Here will be declared the common wrapper options and context object
|
|
93
|
-
- `events.ts` events exposed by this module's event hub
|
|
94
|
-
|
|
95
|
-
_Caching strategies for asynchronous modules may be documented, where we give developers module specific urls, so they can signal to the browsers they might use some modules and the browser will download them in the background as soon as possible, without disturbing any other requests._ <b style="color:red">TODO</b>
|
|
96
|
-
|
|
97
|
-
Description on how to create new modules here. <b style="color:red">TODO</b>
|
|
98
|
-
|
|
99
|
-
Some OneSDK modules will be initialised automatically during the OneSDK initialisation and exposed via methods on the OneSDK instance directly. Besides their invocation, they are no different to modules invoked as "components".
|
|
100
|
-
|
|
101
|
-
The Modules directory can also contain utilities, such as the `FrankieApiClient`, which abstracts integration to BFF and the more general `common` directory, where common types and small utilities are provided.
|
|
102
|
-
|
|
103
|
-
### Frankie Api Client
|
|
104
|
-
|
|
105
|
-
This BFF client is split in modules that cover different use cases. Some of them are
|
|
106
|
-
|
|
107
|
-
1. "Applicant", for CRUD operations on entities of type "individual", which includes list of documents
|
|
108
|
-
2. "Documents", mainly for Id Scan upload.
|
|
109
|
-
3. "OCR", for running OCR in scans
|
|
110
|
-
4. "IDV", for initialising sessions with vendors and running liveness and facial similarity checks
|
|
111
|
-
|
|
112
|
-
Not all OneSDK modules need all those clients and for that reason, they may be invoked asynchronously as well. There are two ways to fetch an API Client module:
|
|
113
|
-
|
|
114
|
-
1. Synchronously (which will bundle the source code with the current source code)
|
|
115
|
-
|
|
116
|
-
```
|
|
117
|
-
import { FrankieApiClient, ApplicantClient } from "@module/frankie-client";
|
|
118
|
-
const bffClient = new FrankieApiClient("/baseurl");
|
|
119
|
-
const applicantsClient = new ApplicantClient(bffClient, { entityId: "some-id" });
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
2. Asynchronously, which will fetch this client only when needed
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
const bffClient = new FrankieApiClient("/baseurl");
|
|
126
|
-
const applicantsClient = await bffClient.getClient("applicants", { entityId: "some-id" });
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
Description on how to create new clients here. <b style="color:red">TODO</b>
|
|
130
|
-
|
|
131
|
-
## Reactivity
|
|
132
|
-
|
|
133
|
-
In javascript, a plain object storing variables is not what you would call "reactive". Reactivity is implemented in different ways by different frameworks, but the most popular way to implement reactivity is using a library called RXJS, which is backed by big names such as Microsoft.
|
|
134
|
-
RXJS (Reactive Extension for Javascript) allows us to build declarative, data driven applications, instead of fully procedural. [Read about it here](https://x-team.com/blog/rxjs-observables/).
|
|
135
|
-
|
|
136
|
-
Due to its popularity, RXJS is native to Angular, easily supported by Vue with an official plugin and easily adopted by React with custom hooks. Since it's built in plain Javascript, any framework can find ways to integrate with it.
|
|
137
|
-
|
|
138
|
-
In OneSDK we will use it to implement internal reactive state, which is used to propagate change as data streams, instead of static values. The entire application will then be aligned with a single source of truth, instead of manual updates that would be required if using events. At the same time, simple use cases require imperative options which, due to the lack of conceptual challenges, are much easier to understand. We aim to offer both alternatives: A reactive and an imperative.
|
|
139
|
-
|
|
140
|
-
The class `ReactiveStore` extends RXJS observables to offer a developer friendly way to read and right from the internal state.
|
|
141
|
-
|
|
142
|
-
`ReactiveStore::constructor(StateDictionary)`
|
|
143
|
-
|
|
144
|
-
```
|
|
145
|
-
const internalState$ = new ReactiveStore({
|
|
146
|
-
dataFieldA: "initialValue",
|
|
147
|
-
dataFieldB: { a: 1, b: 2, c: 3 },
|
|
148
|
-
dataFieldC: new Clonable()
|
|
149
|
-
})
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
To enforce **immutability**, and hence avoiding weird reference bugs that are extremely hard to debug, all data passed to reactive store becomes immutable. Whenever reading a value from it, **you'll always receive a copy of the original value**. For that reason, **all class instances** passed to the data, at any level **need to implement the interface** `Clonable<T> { clone(): T }`. Any other JS native values (dictionaries, arrays and primitives) will be recursively resolved. Check the file `objectUtils.ts` for details.
|
|
153
|
-
|
|
154
|
-
To read from the object `internalState$` defined above we need to destructure its individual fields with the method
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
ReactiveStore::getRootAccessors(Property): Accessors<StateDictionary[Property]>
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
where StateDictionary is the value passed to the constructor function and Property is the string passed to `getRootAccessors`. The returned type is an Accessors object with type inferred automatically.
|
|
161
|
-
|
|
162
|
-
```
|
|
163
|
-
const internalState$ = new ReactiveStore({ dataField: { nestedDataField: "a" }});
|
|
164
|
-
const dataFieldAccessors$ = internalState$.getRootAccessors("dataField");
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
> When updating values, changes are only applied if they differ from the previous value. If the provided new value is considered the same by the `changeDetector` option (the second parameter of the constructor), the change is ignored at the root and none of the derived values are emitted.
|
|
168
|
-
|
|
169
|
-
### Accessors
|
|
170
|
-
|
|
171
|
-
Data Accessors are what we usually call `getters` and `setters`, but here we extend the concept by also including the reactive rxjs `observable`. When calling `getRootAccessors` for a specific field, you'll get the following object:
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
type Accessors<T> = {
|
|
175
|
-
getValue(): T;
|
|
176
|
-
setValue(v: T): void;
|
|
177
|
-
observable: RXJS.Observable<T>;
|
|
178
|
-
// propertyName helps debugging what field the accessors are for.
|
|
179
|
-
// It has no functionality
|
|
180
|
-
propertyName: string;
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
You can as well go deeper and recursively create new accessors from existing accessors with the static method
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
ReactiveStore::mkPropertyAccessors(Accessors<OriginData>, {
|
|
188
|
-
propertyName: Property
|
|
189
|
-
}): Accessors<OriginData[Property]>`
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
```
|
|
193
|
-
const internalState$ = new ReactiveStore({ dataField: { nestedDataField: "a" }});
|
|
194
|
-
const dataField$ = internalState$.getRootAccessors("dataField");
|
|
195
|
-
const nestedDataField$ = ReactiveStore.mkPropertyAccessors(dataField$, {
|
|
196
|
-
propertyName?: "nestedDataField"
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
<b style="color: red">TODO: </b> Allow `getRootAccessors` to accept a dot notation path in the parameter `propertyName`, which returns nested accessors, removing the need to call a different method for root and nested fields.
|
|
201
|
-
|
|
202
|
-
### ReadonlyAccessors
|
|
203
|
-
|
|
204
|
-
Not all data originated from a state dictionary is a simply a value read from a property directly. Some fields are transformed from one or more existing fields. They have a value "computed" from another value. We may create such values, which will be "readonly", by calling the static method
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
ReactiveStore::mkComputedAccessors(Accessors<OriginData>, {
|
|
208
|
-
propertyName?: Property,
|
|
209
|
-
transformer: (originValue: OriginData) => TransformedData
|
|
210
|
-
}): ReadonlyAccessors<TransformedData>`
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
where `ReadonlyAccessors` is a subset of `Accessors`
|
|
214
|
-
|
|
215
|
-
```
|
|
216
|
-
type ReadonlyAccessors<T> = {
|
|
217
|
-
getValue(): T;
|
|
218
|
-
observable: RXJS.Observable<T>;
|
|
219
|
-
// propertyName helps debugging what field the accessors are for.
|
|
220
|
-
// It has no functionality
|
|
221
|
-
propertyName: string;
|
|
222
|
-
}
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
### Accessors of Tuples and Remembering values
|
|
226
|
-
|
|
227
|
-
<b style="color: red">TODO:</b> Document option `rememberNum` and describe `Accessors<Tuple<T>>`
|
|
228
|
-
|
|
229
|
-
### Reactivity code style
|
|
230
|
-
|
|
231
|
-
1. Following RXJS's own style guide, Reactive data, either our **custom Accessors or instances of the ReactiveStore class should contain a trailing dollar sign "$"**, as seen above.
|
|
232
|
-
2. **Computed properties**, which are readonly data extracted and transformed from other reactive data, **should also contain a leading underscore "\_"**.
|
|
233
|
-
|
|
234
|
-
## TypeScript setup
|
|
235
|
-
|
|
236
|
-
Even though this is not a Monorepo, the directories `modules`, and `apps/*` each have their own `tsconfig.json` file, making each of them an independent TypeScript project. Each tsconfig.json file extends a common tsconfig.base.json file in the root of the repository, where most of the typescript configuration lies. This pattern ensures each project uses a configuration compatible to the other.
|
|
237
|
-
|
|
238
|
-
These typescript projects aren't exactly independent and the tsconfig.base.json file actually shares common path aliases
|
|
239
|
-
|
|
240
|
-
- "@module/_": ["./modules/_"],
|
|
241
|
-
- "@static/_": ["./static/_"],
|
|
242
|
-
- "@frankieone/one-sdk": ["./modules/index.ts"],
|
|
243
|
-
- "@types": ["./types/index.ts"],
|
|
244
|
-
|
|
245
|
-
These path aliases had to be hard copied into the common/vite.config.ts file until I find a plugin to do this automatically. <b style="color: red">TODO</b>
|
|
246
|
-
|
|
247
|
-
@frankieone/one-sdk allows the React app to import OneSDK as if it came from npm. What really happens is that typescript maps directly to OneSDK in your local filesystem, allowing for all sorts of development perks, such as change detections for hot reloading and others.
|
|
248
|
-
@module and @static are used by the OneSDK modules to refer to their own resources.
|
|
249
|
-
@types are local types and type utilities available to the entire repository
|
|
250
|
-
|
|
251
|
-
This configuration doesn't need to be shared as they affect different TS projects separately, so they could be split across the specific tsconfig.json files. Since this works as well and makes the configuration a bit easier to understand, I decided to leave it as is for now. <b style="color: red">TODO</b>
|
|
252
|
-
|
|
253
|
-
## Dependencies
|
|
254
|
-
|
|
255
|
-
All dependencies of `modules` are described in the root package.json and contained in the root `node_modules`.
|
|
256
|
-
To avoid conflicts during development, all **dev** dependencies for `apps` were _hoisted_ to the root package.json. If and when there's a dependency conflict, always take note and make sure to resolve it by changing the `apps` dependencies.
|
|
257
|
-
`modules` is the source of truth for our dependencies. Non dev dependencies should be declared in their own package.json.
|
|
258
|
-
|
|
259
|
-
### Shared dependencies
|
|
260
|
-
|
|
261
|
-
The shared repository has deep and convoluted dependencies which makes it nearly impossible and undesirable to include the entire shared package into the dependencies of OneSDK. For that reason, the necessary files were hard copied and pasted into different folders in `modules/common/shared/`. These should be eventually replaced with new and cleaner implementations.
|
|
262
|
-
|
|
263
|
-
## Bundling
|
|
264
|
-
|
|
265
|
-
The **OneSDK modules and assets** are bundled by webpack as UMD scripts, ready to be loaded and run directly on browsers via cdn servers. On the same `build` script, a second compilation is run by `tsc`, where the typescript compiler generates a folder `dist/esm` where ES Modules are generated along with their respective file declarations. This directory is meant to be deployed to the NPM repository for application development, which still needs to be run on a browser or browser-like environment (Native Webviews).
|
|
266
|
-
|
|
267
|
-
The Playground `cdn` app is prepared for deployment using Vite and generates index.html, index.js and index.css files.
|
|
268
|
-
The `react` app also uses Vite, but so far is only used to validate importing the OneSDK as ES Module. No artifacts are generated for it.
|
|
269
|
-
|
|
270
|
-
Both `cdn` and `react` mostly share common configurations, defined in `apps/common/vite.config.js`.
|
|
271
|
-
|
|
272
|
-
Each of these bundlers need to be aware of Typescript paths described in **Typescript setup**, otherwise they are not going to work once built.
|
|
273
|
-
|
|
274
|
-
- For the main OneSDK bundler, we use webpack.config.js with a resolution plugin `TsConfigPathsPlugin`
|
|
275
|
-
- For react and cdn apps, we use a common bundler configuration in `apps/common/vite.config.js`, with paths manually defined (which could be also simplified with a plugin, but this is good enough for now) <b style="color: red">TODO</b>
|
|
276
|
-
|
|
277
|
-
## Getting started
|
|
278
|
-
|
|
279
|
-
Before attempting to install anything, you'll need access to some of our common @frankieone npm packages. For that, please request someone in the frontend team to generate a [npm access token](https://docs.npmjs.com/about-access-tokens) for you. You should set it up, so it will always load into your terminals. For [macOS](https://youngstone89.medium.com/setting-up-environment-variables-in-mac-os-28e5941c771c) and [windows](https://geekflare.com/system-environment-variables-in-windows/). If you use linux, you already know what to do.
|
|
280
|
-
|
|
281
|
-
The following script will make sure you get a fresh install, by removing any existing `dist` and `node_modules` folders and running `npm install` again in all folders.
|
|
282
|
-
|
|
283
|
-
```
|
|
284
|
-
npm run fresh
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
## Environment variables
|
|
288
|
-
|
|
289
|
-
Ideally, don't include environment variables in the OneSDK modules at all. Environment variables will be bundled with the final UMD build, but will need to be specified for usage with ES Modules, which will be imported and bundled in a foreign environment, with its own variables. In case environment variables are required, make sure to document them in the OneSDK public documentation, so developers can include them into their own environment.
|
|
290
|
-
|
|
291
|
-
Add all env variables to the root .env files. Only variables with the `PUBLIC_` prefix will be available to `apps`. Don't forget that environment variables will be discoverable in the UMD files once the OneSDK modules are bundled, so never use secrets anywhere in the `modules` source code, **except during local development, as it might be necessary for testing**.
|
|
292
|
-
|
|
293
|
-
**TODO: create a `SECRET_` prefix that is only used during development**.
|
|
294
|
-
|
|
295
|
-
If you commit them by mistake, make sure to tell someone, so we can deactivate any access given to the corresponding secret keys. Make sure to add any new environment variable to the type declaration `env.d.ts`, for intelisense and typescript support. Also use that file as a guide of which variables are expected, similar to the usual `.env.example`.
|
|
296
|
-
|
|
297
|
-
### Accessing Environment variables
|
|
298
|
-
|
|
299
|
-
~~To keep our codebase according to modern practices and latest standards, this package's internal modules are defined as EcmaScript modules by default (in `package.json > type`). Throughout the entire codebase we will access environment variables through the `import.meta.env`, described [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta) and [here](https://vitejs.dev/guide/env-and-mode.html).~~
|
|
300
|
-
|
|
301
|
-
There's still not a common modern standard of how to access environment variables when using ES Modules, so for a broader compatibility we will keep using the usual `process.env` global variable.
|
|
302
|
-
|
|
303
|
-
Since our Javascript codebase is of type `module` (package.json > type), other advanced `import.meta` variables, such as `import.meta.url` are available at build time.
|
|
304
|
-
|
|
305
|
-
## OneSDK Playground
|
|
306
|
-
|
|
307
|
-
The Playground is where we can work on and validate features being developed. In the future we can also make it available to the Frankie team to test the OneSDK themselves. It's implemented as a React App and imports the OneSDK modules directly from the source code, using the same entry point customers would from the NPM Registry. For that to work, the OneSDK `modules` folder contains two tsconfig files, the main `tsconfig.json`, which builds the OneSDK bundle for NPM and `tsconfig.composite.json`, which allows other typescript projects present in the same repo to import it as if they belong to the same project. This is done by adding the `composite: true` option to the latter tsconfig file and by including `"reference":[{ path: "../relative-to/modules/tsconfig.composite.json" }]` in the project that needs access to OneSDK modules locally.
|
|
308
|
-
|
|
309
|
-
To run the playground you should run the following script
|
|
310
|
-
|
|
311
|
-
```
|
|
312
|
-
npm run dev
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
## Content Security Policy
|
|
316
|
-
|
|
317
|
-
Use the CDN playground during development to find out if a new integration requires special [content security policies](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).
|
|
318
|
-
Any request that fails due to missing policy should be added to the list of policies in `apps/cdn/index.html` and then documented in the official OneSDK documentation.
|
|
319
|
-
|
|
320
|
-
## Build and Release process
|
|
321
|
-
|
|
322
|
-
We have two types of deployment: `development` and `production`. For both type of deployments, the deployment pipeline will do the following:
|
|
323
|
-
|
|
324
|
-
1. run tests and type check the code base
|
|
325
|
-
2. build the sdk as both UMD and Typed ESModules into the `dist/umd` and `dist/esm` directories, respectively
|
|
326
|
-
3. build the Playground into `apps/playground/dist` directory
|
|
327
|
-
4. deploy the UMD build to the "assets" S3 bucket, into the namespace defined by either the branch or tag name
|
|
328
|
-
5. deploy the ESM build to npm, with the version defined in package.json
|
|
329
|
-
6. deploy the Playground to the "playground" S3 bucket
|
|
330
|
-
|
|
331
|
-
The UMD build is done by Webpack, whilst the ESM build is done solely by the Typescript compiler. For that reason, ESM has no extra pre-processing and all `.json` imports need to be converted to `.ts` files exporting the JSON content as Typescript values.
|
|
332
|
-
|
|
333
|
-
### Development releases
|
|
334
|
-
|
|
335
|
-
Until we're mature enough to do Continuous Integration and trunk-based flows, Gitflow is the chosen git branching strategy. `development` release types will be triggered from the following development branches:
|
|
336
|
-
|
|
337
|
-
1. `develop`, where tested and validated code will wait for the release into production (see below)
|
|
338
|
-
2. `feature/*`, new feature work that can be tested independently. You may use its suffix instead of the version name in the CDN url to test features live
|
|
339
|
-
|
|
340
|
-
The S3 buckets for `development` are suffixed by `.dev`, so `assets.dev.frankiefinancial.io` and `play.dev.frankiefinancial.io`.
|
|
341
|
-
|
|
342
|
-
While in the `develop` branch, npm releases might be manually triggered in the pipeline. This will cause an `rc` prelease version to be created and pushed to the npm registry. Keep in mind that two subsequent rc releases without a version upgrade will fail, since versions can't be repeated. Whenever intending to release to npm, either in production or RC, always up the version before merging into `develop`.
|
|
343
|
-
|
|
344
|
-
### Production releases
|
|
345
|
-
|
|
346
|
-
**WIP** This is not the final release process
|
|
347
|
-
|
|
348
|
-
Once the `develop` branch is ready to be released, it will have its version tagged appropriately with the following command
|
|
349
|
-
|
|
350
|
-
```
|
|
351
|
-
npm version \
|
|
352
|
-
major | minor | patch | premajor | preminor | prepatch \
|
|
353
|
-
-m "Short release description here"
|
|
354
|
-
|
|
355
|
-
git push origin develop && git push --tags
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
And merged into `main`, triggering the pipeline to deploy the build into S3 and npm.
|
|
359
|
-
|
|
360
|
-
## Debugging
|
|
361
|
-
|
|
362
|
-
This repository is bundled with the following vscode launch configurations:
|
|
363
|
-
|
|
364
|
-
### Debug React app
|
|
365
|
-
|
|
366
|
-
Which executes react app on the browser, stopping the execution at any defined breakpoints
|
|
367
|
-
|
|
368
|
-
### Debug Jest tests
|
|
369
|
-
|
|
370
|
-
Runs jest in debug mode
|
|
371
|
-
|
|
372
|
-
### Debug react app
|
|
373
|
-
|
|
374
|
-
First run `npm run dev` and then run this debug script. You'll be able to position breakpoints anywhere in the codebase.
|
|
375
|
-
|
|
376
|
-
## All scripts commented
|
|
377
|
-
|
|
378
|
-
| Script | Description |
|
|
379
|
-
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
380
|
-
| fresh | Fresh installs all dependencies and generates the modules/meta.json file. |
|
|
381
|
-
| test | Run all tests. |
|
|
382
|
-
| refactor | Watches for both typescript errors and unit tests failures in parallel. Useful for refactoring code. |
|
|
383
|
-
| build:dev | Builds OneSDK in development mode (without minifying it, which allows us to visually debug the build). |
|
|
384
|
-
| dev | Runs React app for developing the OneSDK as a module import (with typescript types available and all that). |
|
|
385
|
-
| dev --dist | Same script as above, but with the --dist option. Runs ESM Javascript from `dist` folder, instead of source code. |
|
|
386
|
-
|
|
387
|
-
## Scripts you shouldn't call directly, but are used by other scripts in the background
|
|
388
|
-
|
|
389
|
-
| Script | Description |
|
|
390
|
-
| --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
391
|
-
| lint:staged | Runs linting before committing. [More info here.](https://github.com/okonet/lint-staged) |
|
|
392
|
-
| start:react-app | Runs React app in dev mode. Aliased by script "dev" |
|
|
393
|
-
| start:cdn-app | Runs the CDN app in dev mode. Used by script "dev:cdn" |
|
|
394
|
-
| build | Builds OneSDK for production, also generating type declarations. This is used by the pipeline. |
|
|
395
|
-
| watch | Watches for changes in the OneSDK source code and rebuilds it when one occurs. Used by script "dev:cdn" |
|
|
396
|
-
| watch-tsc | Watches for changes in the OneSDK source code and type checks it. Used by script "refactor" |
|
|
397
|
-
| tsc-check | Runs type check in the entire code base once. Used by the pipeline |
|
|
398
|
-
| eslint-check | Runs eslint checks in the entire code base once. Used by the pipeline |
|
|
399
|
-
|
|
400
|
-
## Telemetry
|
|
401
|
-
|
|
402
|
-
Telemetry is the extraction and storage of analytical information regarding the health and usage the OneSDK. It works by sending a sequence of Event objects assembled by the client side and submitted to BFF `POST /events` endpoint, which will then store it as an individual record in the `events` SQL database table. In OneSDK, telemetry events are created by emitting the global event `telemetry` from anywhere in the codebase.
|
|
403
|
-
|
|
404
|
-
### Definition of the telemetry event
|
|
405
|
-
|
|
406
|
-
Each telemetry event will always contain common details that refer to the overall stats of the environment the OneSDK is beeing run on. Additional data may be provided in the fields:
|
|
407
|
-
|
|
408
|
-
1. `data`, where details are simply attached to the `data` field of the event record
|
|
409
|
-
2. `error`,
|
|
410
|
-
1. if error is an instance of the native JS class `Error`, it will extract `message` and `stack` and assign them to an `error` object inside the event record's `data` field
|
|
411
|
-
2. if error is an instance of the JS class `OneSDKError`, which extends the native class `Error`, then besides extracting the same as point 1, it also extracts extra details from the `payload` field, provided to `OneSDKError` constructor
|
|
412
|
-
1. `OneSDKError(message: string, payload: unknown)`, where `payload` may be anything
|
|
413
|
-
3. if error is anything else, we'll try to break it down into a serialisable object. This is only to prevent missing important information and shouldn't be used explicitly.
|
|
414
|
-
|
|
415
|
-
If no additional data is required, then you may pass a string representing the eventName directly. See last example below.
|
|
416
|
-
|
|
417
|
-
```typescript
|
|
418
|
-
telemetry: [string | { eventName: string; data?: TelemetryEvent["data"]; error?: Error }];
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
Example of telemetry event for the event `INIT`
|
|
422
|
-
|
|
423
|
-
```typescript
|
|
424
|
-
globalEventHub.emit("telemetry", {
|
|
425
|
-
eventName: "INIT",
|
|
426
|
-
data: { mode: sharedState.mode, recipe: sharedState.recipe, warnings },
|
|
427
|
-
});
|
|
428
|
-
```
|
|
429
|
-
|
|
430
|
-
Example of telemetry event for the event `INIT:ERROR`
|
|
431
|
-
|
|
432
|
-
```typescript
|
|
433
|
-
globalEventHub.emit("telemetry", {
|
|
434
|
-
eventName: "INIT:ERROR",
|
|
435
|
-
data: {
|
|
436
|
-
oneSdkOptions,
|
|
437
|
-
},
|
|
438
|
-
error,
|
|
439
|
-
});
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
Example of telemetry event for the event `OCR:RESULTS`, which doesn't contain any additional data
|
|
443
|
-
|
|
444
|
-
```typescript
|
|
445
|
-
globalEventHub.emit("telemetry", "OCR:RESULTS");
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
## Test Coverage
|
|
449
|
-
|
|
450
|
-
### Ignoring files for test coverage
|
|
451
|
-
|
|
452
|
-
It won't make sense to add coverage to some files. In those circumstances, either simply add the following comment to the top of the file:
|
|
453
|
-
|
|
454
|
-
```typescript
|
|
455
|
-
/* istanbul ignore file */
|
|
456
|
-
```
|
|
457
|
-
|
|
458
|
-
OR add a negation pattern **to the end** of the `collectCoverageFrom` option in `jest.config.js`
|
|
459
|
-
|
|
460
|
-
```javascript
|
|
461
|
-
collectCoverageFrom: ["modules/**/*.ts", "!**/__*.ts"];
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
## "Cannot use import statement outside a module" and other similar nonsense when running Jest tests
|
|
465
|
-
|
|
466
|
-
```
|
|
467
|
-
npx jest --clearCache
|
|
468
|
-
```
|
|
469
|
-
|
|
470
|
-
## TODO
|
|
471
|
-
|
|
472
|
-
- [x] Refactor `modules/sdk` directory to follow the directions for regular components
|
|
473
|
-
- [ ] Decide which files to keep and cleanup legacy files from old setup (such as rollup.config.js)
|
|
474
|
-
- [x] Setup npm deployment
|
|
475
|
-
- [ ] Install commitzen
|
|
476
|
-
- [ ] Publish documentation with version log from commitzen
|
|
477
|
-
- [x] Production deployments
|
|
478
|
-
- [x] CDN
|
|
479
|
-
- [x] NPM
|
|
480
|
-
- [ ] Document caching for assynchronous modules
|
|
481
|
-
- [ ] Find out browser compatibility and declare in package.json and in the documentation
|
|
482
|
-
- [ ] **Auto unregister all events when closing the one sdk**
|
|
483
|
-
- [ ] Avoid duplicated code with https://www.typescriptlang.org/tsconfig#importHelpers
|
|
1
|
+
https://apidocs.frankiefinancial.com/docs/about-onesdk
|
package/dist/commit.txt
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
959fe7d04c6a39b550f351960cfdd5cad7fe4b23
|
|
2
|
+
959fe7d04c6a39b550f351960cfdd5cad7fe4b23
|
|
@@ -54,60 +54,71 @@ const mountUserInterface = (options) => {
|
|
|
54
54
|
const mountElement = originalElement.cloneNode(true);
|
|
55
55
|
originalElement.parentNode.replaceChild(mountElement, originalElement);
|
|
56
56
|
mountElement.id = "onesdk_idv_container";
|
|
57
|
-
mountElement.style
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
Object.assign(mountElement.style, {
|
|
58
|
+
height: "100%",
|
|
59
|
+
minHeight: "50px",
|
|
60
|
+
width: "100%",
|
|
61
|
+
position: "absolute",
|
|
62
|
+
top: "0",
|
|
63
|
+
left: "0",
|
|
64
|
+
overflow: "hidden",
|
|
65
|
+
backgroundColor: "#000",
|
|
66
|
+
});
|
|
65
67
|
const loader = document.createElement("div");
|
|
66
68
|
loader.id = "onesdk_idv_loader";
|
|
67
|
-
loader.style
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
Object.assign(loader.style, {
|
|
70
|
+
position: "absolute",
|
|
71
|
+
top: "20px",
|
|
72
|
+
left: "50%",
|
|
73
|
+
transform: "translate(-50%, 0)",
|
|
74
|
+
color: "#fff",
|
|
75
|
+
fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
|
76
|
+
fontSize: "24px",
|
|
77
|
+
});
|
|
74
78
|
loader.innerText = "Loading...";
|
|
75
79
|
mountElement.appendChild(loader);
|
|
76
80
|
const videoElement = document.createElement("video");
|
|
77
81
|
videoElement.id = "onesdk_idv_video";
|
|
78
82
|
videoElement.autoplay = true;
|
|
79
83
|
videoElement.muted = true;
|
|
80
|
-
videoElement.style.position = "absolute";
|
|
81
|
-
videoElement.style.top = "0";
|
|
82
|
-
videoElement.style.left = "0";
|
|
83
84
|
videoElement.playsInline = true;
|
|
84
|
-
videoElement.style
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
Object.assign(videoElement.style, {
|
|
86
|
+
position: "absolute",
|
|
87
|
+
top: "0",
|
|
88
|
+
left: "0",
|
|
89
|
+
height: "100%",
|
|
90
|
+
width: "100%",
|
|
91
|
+
display: "none",
|
|
92
|
+
zIndex: "1",
|
|
93
|
+
objectFit: "cover",
|
|
94
|
+
transform: "scale(-1,1)", // mirror the video element
|
|
95
|
+
});
|
|
89
96
|
mountElement.appendChild(videoElement);
|
|
90
97
|
const overlayContainer = document.createElement("div");
|
|
91
98
|
overlayContainer.id = "onesdk_idv_overlay";
|
|
92
|
-
overlayContainer.style
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
+
Object.assign(overlayContainer.style, {
|
|
100
|
+
position: "absolute",
|
|
101
|
+
top: "0",
|
|
102
|
+
left: "0",
|
|
103
|
+
height: "100%",
|
|
104
|
+
width: "100%",
|
|
105
|
+
display: "none",
|
|
106
|
+
zIndex: "2",
|
|
107
|
+
});
|
|
99
108
|
overlayContainer.appendChild(createOverlay("white"));
|
|
100
109
|
mountElement.appendChild(overlayContainer);
|
|
101
110
|
const instructions = document.createElement("div");
|
|
102
111
|
instructions.id = "onesdk_idv_instructions";
|
|
103
|
-
instructions.style
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
Object.assign(instructions.style, {
|
|
113
|
+
position: "absolute",
|
|
114
|
+
bottom: "20px",
|
|
115
|
+
left: "50%",
|
|
116
|
+
transform: "translate(-50%, 0)",
|
|
117
|
+
color: "#fff",
|
|
118
|
+
fontFamily: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
|
119
|
+
fontSize: "24px",
|
|
120
|
+
zIndex: "3",
|
|
121
|
+
});
|
|
111
122
|
mountElement.appendChild(instructions);
|
|
112
123
|
loadOcrLabs(options);
|
|
113
124
|
return videoElement;
|
|
@@ -126,7 +137,9 @@ const cleanup = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
126
137
|
eventHub.emit("error", new OneSDKError("Error from vendor SDK 'OcrLabs'", {
|
|
127
138
|
message,
|
|
128
139
|
}));
|
|
140
|
+
return false;
|
|
129
141
|
}
|
|
142
|
+
return true;
|
|
130
143
|
});
|
|
131
144
|
const loadOcrLabs = (options) => __awaiter(void 0, void 0, void 0, function* () {
|
|
132
145
|
const idvClient = new IDVClient(options.frankieClient);
|
|
@@ -216,7 +229,8 @@ const runLivenessChecks = (options, idvClient) => __awaiter(void 0, void 0, void
|
|
|
216
229
|
yield handleDetection("smile", "Please give us a BIG Smile!");
|
|
217
230
|
eventHub.emit("detection_complete");
|
|
218
231
|
try {
|
|
219
|
-
yield cleanup(options)
|
|
232
|
+
if (!(yield cleanup(options)))
|
|
233
|
+
return; // escape initProcess if cleanup failed
|
|
220
234
|
const { checkStatus } = yield idvClient.initProcess({ isSynchronous: !!isSynchronous });
|
|
221
235
|
switch (checkStatus) {
|
|
222
236
|
case Status.PROVIDER_OFFLINE: {
|