@coherentglobal/spark-execute-sdk 0.3.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.
Files changed (104) hide show
  1. package/.babelrc +12 -0
  2. package/.eslintrc.json +21 -0
  3. package/.github/workflows/build-publish.yml +46 -0
  4. package/.prettierrc +0 -0
  5. package/README.md +92 -0
  6. package/dist/browser.js +6925 -0
  7. package/examples/UsingBearerTokenFNAsync/bearerTokenExample.js +22 -0
  8. package/examples/UsingBearerTokenFNAsync/config.js +88 -0
  9. package/examples/UsingBearerTokenFNAsync/index.html +205 -0
  10. package/examples/UsingBearerTokenFNAsync/modelCInput.json +15 -0
  11. package/examples/UsingBearerTokenFNAsync/models/Model_C.zip +0 -0
  12. package/examples/UsingBearerTokenString/bearerTokenExample.js +22 -0
  13. package/examples/UsingBearerTokenString/config.js +182 -0
  14. package/examples/UsingBearerTokenString/index.html +191 -0
  15. package/examples/UsingBearerTokenString/modelCInput.json +15 -0
  16. package/examples/UsingBearerTokenString/models/Model_C.zip +0 -0
  17. package/examples/UsingSyntheticKeyFNAsync/bearerTokenExample.js +22 -0
  18. package/examples/UsingSyntheticKeyFNAsync/config.js +195 -0
  19. package/examples/UsingSyntheticKeyFNAsync/index.html +205 -0
  20. package/examples/UsingSyntheticKeyFNAsync/modelCInput.json +15 -0
  21. package/examples/UsingSyntheticKeyFNAsync/models/Model_C.zip +0 -0
  22. package/examples/UsingSyntheticKeyString/bearerTokenExample.js +22 -0
  23. package/examples/UsingSyntheticKeyString/config.js +181 -0
  24. package/examples/UsingSyntheticKeyString/index.html +192 -0
  25. package/examples/UsingSyntheticKeyString/modelCInput.json +15 -0
  26. package/examples/UsingSyntheticKeyString/models/Model_C.zip +0 -0
  27. package/examples/asyncFunctionModel/config.js +96 -0
  28. package/examples/asyncFunctionModel/modelCInput.json +15 -0
  29. package/examples/asyncFunctionModel/models/Model_C.zip +0 -0
  30. package/examples/asyncFunctionModel/node.js +125 -0
  31. package/examples/asyncFunctionModel/tool.js +17 -0
  32. package/examples/base64Model/config.js +96 -0
  33. package/examples/base64Model/modelCInput.json +15 -0
  34. package/examples/base64Model/models/Model_C.zip +0 -0
  35. package/examples/base64Model/node.js +125 -0
  36. package/examples/base64Model/tool.js +17 -0
  37. package/examples/base64modelBrowser/config.js +96 -0
  38. package/examples/base64modelBrowser/index.html +194 -0
  39. package/examples/base64modelBrowser/modelCInput.json +15 -0
  40. package/examples/base64modelBrowser/models/Model_C.zip +0 -0
  41. package/examples/base64modelBrowser/models/base64model.txt +1 -0
  42. package/examples/base64modelBrowser/node.js +126 -0
  43. package/examples/base64modelBrowser/tool.js +17 -0
  44. package/examples/binary/config.js +96 -0
  45. package/examples/binary/modelCInput.json +15 -0
  46. package/examples/binary/models/Model_C.zip +0 -0
  47. package/examples/binary/models/binary.txt +1 -0
  48. package/examples/binary/node.js +131 -0
  49. package/examples/binary/tool.js +18 -0
  50. package/examples/functionModel/config.js +96 -0
  51. package/examples/functionModel/modelCInput.json +15 -0
  52. package/examples/functionModel/models/Model_C.zip +0 -0
  53. package/examples/functionModel/node.js +138 -0
  54. package/examples/functionModel/tool.js +17 -0
  55. package/examples/nodejs/node.js +250 -0
  56. package/examples/nodejs/test_models/Archive.zip +0 -0
  57. package/examples/nodejs/test_models/BlackScholes.zip +0 -0
  58. package/examples/nodejs/test_models/Model_A.zip +0 -0
  59. package/examples/nodejs/test_models/Par7.zip +0 -0
  60. package/package.json +79 -0
  61. package/src/browser.js +198 -0
  62. package/src/error.js +87 -0
  63. package/src/helpers/utils.js +79 -0
  64. package/src/logger.js +3 -0
  65. package/src/models.js +26 -0
  66. package/src/node.js +254 -0
  67. package/src/resolver/externalResolver.js +118 -0
  68. package/src/services/entityStore.js +260 -0
  69. package/src/services/resolver.js +134 -0
  70. package/src/services/spark.js +80 -0
  71. package/src/tenant_resolver/externalResolver_coherent.js +118 -0
  72. package/src/tenant_resolver/externalResolver_test.js +118 -0
  73. package/src/validate.js +38 -0
  74. package/test/config.test.js +124 -0
  75. package/test/mock-data/dummy-config.json +102 -0
  76. package/test/mock-data/valid.json +21 -0
  77. package/test/spark-sdk.test.js +28 -0
  78. package/tsconfig.json +105 -0
  79. package/types/browser.d.ts +72 -0
  80. package/types/browser.d.ts.map +1 -0
  81. package/types/error.d.ts +42 -0
  82. package/types/error.d.ts.map +1 -0
  83. package/types/helpers/utils.d.ts +11 -0
  84. package/types/helpers/utils.d.ts.map +1 -0
  85. package/types/logger.d.ts +3 -0
  86. package/types/logger.d.ts.map +1 -0
  87. package/types/models.d.ts +8 -0
  88. package/types/models.d.ts.map +1 -0
  89. package/types/node.d.ts +78 -0
  90. package/types/node.d.ts.map +1 -0
  91. package/types/resolver/externalResolver.d.ts +2 -0
  92. package/types/resolver/externalResolver.d.ts.map +1 -0
  93. package/types/services/entityStore.d.ts +51 -0
  94. package/types/services/entityStore.d.ts.map +1 -0
  95. package/types/services/resolver.d.ts +20 -0
  96. package/types/services/resolver.d.ts.map +1 -0
  97. package/types/services/spark.d.ts +6 -0
  98. package/types/services/spark.d.ts.map +1 -0
  99. package/types/tenant_resolver/externalResolver_coherent.d.ts +2 -0
  100. package/types/tenant_resolver/externalResolver_coherent.d.ts.map +1 -0
  101. package/types/tenant_resolver/externalResolver_test.d.ts +2 -0
  102. package/types/tenant_resolver/externalResolver_test.d.ts.map +1 -0
  103. package/types/validate.d.ts +4 -0
  104. package/types/validate.d.ts.map +1 -0
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@coherentglobal/spark-execute-sdk",
3
+ "version": "0.3.0",
4
+ "description": "",
5
+ "mains": "index.js",
6
+ "browser": "dist/browser.js",
7
+ "scripts": {
8
+ "build:types": "tsc --declaration",
9
+ "bundle:full": "browserify src/browser.js --standalone Spark > dist/browser.js",
10
+ "bundle:min": "browserify src/browser.js --standalone Spark | uglifyjs --compress --mangle --comments false > lib/sparkexecute.min.js",
11
+ "test": "npx mocha --bail --exit --recursive --timeout 100000 --reporter nyan"
12
+ },
13
+ "engines": {
14
+ "node": ">=14.0.0"
15
+ },
16
+ "directories": {
17
+ "test": "test"
18
+ },
19
+ "keywords": [],
20
+ "author": "",
21
+ "license": "ISC",
22
+ "dependencies": {
23
+ "@coherentglobal/wasm-runner": "^0.0.45",
24
+ "@types/node": "^18.7.18",
25
+ "axios": "^0.27.2",
26
+ "chai": "^4.3.6",
27
+ "got": "^11.8.3",
28
+ "http-status-codes": "^2.2.0",
29
+ "joi": "^17.6.0",
30
+ "jstify": "^0.14.0",
31
+ "pino": "^8.5.0",
32
+ "prettier": "^2.7.1"
33
+ },
34
+ "devDependencies": {
35
+ "@babel/cli": "^7.18.10",
36
+ "@babel/core": "^7.19.0",
37
+ "@babel/plugin-transform-runtime": "^7.18.10",
38
+ "@babel/preset-env": "^7.19.0",
39
+ "babel-eslint": "^10.1.0",
40
+ "babelify": "^10.0.0",
41
+ "browserify": "^17.0.0",
42
+ "eslint": "^8.22.0",
43
+ "eslint-config-airbnb": "^19.0.4",
44
+ "eslint-config-node": "^4.1.0",
45
+ "eslint-config-prettier": "^8.5.0",
46
+ "eslint-plugin-node": "^11.1.0",
47
+ "eslint-plugin-prettier": "^4.0.0",
48
+ "jest": "^29.0.2",
49
+ "jsdoc": "^3.6.11",
50
+ "mocha": "^10.0.0",
51
+ "should": "^13.2.3",
52
+ "ts-node": "^10.9.1",
53
+ "typescript": "^4.8.3",
54
+ "uglify-js": "^3.17.0"
55
+ },
56
+ "overrides": {
57
+ "@types/cacheable-request": "8.3.1"
58
+ },
59
+ "browserify": {
60
+ "transform": [
61
+ [
62
+ "babelify",
63
+ {
64
+ "presets": [
65
+ "@babel/preset-env"
66
+ ],
67
+ "plugins": [
68
+ [
69
+ "@babel/plugin-transform-runtime",
70
+ {
71
+ "regenerator": true
72
+ }
73
+ ]
74
+ ]
75
+ }
76
+ ]
77
+ ]
78
+ }
79
+ }
package/src/browser.js ADDED
@@ -0,0 +1,198 @@
1
+ const utils = require("./helpers/utils.js");
2
+ const WasmRunnerErrors = require("./error.js");
3
+ const { WasmRunner } = require("@coherentglobal/wasm-runner");
4
+ const validate = require("./validate.js");
5
+ const processModels = require("./models.js");
6
+
7
+ let registry = [];
8
+ let INVOCATION_CACHE = [];
9
+
10
+ class Spark {
11
+ /**
12
+ * @param {object} config
13
+ */
14
+ constructor(config) {
15
+ /**
16
+ * @private
17
+ */
18
+ this.config = this.validateConfig(config);
19
+
20
+ const {
21
+ sparkEndpoint: { syntheticKey, bearerToken, authType, tenant, url },
22
+ nodeGenModels,
23
+ } = this.config;
24
+ /**
25
+ * @private
26
+ */
27
+ this.tenant = tenant;
28
+ /**
29
+ * @private
30
+ */
31
+ this.authType = authType;
32
+ /**
33
+ * @private
34
+ */
35
+ this.token = syntheticKey || bearerToken;
36
+ /**
37
+ * @private
38
+ */
39
+ this.url = url;
40
+ /**
41
+ * @private
42
+ */
43
+ this.isCompatible = utils.isWasmSupported();
44
+ /**
45
+ * @private
46
+ */
47
+ this.model = this._models(nodeGenModels);
48
+ /**
49
+ * @private
50
+ */
51
+ this.fallbackEnabled = false;
52
+ }
53
+ /**
54
+ *
55
+ * @param {object} nodegen
56
+ * @returns {object}
57
+ */
58
+ _models(nodegen) {
59
+ const models = nodegen.map(processModels);
60
+ return models;
61
+ }
62
+ /**
63
+ *
64
+ * @param {object} config
65
+ * @returns {object}
66
+ */
67
+ validateConfig(config) {
68
+ const { value, error } = validate(config);
69
+ if (!error) {
70
+ return value;
71
+ } else {
72
+ const message = JSON.stringify(error.details);
73
+ throw new Error(`ValidationError: ${message}`);
74
+ }
75
+ }
76
+ /**
77
+ *
78
+ * @param {string} id
79
+ * @returns {boolean}
80
+ */
81
+ _getModel(id) {
82
+ const model = this.model.find((m) => m.versionId === id);
83
+ if (!model) throw new WasmRunnerErrors.MissingModelError(id);
84
+
85
+ return model;
86
+ }
87
+ /**
88
+ *
89
+ * @param {string} versionId
90
+ */
91
+ async load(versionId) {
92
+ const { versionId: versionUuid, binary: modelPath } =
93
+ this._getModel(versionId);
94
+
95
+ console.log("SPARK load: ", modelPath);
96
+ if (!utils.isEmpty(registry)) {
97
+ registry = new WasmRunner();
98
+ }
99
+
100
+ await registry.append({
101
+ id: versionUuid,
102
+ url: modelPath,
103
+ // size,
104
+ });
105
+
106
+ INVOCATION_CACHE = [...INVOCATION_CACHE, { id: versionUuid }];
107
+ }
108
+
109
+ isExist = (model) => {
110
+ return registry.isExist(model);
111
+ };
112
+
113
+ /**
114
+ * Execute model
115
+ *
116
+ * @param {object} input
117
+ * @param {string} version_id
118
+ * @returns {object}
119
+ */
120
+ async execute(input, version_id) {
121
+ if (!input.request_meta.version_id && !input.request_meta.version_uuid) {
122
+ input.request_meta.version_id = version_id;
123
+ }
124
+ const versionID =
125
+ input.request_meta.version_id || input.request_meta.version_uuid;
126
+
127
+ const model = this._getModel(versionID);
128
+
129
+ await this.load(model.versionId);
130
+
131
+ // logger.info({
132
+ // EventType: "ExecuteModel",
133
+ // TextMessage: `Execute uuid: ${model.versionId}`,
134
+ // });
135
+
136
+ if (input && this.isExist(model.versionId)) {
137
+ let modelUrl = "";
138
+
139
+ if (
140
+ !this.isCompatible &&
141
+ !utils.isEmpty(this.url) &&
142
+ this.fallbackEnabled
143
+ ) {
144
+ let token = "";
145
+ if (this.token.constructor.name === "AsyncFunction") {
146
+ token = await this.token();
147
+ } else {
148
+ token = this.token;
149
+ }
150
+ console.log("TOKEN", token);
151
+
152
+ if (utils.isEmpty(this.token)) {
153
+ throw new WasmRunnerErrors.UnauthorizedError();
154
+ }
155
+ const header = utils.getAuthHeaders(token, this.authType, this.tenant);
156
+
157
+ const options = {
158
+ method: "POST",
159
+ body: JSON.stringify({
160
+ request_data: {},
161
+ request_meta: input.request_meta,
162
+ }),
163
+ headers: { ...{ "content-type": "application/json" }, ...header },
164
+ };
165
+
166
+ // console.log("OPTIONS", options);
167
+ modelUrl = new URL(
168
+ `/${this.tenant}/api/v3/version/${input.request_meta.version_id}`,
169
+ this.url
170
+ );
171
+ try {
172
+ console.log("URL", modelUrl);
173
+ const response = await fetch(modelUrl, options);
174
+
175
+ return await response.json();
176
+ } catch (err) {
177
+ throw new WasmRunnerErrors.UnauthorizedError();
178
+ }
179
+ } else if (this.isCompatible) {
180
+ const modelIndex = INVOCATION_CACHE.findIndex(
181
+ (item) => item.id === model.versionId
182
+ );
183
+
184
+ INVOCATION_CACHE.lastUsed = Date.now();
185
+
186
+ const result = await registry.execute(input, model.versionId, true);
187
+
188
+ return result;
189
+ } else {
190
+ throw new WasmRunnerErrors.NotSupportedError(
191
+ "WebAssembly is not supported"
192
+ );
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ module.exports = Spark;
package/src/error.js ADDED
@@ -0,0 +1,87 @@
1
+ class WasmError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.status = 500;
5
+ }
6
+
7
+ get v3() {
8
+ return {
9
+ error_category: this.name,
10
+ error_type: this.type,
11
+ message: this.message,
12
+ severity: this.severity,
13
+ };
14
+ }
15
+
16
+ get modelInfo() {
17
+ return {
18
+ uuid: this.uuid,
19
+ input: this.input,
20
+ };
21
+ }
22
+ }
23
+
24
+ class BrowserError extends WasmError {
25
+ constructor(message) {
26
+ super(message);
27
+ this.name = "browser_error";
28
+ this.type = "error.BROWSER_ERROR";
29
+ this.severity = "low";
30
+ }
31
+ }
32
+
33
+ class NotSupportedError extends BrowserError {
34
+ constructor(support, uuid = "", input = {}) {
35
+ super(`${support} support is required`);
36
+ this.name = "not_supported_error";
37
+ this.type = "error.NOT_SUPPORTED";
38
+ this.severity = "high";
39
+ this.uuid = uuid;
40
+ this.input = input;
41
+ }
42
+ }
43
+
44
+ class ModelError extends WasmError {
45
+ constructor(message) {
46
+ super(message);
47
+ this.name = "model_error";
48
+ this.type = "error.MODEL_ERROR";
49
+ this.severity = "low";
50
+ }
51
+ }
52
+
53
+ class MissingModelError extends ModelError {
54
+ constructor(uuid) {
55
+ super(`Model ${uuid} cannot be found.`);
56
+ this.name = "missing_model_error";
57
+ this.uuid = uuid;
58
+ this.type = "error.MISSING_MODEL_ERROR";
59
+ this.severity = "low";
60
+ }
61
+ }
62
+
63
+ class UnauthorizedError extends WasmError {
64
+ constructor(message, status) {
65
+ super(message || "Unauthorized");
66
+ this.status = status || 401;
67
+ this.name = "unauthorized";
68
+ this.type = "error.UNAUTHORIZED";
69
+ }
70
+ }
71
+
72
+ class BadRequestError extends WasmError {
73
+ constructor(message, status) {
74
+ super(message);
75
+ this.status = status || 400;
76
+ this.name = "bad_request_error";
77
+ this.type = "error.BAD_REQUEST_ERROR";
78
+ }
79
+ }
80
+
81
+ module.exports = {
82
+ MissingModelError,
83
+ BrowserError,
84
+ NotSupportedError,
85
+ UnauthorizedError,
86
+ BadRequestError,
87
+ };
@@ -0,0 +1,79 @@
1
+ const WasmRunnerErrors = require("../error.js");
2
+
3
+ const isEmpty = (obj) => {
4
+ [Object, Array].includes((obj || {}).constructor) &&
5
+ !Object.entries(obj || {}).length;
6
+ };
7
+
8
+ const isValidURL = (url) => {
9
+ try {
10
+ new URL(url);
11
+ return true;
12
+ } catch (e) {
13
+ return false;
14
+ }
15
+ };
16
+
17
+ function isBrowser() {
18
+ try {
19
+ // eslint-disable-next-line no-undef
20
+ return window;
21
+ } catch (e) {
22
+ return false;
23
+ }
24
+ }
25
+
26
+ function isWasmSupported() {
27
+ try {
28
+ if (
29
+ typeof WebAssembly === "object" &&
30
+ typeof WebAssembly.instantiate === "function"
31
+ ) {
32
+ const module = new WebAssembly.Module(
33
+ Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00)
34
+ );
35
+
36
+ if (module instanceof WebAssembly.Module) {
37
+ return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
38
+ }
39
+ }
40
+ } catch (e) {}
41
+ return false;
42
+ }
43
+
44
+ function isCompatible() {
45
+ // if (!isBrowser()) {
46
+ // throw new WasmRunnerErrors.NotSupportedError(
47
+ // "Javascript Browser Environment"
48
+ // );
49
+ // }
50
+
51
+ if (!isWasmSupported()) {
52
+ throw new WasmRunnerErrors.NotSupportedError("WebAssembly");
53
+ }
54
+
55
+ return true;
56
+ }
57
+
58
+ const getAuthHeaders = (token, authType, tenant) => {
59
+ const authHeaders = {};
60
+ if (authType === "syntheticKey") {
61
+ authHeaders["x-tenant-name"] = tenant;
62
+ authHeaders["x-synthetic-key"] = token;
63
+ }
64
+
65
+ if (authType === "bearerToken") {
66
+ authHeaders.authorization = `Bearer ${token}`;
67
+ }
68
+
69
+ return authHeaders;
70
+ };
71
+
72
+ module.exports = {
73
+ isEmpty,
74
+ isValidURL,
75
+ isCompatible,
76
+ isWasmSupported,
77
+ isBrowser,
78
+ getAuthHeaders,
79
+ };
package/src/logger.js ADDED
@@ -0,0 +1,3 @@
1
+ const pino = require("pino");
2
+
3
+ module.exports = pino({});
package/src/models.js ADDED
@@ -0,0 +1,26 @@
1
+ /**
2
+ *
3
+ * @param {object} nodegen
4
+ * @returns
5
+ */
6
+ const processModels = (nodegen) => {
7
+ switch (nodegen.type) {
8
+ case "binary":
9
+ case "uint8array":
10
+ case "base64":
11
+ console.log("ENTERED PROCESSING");
12
+ const model = Buffer.from(nodegen.binary, "base64");
13
+ nodegen.binary = model;
14
+ console.log("NODE: ", nodegen.binary);
15
+ break;
16
+ case "function":
17
+ const func = nodegen.binary;
18
+ nodegen.binary = func();
19
+ default:
20
+ break;
21
+ }
22
+
23
+ return nodegen;
24
+ };
25
+
26
+ module.exports = processModels;