@neuralinnovations/dataisland-sdk 0.0.1-dev1
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/.browserslistrc +5 -0
- package/.editorconfig +22 -0
- package/.eslintrc.json +44 -0
- package/.github/workflows/publish-npm.yml +28 -0
- package/.prettierignore +1 -0
- package/.prettierrc +11 -0
- package/.yarnrc +2 -0
- package/README.md +7 -0
- package/babel.config.js +6 -0
- package/jest.config.ts +199 -0
- package/package.json +60 -0
- package/src/appBuilder.ts +39 -0
- package/src/appSdk.ts +40 -0
- package/src/credentials.ts +63 -0
- package/src/disposable.ts +151 -0
- package/src/events.ts +71 -0
- package/src/index.ts +69 -0
- package/src/internal/app.impl.ts +119 -0
- package/src/internal/appBuilder.impl.ts +59 -0
- package/src/internal/context.ts +13 -0
- package/src/internal/createApp.impl.ts +12 -0
- package/src/internal/registry.ts +83 -0
- package/src/middleware.ts +7 -0
- package/src/services/credentialService.ts +24 -0
- package/src/services/middlewareService.ts +33 -0
- package/src/services/rpcService.ts +72 -0
- package/src/services/service.ts +46 -0
- package/src/types.ts +110 -0
- package/test/disposable.test.ts +39 -0
- package/test/events.test.ts +151 -0
- package/test/index.test.ts +83 -0
- package/test/registry.test.ts +44 -0
- package/tsconfig.json +31 -0
package/.browserslistrc
ADDED
package/.editorconfig
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Shared editor configurations that track the way prettier formats source.
|
2
|
+
#
|
3
|
+
# To use this in vscode:
|
4
|
+
#
|
5
|
+
# ext install EditorConfig
|
6
|
+
|
7
|
+
root = true
|
8
|
+
|
9
|
+
# Source files look unixy by default
|
10
|
+
[*]
|
11
|
+
end_of_line = lf
|
12
|
+
insert_final_newline = true
|
13
|
+
|
14
|
+
# Javascript and Typescript look like Google-style
|
15
|
+
[*.{js,json,ts}]
|
16
|
+
charset = utf-8
|
17
|
+
indent_style = space
|
18
|
+
indent_size = 2
|
19
|
+
ij_javascript_use_semicolon_after_statement = false
|
20
|
+
ij_typescript_use_semicolon_after_statement = false
|
21
|
+
# Not currently supported by vscode, but is supported others, e.g. vim.
|
22
|
+
max_line_length = 80
|
package/.eslintrc.json
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
{
|
2
|
+
"env": {
|
3
|
+
"browser": true,
|
4
|
+
"es2021": true
|
5
|
+
},
|
6
|
+
"extends": [
|
7
|
+
"eslint:recommended",
|
8
|
+
"plugin:@typescript-eslint/recommended"
|
9
|
+
],
|
10
|
+
"parser": "@typescript-eslint/parser",
|
11
|
+
"parserOptions": {
|
12
|
+
"ecmaVersion": "latest",
|
13
|
+
"sourceType": "module"
|
14
|
+
},
|
15
|
+
"plugins": [
|
16
|
+
"@typescript-eslint"
|
17
|
+
],
|
18
|
+
"ignorePatterns": [
|
19
|
+
"node_modules/",
|
20
|
+
"dist/"
|
21
|
+
],
|
22
|
+
"rules": {
|
23
|
+
"@typescript-eslint/space-before-function-paren": "off",
|
24
|
+
"@typescript-eslint/semi": "off",
|
25
|
+
"@typescript-eslint/member-delimiter-style": "off",
|
26
|
+
"@typescript-eslint/no-explicit-any": "off",
|
27
|
+
"indent": [
|
28
|
+
"error",
|
29
|
+
2
|
30
|
+
],
|
31
|
+
"linebreak-style": [
|
32
|
+
"error",
|
33
|
+
"unix"
|
34
|
+
],
|
35
|
+
"quotes": [
|
36
|
+
"error",
|
37
|
+
"single"
|
38
|
+
],
|
39
|
+
"semi": [
|
40
|
+
"error",
|
41
|
+
"never"
|
42
|
+
]
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: Publish package to npmjs
|
2
|
+
on:
|
3
|
+
workflow_dispatch:
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
publish:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v4
|
10
|
+
|
11
|
+
- uses: actions/setup-node@v3
|
12
|
+
with:
|
13
|
+
node-version: '20.x'
|
14
|
+
registry-url: 'https://registry.npmjs.org'
|
15
|
+
|
16
|
+
- name: ⏩ CI Install ⏩
|
17
|
+
run: npm ci
|
18
|
+
|
19
|
+
- name: 🧪 Test 🧪
|
20
|
+
run: npm test >> $GITHUB_STEP_SUMMARY | cat
|
21
|
+
|
22
|
+
- name: 🧱 Build 🧱
|
23
|
+
run: npm run build
|
24
|
+
|
25
|
+
- name: 📢 Publish to npm 📢
|
26
|
+
run: npm publish --access public
|
27
|
+
env:
|
28
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.prettierignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
dist
|
package/.prettierrc
ADDED
package/.yarnrc
ADDED
package/README.md
ADDED
package/babel.config.js
ADDED
package/jest.config.ts
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
/**
|
2
|
+
* For a detailed explanation regarding each configuration property, visit:
|
3
|
+
* https://jestjs.io/docs/configuration
|
4
|
+
*/
|
5
|
+
|
6
|
+
import type {Config} from 'jest';
|
7
|
+
|
8
|
+
const config: Config = {
|
9
|
+
// All imported modules in your tests should be mocked automatically
|
10
|
+
// automock: false,
|
11
|
+
|
12
|
+
// Stop running tests after `n` failures
|
13
|
+
// bail: 0,
|
14
|
+
|
15
|
+
// The directory where Jest should store its cached dependency information
|
16
|
+
// cacheDirectory: "/private/var/folders/nn/flsl70711r1cpd4m58p0wzgc0000gn/T/jest_dx",
|
17
|
+
|
18
|
+
// Automatically clear mock calls, instances, contexts and results before every test
|
19
|
+
clearMocks: true,
|
20
|
+
|
21
|
+
// Indicates whether the coverage information should be collected while executing the test
|
22
|
+
collectCoverage: true,
|
23
|
+
|
24
|
+
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
25
|
+
// collectCoverageFrom: undefined,
|
26
|
+
|
27
|
+
// The directory where Jest should output its coverage files
|
28
|
+
coverageDirectory: "coverage",
|
29
|
+
|
30
|
+
// An array of regexp pattern strings used to skip coverage collection
|
31
|
+
// coveragePathIgnorePatterns: [
|
32
|
+
// "/node_modules/"
|
33
|
+
// ],
|
34
|
+
|
35
|
+
// Indicates which provider should be used to instrument code for coverage
|
36
|
+
// coverageProvider: "babel",
|
37
|
+
|
38
|
+
// A list of reporter names that Jest uses when writing coverage reports
|
39
|
+
// coverageReporters: [
|
40
|
+
// "json",
|
41
|
+
// "text",
|
42
|
+
// "lcov",
|
43
|
+
// "clover"
|
44
|
+
// ],
|
45
|
+
|
46
|
+
// An object that configures minimum threshold enforcement for coverage results
|
47
|
+
// coverageThreshold: undefined,
|
48
|
+
|
49
|
+
// A path to a custom dependency extractor
|
50
|
+
// dependencyExtractor: undefined,
|
51
|
+
|
52
|
+
// Make calling deprecated APIs throw helpful error messages
|
53
|
+
// errorOnDeprecated: false,
|
54
|
+
|
55
|
+
// The default configuration for fake timers
|
56
|
+
// fakeTimers: {
|
57
|
+
// "enableGlobally": false
|
58
|
+
// },
|
59
|
+
|
60
|
+
// Force coverage collection from ignored files using an array of glob patterns
|
61
|
+
// forceCoverageMatch: [],
|
62
|
+
|
63
|
+
// A path to a module which exports an async function that is triggered once before all test suites
|
64
|
+
// globalSetup: undefined,
|
65
|
+
|
66
|
+
// A path to a module which exports an async function that is triggered once after all test suites
|
67
|
+
// globalTeardown: undefined,
|
68
|
+
|
69
|
+
// A set of global variables that need to be available in all test environments
|
70
|
+
// globals: {},
|
71
|
+
|
72
|
+
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
73
|
+
// maxWorkers: "50%",
|
74
|
+
|
75
|
+
// An array of directory names to be searched recursively up from the requiring module's location
|
76
|
+
// moduleDirectories: [
|
77
|
+
// "node_modules"
|
78
|
+
// ],
|
79
|
+
|
80
|
+
// An array of file extensions your modules use
|
81
|
+
// moduleFileExtensions: [
|
82
|
+
// "js",
|
83
|
+
// "mjs",
|
84
|
+
// "cjs",
|
85
|
+
// "jsx",
|
86
|
+
// "ts",
|
87
|
+
// "tsx",
|
88
|
+
// "json",
|
89
|
+
// "node"
|
90
|
+
// ],
|
91
|
+
|
92
|
+
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
93
|
+
// moduleNameMapper: {},
|
94
|
+
|
95
|
+
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
96
|
+
// modulePathIgnorePatterns: [],
|
97
|
+
|
98
|
+
// Activates notifications for test results
|
99
|
+
// notify: false,
|
100
|
+
|
101
|
+
// An enum that specifies notification mode. Requires { notify: true }
|
102
|
+
// notifyMode: "failure-change",
|
103
|
+
|
104
|
+
// A preset that is used as a base for Jest's configuration
|
105
|
+
// preset: undefined,
|
106
|
+
|
107
|
+
// Run tests from one or more projects
|
108
|
+
// projects: undefined,
|
109
|
+
|
110
|
+
// Use this configuration option to add custom reporters to Jest
|
111
|
+
// reporters: undefined,
|
112
|
+
|
113
|
+
// Automatically reset mock state before every test
|
114
|
+
// resetMocks: false,
|
115
|
+
|
116
|
+
// Reset the module registry before running each individual test
|
117
|
+
// resetModules: false,
|
118
|
+
|
119
|
+
// A path to a custom resolver
|
120
|
+
// resolver: undefined,
|
121
|
+
|
122
|
+
// Automatically restore mock state and implementation before every test
|
123
|
+
// restoreMocks: false,
|
124
|
+
|
125
|
+
// The root directory that Jest should scan for tests and modules within
|
126
|
+
// rootDir: undefined,
|
127
|
+
|
128
|
+
// A list of paths to directories that Jest should use to search for files in
|
129
|
+
// roots: [
|
130
|
+
// "<rootDir>"
|
131
|
+
// ],
|
132
|
+
|
133
|
+
// Allows you to use a custom runner instead of Jest's default test runner
|
134
|
+
// runner: "jest-runner",
|
135
|
+
|
136
|
+
// The paths to modules that run some code to configure or set up the testing environment before each test
|
137
|
+
// setupFiles: [],
|
138
|
+
|
139
|
+
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
140
|
+
// setupFilesAfterEnv: [],
|
141
|
+
|
142
|
+
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
143
|
+
// slowTestThreshold: 5,
|
144
|
+
|
145
|
+
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
146
|
+
// snapshotSerializers: [],
|
147
|
+
|
148
|
+
// The test environment that will be used for testing
|
149
|
+
// testEnvironment: "jest-environment-node",
|
150
|
+
|
151
|
+
// Options that will be passed to the testEnvironment
|
152
|
+
// testEnvironmentOptions: {},
|
153
|
+
|
154
|
+
// Adds a location field to test results
|
155
|
+
// testLocationInResults: false,
|
156
|
+
|
157
|
+
// The glob patterns Jest uses to detect test files
|
158
|
+
// testMatch: [
|
159
|
+
// "**/__tests__/**/*.[jt]s?(x)",
|
160
|
+
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
161
|
+
// ],
|
162
|
+
|
163
|
+
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
164
|
+
// testPathIgnorePatterns: [
|
165
|
+
// "/node_modules/"
|
166
|
+
// ],
|
167
|
+
|
168
|
+
// The regexp pattern or array of patterns that Jest uses to detect test files
|
169
|
+
// testRegex: [],
|
170
|
+
|
171
|
+
// This option allows the use of a custom results processor
|
172
|
+
// testResultsProcessor: undefined,
|
173
|
+
|
174
|
+
// This option allows use of a custom test runner
|
175
|
+
// testRunner: "jest-circus/runner",
|
176
|
+
|
177
|
+
// A map from regular expressions to paths to transformers
|
178
|
+
// transform: undefined,
|
179
|
+
|
180
|
+
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
181
|
+
// transformIgnorePatterns: [
|
182
|
+
// "/node_modules/",
|
183
|
+
// "\\.pnp\\.[^\\/]+$"
|
184
|
+
// ],
|
185
|
+
|
186
|
+
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
187
|
+
// unmockedModulePathPatterns: undefined,
|
188
|
+
|
189
|
+
// Indicates whether each individual test should be reported during the run
|
190
|
+
// verbose: undefined,
|
191
|
+
|
192
|
+
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
193
|
+
// watchPathIgnorePatterns: [],
|
194
|
+
|
195
|
+
// Whether to use watchman for file crawling
|
196
|
+
// watchman: true,
|
197
|
+
};
|
198
|
+
|
199
|
+
export default config;
|
package/package.json
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
{
|
2
|
+
"name": "@neuralinnovations/dataisland-sdk",
|
3
|
+
"version": "0.0.1-dev1",
|
4
|
+
"description": "SDK for DataIsland project",
|
5
|
+
"licenses": [
|
6
|
+
{
|
7
|
+
"type": "Apache-2.0",
|
8
|
+
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
|
9
|
+
}
|
10
|
+
],
|
11
|
+
"publishConfig": {
|
12
|
+
"access": "public"
|
13
|
+
},
|
14
|
+
"scripts": {
|
15
|
+
"build": "tsc",
|
16
|
+
"test": "jest",
|
17
|
+
"lint": "eslint --ext .ts,.tsx src"
|
18
|
+
},
|
19
|
+
"author": "Neural Innovations LTD",
|
20
|
+
"license": "Apache-2.0",
|
21
|
+
"devDependencies": {
|
22
|
+
"@babel/core": "^7.22.11",
|
23
|
+
"@babel/plugin-transform-modules-commonjs": "7.22.11",
|
24
|
+
"@babel/preset-env": "^7.22.10",
|
25
|
+
"@babel/preset-typescript": "^7.23.3",
|
26
|
+
"@babel/register": "7.22.5",
|
27
|
+
"@jest/globals": "^29.7.0",
|
28
|
+
"@types/jest": "^29.5.11",
|
29
|
+
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
30
|
+
"@typescript-eslint/parser": "^6.19.0",
|
31
|
+
"babel-jest": "^29.7.0",
|
32
|
+
"babel-loader": "8.3.0",
|
33
|
+
"eslint": "^8.56.0",
|
34
|
+
"eslint-config-semistandard": "^17.0.0",
|
35
|
+
"eslint-config-standard": "^17.1.0",
|
36
|
+
"eslint-config-standard-with-typescript": "^43.0.0",
|
37
|
+
"eslint-config-xo": "^0.43.1",
|
38
|
+
"eslint-config-xo-typescript": "^1.0.1",
|
39
|
+
"eslint-plugin-import": "^2.29.1",
|
40
|
+
"eslint-plugin-n": "^15.7.0",
|
41
|
+
"eslint-plugin-promise": "^6.1.1",
|
42
|
+
"eslint-plugin-react": "^7.33.2",
|
43
|
+
"glob": "7.2.3",
|
44
|
+
"jest": "^29.7.0",
|
45
|
+
"prettier": "2.8.7",
|
46
|
+
"ts-jest": "^29.1.1",
|
47
|
+
"ts-loader": "8.4.0",
|
48
|
+
"ts-node": "10.9.1",
|
49
|
+
"tslib": "^2.6.2",
|
50
|
+
"tslint": "6.1.3",
|
51
|
+
"typedoc": "^0.25.7",
|
52
|
+
"typescript": "^5.3.3",
|
53
|
+
"watch": "^0.13.0",
|
54
|
+
"webpack": "5.76.0",
|
55
|
+
"yargs": "17.7.2"
|
56
|
+
},
|
57
|
+
"dependencies": {
|
58
|
+
"jsdom": "^23.2.0"
|
59
|
+
}
|
60
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import type { Middleware } from './middleware'
|
2
|
+
import type { CredentialBase } from './credentials'
|
3
|
+
import type { Service, ServiceContext } from './services/service'
|
4
|
+
import type { Constructor } from './internal/registry'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* DataIsland App builder.
|
8
|
+
*/
|
9
|
+
export abstract class AppBuilder {
|
10
|
+
/**
|
11
|
+
* Add a middleware to the app.
|
12
|
+
*/
|
13
|
+
abstract addMiddleware(middleware: Middleware): AppBuilder
|
14
|
+
|
15
|
+
/**
|
16
|
+
* Host of the app.
|
17
|
+
*/
|
18
|
+
abstract useHost(host: string): AppBuilder
|
19
|
+
|
20
|
+
/**
|
21
|
+
* GDPR compliant
|
22
|
+
*/
|
23
|
+
abstract useAutomaticDataCollectionEnabled(value: boolean): AppBuilder
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Credential of the app.
|
27
|
+
*/
|
28
|
+
abstract useCredential(credential: CredentialBase): AppBuilder
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Register a service to the app.
|
32
|
+
* @param type
|
33
|
+
* @param factory
|
34
|
+
*/
|
35
|
+
abstract registerService<T extends Service>(
|
36
|
+
type: Constructor<T>,
|
37
|
+
factory: (context: ServiceContext) => T
|
38
|
+
): AppBuilder
|
39
|
+
}
|
package/src/appSdk.ts
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
import type { Lifetime } from './disposable'
|
2
|
+
import type { CredentialBase } from './credentials'
|
3
|
+
import type { Constructor } from './internal/registry'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* DataIsland App instance.
|
7
|
+
*/
|
8
|
+
export interface AppSdk {
|
9
|
+
/**
|
10
|
+
* The name of this app.
|
11
|
+
*/
|
12
|
+
get name(): string
|
13
|
+
|
14
|
+
/**
|
15
|
+
* The host of this app.
|
16
|
+
*/
|
17
|
+
get host(): string
|
18
|
+
|
19
|
+
/**
|
20
|
+
* The automaticDataCollectionEnabled of this app.
|
21
|
+
*/
|
22
|
+
get automaticDataCollectionEnabled(): boolean
|
23
|
+
|
24
|
+
/**
|
25
|
+
* The lifetime of this app.
|
26
|
+
*/
|
27
|
+
get lifetime(): Lifetime
|
28
|
+
|
29
|
+
/**
|
30
|
+
* The credential of this app.
|
31
|
+
*/
|
32
|
+
get credential(): CredentialBase | undefined
|
33
|
+
|
34
|
+
set credential(value: CredentialBase)
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Gets the service registered with the given type.
|
38
|
+
*/
|
39
|
+
resolve: <T>(type: Constructor<T>) => T | undefined
|
40
|
+
}
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import { MiddlewareService } from './services/middlewareService'
|
2
|
+
import { type Lifetime } from './disposable'
|
3
|
+
import { type Context } from './internal/context'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* DataIsland App credential.
|
7
|
+
*/
|
8
|
+
export abstract class CredentialBase {
|
9
|
+
abstract onRegister(lifetime: Lifetime, context: Context): void
|
10
|
+
}
|
11
|
+
|
12
|
+
export class DefaultCredential extends CredentialBase {
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
14
|
+
onRegister(lifetime: Lifetime, context: Context): void {
|
15
|
+
// Do nothing.
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
export class BasicCredential extends CredentialBase {
|
20
|
+
readonly email: string
|
21
|
+
readonly password: string
|
22
|
+
|
23
|
+
constructor(email: string, password: string) {
|
24
|
+
super()
|
25
|
+
this.email = email
|
26
|
+
this.password = password
|
27
|
+
}
|
28
|
+
|
29
|
+
onRegister(lifetime: Lifetime, context: Context): void {
|
30
|
+
const service = context.resolve(MiddlewareService)
|
31
|
+
if (service === undefined) {
|
32
|
+
throw new Error('MiddlewareService is not registered.')
|
33
|
+
}
|
34
|
+
lifetime.add(
|
35
|
+
service.useMiddleware(async (req, next) => {
|
36
|
+
req.headers.set('Authorization', `Basic ${this.email}:${this.password}`)
|
37
|
+
await next(req)
|
38
|
+
})
|
39
|
+
)
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export class BearerCredential extends CredentialBase {
|
44
|
+
readonly token: string
|
45
|
+
|
46
|
+
constructor(token: string) {
|
47
|
+
super()
|
48
|
+
this.token = token
|
49
|
+
}
|
50
|
+
|
51
|
+
onRegister(lifetime: Lifetime, context: Context): void {
|
52
|
+
const service = context.resolve(MiddlewareService)
|
53
|
+
if (service === undefined) {
|
54
|
+
throw new Error('MiddlewareService is not registered.')
|
55
|
+
}
|
56
|
+
lifetime.add(
|
57
|
+
service.useMiddleware(async (req, next) => {
|
58
|
+
req.headers.set('Authorization', `Bearer ${this.token}`)
|
59
|
+
await next(req)
|
60
|
+
})
|
61
|
+
)
|
62
|
+
}
|
63
|
+
}
|
@@ -0,0 +1,151 @@
|
|
1
|
+
/**
|
2
|
+
* Represents an object that can be disposed.
|
3
|
+
*/
|
4
|
+
export interface Disposable {
|
5
|
+
dispose: () => void
|
6
|
+
}
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Represents a lifetime.
|
10
|
+
*/
|
11
|
+
export class Lifetime {
|
12
|
+
constructor(private readonly container: DisposableContainer) {}
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Define a new nested disposable to this lifetime.
|
16
|
+
*/
|
17
|
+
public defineNested(): DisposableContainer {
|
18
|
+
return this.container.defineNested()
|
19
|
+
}
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Shows whether this lifetime is disposed.
|
23
|
+
*/
|
24
|
+
public get isDisposed(): boolean {
|
25
|
+
return this.container.isDisposed
|
26
|
+
}
|
27
|
+
|
28
|
+
/**
|
29
|
+
* Adds a disposable to this lifetime.
|
30
|
+
*/
|
31
|
+
public add(disposable: Disposable): this {
|
32
|
+
this.container.add(disposable)
|
33
|
+
return this
|
34
|
+
}
|
35
|
+
|
36
|
+
/**
|
37
|
+
* Adds a callback to this lifetime.
|
38
|
+
*/
|
39
|
+
public addCallback(callback: () => void, target?: unknown): this {
|
40
|
+
this.container.addCallback(callback, target)
|
41
|
+
return this
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
/**
|
46
|
+
* A container for disposables.
|
47
|
+
* Last added, first disposed.
|
48
|
+
* @example
|
49
|
+
* const container = new DisposableContainer();
|
50
|
+
* container.add(someDisposable);
|
51
|
+
* container.addCallback(() => console.log('disposed'));
|
52
|
+
* container.dispose();
|
53
|
+
*/
|
54
|
+
export class DisposableContainer implements Disposable {
|
55
|
+
private _disposables: Disposable[] = []
|
56
|
+
private _isDisposed = false
|
57
|
+
private _lifetime?: Lifetime
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Gets whether this container is disposed.
|
61
|
+
*/
|
62
|
+
public get isDisposed(): boolean {
|
63
|
+
return this._isDisposed
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Define new lifetime.
|
68
|
+
*/
|
69
|
+
public get lifetime(): Lifetime {
|
70
|
+
return this._lifetime ?? (this._lifetime = new Lifetime(this))
|
71
|
+
}
|
72
|
+
|
73
|
+
/**
|
74
|
+
* Adds a disposable to this container.
|
75
|
+
* @param disposable The disposable to add.
|
76
|
+
* @returns The disposable container.
|
77
|
+
*/
|
78
|
+
public add(disposable: Disposable): Disposable {
|
79
|
+
this._throwIfDisposed()
|
80
|
+
this._disposables.push(disposable)
|
81
|
+
return disposable
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Adds a callback to be executed when this container is disposed.
|
86
|
+
* @param callback The callback to execute.
|
87
|
+
* @param target The target to bind the callback to.
|
88
|
+
* @returns The disposable container.
|
89
|
+
*/
|
90
|
+
public addCallback(callback: () => void, target?: unknown): Disposable {
|
91
|
+
this._throwIfDisposed()
|
92
|
+
return this.add({
|
93
|
+
dispose() {
|
94
|
+
callback.call(target)
|
95
|
+
}
|
96
|
+
})
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Defines a nested disposable container.
|
101
|
+
*/
|
102
|
+
defineNested(): DisposableContainer {
|
103
|
+
const nested = new DisposableContainer()
|
104
|
+
this._disposables.push(nested)
|
105
|
+
nested.addCallback(() => {
|
106
|
+
const index = this._disposables.indexOf(nested)
|
107
|
+
if (index > -1) {
|
108
|
+
this._disposables.splice(index, 1)
|
109
|
+
}
|
110
|
+
}, this)
|
111
|
+
return nested
|
112
|
+
}
|
113
|
+
|
114
|
+
/**
|
115
|
+
* Disposes all disposables in this container. Last added, first disposed.
|
116
|
+
*/
|
117
|
+
public dispose(): void {
|
118
|
+
this._throwIfDisposed()
|
119
|
+
this._isDisposed = true
|
120
|
+
this._disposables
|
121
|
+
.slice()
|
122
|
+
.reverse()
|
123
|
+
.forEach(it => {
|
124
|
+
it.dispose()
|
125
|
+
})
|
126
|
+
this._disposables = []
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Throws an error if this container is disposed.
|
131
|
+
*/
|
132
|
+
private _throwIfDisposed(): void {
|
133
|
+
if (this._isDisposed) {
|
134
|
+
throw new Error('Object disposed')
|
135
|
+
}
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* Creates a disposable.
|
141
|
+
* @param action The action to execute when disposed.
|
142
|
+
* @param target The target to bind the action to.
|
143
|
+
* @returns The disposable.
|
144
|
+
*/
|
145
|
+
export function disposable(action: () => void, target: unknown): Disposable {
|
146
|
+
return new DisposableContainer().addCallback(() => {
|
147
|
+
action.call(target)
|
148
|
+
})
|
149
|
+
}
|
150
|
+
|
151
|
+
export const eternalLifetime = new DisposableContainer().lifetime
|