@milaboratories/pl-client 2.16.11 → 2.16.13
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/dist/core/driver.cjs +1 -1
- package/dist/core/driver.cjs.map +1 -1
- package/dist/core/driver.js +1 -1
- package/dist/core/driver.js.map +1 -1
- package/dist/core/errors.cjs +2 -0
- package/dist/core/errors.cjs.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +2 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/ll_client.cjs +32 -9
- package/dist/core/ll_client.cjs.map +1 -1
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/core/ll_client.js +32 -9
- package/dist/core/ll_client.js.map +1 -1
- package/dist/core/ll_transaction.cjs +10 -0
- package/dist/core/ll_transaction.cjs.map +1 -1
- package/dist/core/ll_transaction.d.ts +1 -0
- package/dist/core/ll_transaction.d.ts.map +1 -1
- package/dist/core/ll_transaction.js +10 -0
- package/dist/core/ll_transaction.js.map +1 -1
- package/dist/core/websocket_stream.cjs +330 -0
- package/dist/core/websocket_stream.cjs.map +1 -0
- package/dist/core/websocket_stream.d.ts +67 -0
- package/dist/core/websocket_stream.d.ts.map +1 -0
- package/dist/core/websocket_stream.js +328 -0
- package/dist/core/websocket_stream.js.map +1 -0
- package/dist/helpers/retry_strategy.cjs +92 -0
- package/dist/helpers/retry_strategy.cjs.map +1 -0
- package/dist/helpers/retry_strategy.d.ts +24 -0
- package/dist/helpers/retry_strategy.d.ts.map +1 -0
- package/dist/helpers/retry_strategy.js +89 -0
- package/dist/helpers/retry_strategy.js.map +1 -0
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +136 -0
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +75 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +135 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
- package/dist/proto-rest/index.cjs +16 -2
- package/dist/proto-rest/index.cjs.map +1 -1
- package/dist/proto-rest/index.d.ts.map +1 -1
- package/dist/proto-rest/index.js +16 -2
- package/dist/proto-rest/index.js.map +1 -1
- package/package.json +6 -6
- package/src/core/driver.ts +1 -1
- package/src/core/errors.ts +1 -0
- package/src/core/ll_client.ts +42 -9
- package/src/core/ll_transaction.test.ts +18 -0
- package/src/core/ll_transaction.ts +12 -0
- package/src/core/websocket_stream.test.ts +423 -0
- package/src/core/websocket_stream.ts +400 -0
- package/src/helpers/retry_strategy.ts +123 -0
- package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +179 -1
- package/src/proto-rest/index.ts +17 -2
|
@@ -14,9 +14,23 @@ function createClient(opts) {
|
|
|
14
14
|
const client = createOpenApiClient({
|
|
15
15
|
baseUrl: `${scheme}${opts.hostAndPort}`,
|
|
16
16
|
fetch: (input) => {
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// If body has already been consumed, clone the request
|
|
18
|
+
const request = input.bodyUsed ? input.clone() : input;
|
|
19
|
+
return undici.fetch(request.url, {
|
|
20
|
+
body: request.body,
|
|
21
|
+
cache: request.cache,
|
|
22
|
+
credentials: request.credentials,
|
|
19
23
|
dispatcher: opts.dispatcher,
|
|
24
|
+
duplex: request.duplex,
|
|
25
|
+
headers: request.headers,
|
|
26
|
+
integrity: request.integrity,
|
|
27
|
+
keepalive: request.keepalive,
|
|
28
|
+
method: request.method,
|
|
29
|
+
mode: request.mode,
|
|
30
|
+
redirect: request.redirect,
|
|
31
|
+
referrer: request.referrer,
|
|
32
|
+
referrerPolicy: request.referrerPolicy,
|
|
33
|
+
signal: request.signal,
|
|
20
34
|
});
|
|
21
35
|
},
|
|
22
36
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n return undiciFetch(
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(respErr.error);\n },\n };\n}\n"],"names":["undiciFetch","rethrowMeaningfulError"],"mappings":";;;;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,YAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;AAEA,YAAAC,6BAAsB,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,CAAC;KACF;AACH;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proto-rest/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAkC,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAwB,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAElD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAA;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,EAAE,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/proto-rest/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAkC,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAwB,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAErD,OAAO,EAAE,UAAU,EAAE,CAAC;AACtB,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;AAElD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,CAAA;AAED,wBAAgB,YAAY,CAAC,KAAK,SAAS,EAAE,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,CA4BpF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,GAAG,EAAE,CAAC;CAChB,CAAA;AACD,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC;IACpE,KAAK,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC,CAeD"}
|
package/dist/proto-rest/index.js
CHANGED
|
@@ -12,9 +12,23 @@ function createClient(opts) {
|
|
|
12
12
|
const client = createOpenApiClient({
|
|
13
13
|
baseUrl: `${scheme}${opts.hostAndPort}`,
|
|
14
14
|
fetch: (input) => {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// If body has already been consumed, clone the request
|
|
16
|
+
const request = input.bodyUsed ? input.clone() : input;
|
|
17
|
+
return fetch(request.url, {
|
|
18
|
+
body: request.body,
|
|
19
|
+
cache: request.cache,
|
|
20
|
+
credentials: request.credentials,
|
|
17
21
|
dispatcher: opts.dispatcher,
|
|
22
|
+
duplex: request.duplex,
|
|
23
|
+
headers: request.headers,
|
|
24
|
+
integrity: request.integrity,
|
|
25
|
+
keepalive: request.keepalive,
|
|
26
|
+
method: request.method,
|
|
27
|
+
mode: request.mode,
|
|
28
|
+
redirect: request.redirect,
|
|
29
|
+
referrer: request.referrer,
|
|
30
|
+
referrerPolicy: request.referrerPolicy,
|
|
31
|
+
signal: request.signal,
|
|
18
32
|
});
|
|
19
33
|
},
|
|
20
34
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n return undiciFetch(
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/proto-rest/index.ts"],"sourcesContent":["//\n// This file is NOT autogenerated!\n//\n// After generating new clients from openapi specifications, add client types here.\n//\n\nimport type { paths as PlApiPaths } from './plapi';\nimport { default as createOpenApiClient, type Middleware, type Client } from 'openapi-fetch';\nimport { Dispatcher, fetch as undiciFetch } from 'undici';\nimport { rethrowMeaningfulError } from '../core/errors';\nimport { Code } from '../proto-grpc/google/rpc/code';\n\nexport { PlApiPaths };\nexport type PlRestClientType = Client<PlApiPaths>;\n\nexport type RestClientConfig = {\n hostAndPort: string;\n ssl: boolean;\n dispatcher: Dispatcher;\n middlewares: Middleware[];\n}\n\nexport function createClient<Paths extends {}>(opts: RestClientConfig): Client<Paths> {\n const scheme = opts.ssl ? 'https://' : 'http://';\n const client = createOpenApiClient<Paths>({\n baseUrl: `${scheme}${opts.hostAndPort}`,\n fetch: (input: Request): Promise<Response> => {\n // If body has already been consumed, clone the request\n const request = input.bodyUsed ? input.clone() : input;\n\n return undiciFetch(request.url, {\n body: request.body,\n cache: request.cache,\n credentials: request.credentials,\n dispatcher: opts.dispatcher,\n duplex: request.duplex,\n headers: request.headers,\n integrity: request.integrity,\n keepalive: request.keepalive,\n method: request.method,\n mode: request.mode,\n redirect: request.redirect,\n referrer: request.referrer,\n referrerPolicy: request.referrerPolicy,\n signal: request.signal,\n });\n },\n });\n client.use(errorHandlerMiddleware(), ...opts.middlewares);\n return client;\n}\n\nexport type ErrorResponse = {\n code: Code,\n message: string,\n details: any[],\n}\nexport async function parseResponseError(response: Response): Promise<{\n error?: ErrorResponse | string,\n origBody?: string,\n}>{\n if (response.status < 400) {\n return {};\n }\n\n let error: any = await response.clone().text();\n const origBody = error;\n try {\n error = JSON.parse(error) as ErrorResponse;\n } catch {\n }\n return {\n error: error,\n origBody\n };\n}\n\n/**\n * Parses all API responses and thrown error in case of error response.\n * Allows all callers to not bother about .error response field checking.\n */\nfunction errorHandlerMiddleware(): Middleware {\n return {\n onResponse: async ({ request: _request, response, options: _options }) => {\n const respErr = await parseResponseError(response);\n if (!respErr.error) {\n const { body, ...resOptions } = response;\n return new Response(body, { ...resOptions, status: response.status });\n }\n\n if (typeof respErr.error === 'string') {\n throw new Error(respErr.error);\n }\n\n rethrowMeaningfulError(respErr.error);\n },\n };\n}\n"],"names":["undiciFetch"],"mappings":";;;;AAAA;AACA;AACA;AACA;AACA;AAkBM,SAAU,YAAY,CAAmB,IAAsB,EAAA;AACnE,IAAA,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,GAAG,UAAU,GAAG,SAAS;IAChD,MAAM,MAAM,GAAG,mBAAmB,CAAQ;AACxC,QAAA,OAAO,EAAE,CAAA,EAAG,MAAM,GAAG,IAAI,CAAC,WAAW,CAAA,CAAE;AACvC,QAAA,KAAK,EAAE,CAAC,KAAc,KAAuB;;AAE3C,YAAA,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,KAAK;AAEtD,YAAA,OAAOA,KAAW,CAAC,OAAO,CAAC,GAAG,EAAE;gBAC9B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;gBACtC,MAAM,EAAE,OAAO,CAAC,MAAM;AACvB,aAAA,CAAC;QACJ,CAAC;AACF,KAAA,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,sBAAsB,EAAE,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;AACzD,IAAA,OAAO,MAAM;AACf;AAOO,eAAe,kBAAkB,CAAC,QAAkB,EAAA;AAIzD,IAAA,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE;AACzB,QAAA,OAAO,EAAE;IACX;IAEA,IAAI,KAAK,GAAQ,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE;IAC9C,MAAM,QAAQ,GAAG,KAAK;AACtB,IAAA,IAAI;AACF,QAAA,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAkB;IAC5C;AAAE,IAAA,MAAM;IACR;IACA,OAAO;AACL,QAAA,KAAK,EAAE,KAAK;QACZ;KACD;AACH;AAEA;;;AAGG;AACH,SAAS,sBAAsB,GAAA;IAC7B,OAAO;AACL,QAAA,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAI;AACvE,YAAA,MAAM,OAAO,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC;AAClD,YAAA,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE;gBAClB,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,EAAE,GAAG,QAAQ;AACxC,gBAAA,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;YACvE;AAEA,YAAA,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE;AACrC,gBAAA,MAAM,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YAChC;AAEA,YAAA,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC;QACvC,CAAC;KACF;AACH;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-client",
|
|
3
|
-
"version": "2.16.
|
|
3
|
+
"version": "2.16.13",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22.19.0"
|
|
6
6
|
},
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"utility-types": "^3.11.0",
|
|
37
37
|
"yaml": "^2.8.0",
|
|
38
38
|
"@milaboratories/pl-http": "1.2.0",
|
|
39
|
-
"@milaboratories/
|
|
40
|
-
"@milaboratories/
|
|
39
|
+
"@milaboratories/ts-helpers": "1.5.4",
|
|
40
|
+
"@milaboratories/pl-model-common": "1.21.10"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@protobuf-ts/plugin": "2.11.1",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
"@vitest/coverage-v8": "^4.0.7",
|
|
48
48
|
"vitest": "^4.0.7",
|
|
49
49
|
"typescript": "~5.6.3",
|
|
50
|
-
"@milaboratories/
|
|
51
|
-
"@milaboratories/
|
|
52
|
-
"@milaboratories/ts-
|
|
50
|
+
"@milaboratories/ts-builder": "1.0.6",
|
|
51
|
+
"@milaboratories/build-configs": "1.0.9",
|
|
52
|
+
"@milaboratories/ts-configs": "1.0.6"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"type-check": "ts-builder types --target node",
|
package/src/core/driver.ts
CHANGED
|
@@ -36,6 +36,6 @@ export function addRTypeToMetadata(rType: ResourceType, options?: RpcOptions) {
|
|
|
36
36
|
*/
|
|
37
37
|
export function createRTypeRoutingHeader(rType: ResourceType): Record<string, string> {
|
|
38
38
|
return {
|
|
39
|
-
|
|
39
|
+
resourceType: `${rType.name}:${rType.version}`,
|
|
40
40
|
};
|
|
41
41
|
}
|
package/src/core/errors.ts
CHANGED
|
@@ -24,6 +24,7 @@ export function isUnauthenticated(err: unknown, nested: boolean = false): boolea
|
|
|
24
24
|
|
|
25
25
|
export function isTimeoutOrCancelError(err: unknown, nested: boolean = false): boolean {
|
|
26
26
|
if (err instanceof Aborted || (err as any).name == 'AbortError') return true;
|
|
27
|
+
if ((err as any).name == 'TimeoutError') return true;
|
|
27
28
|
if ((err as any).code == 'ABORT_ERR') return true;
|
|
28
29
|
if ((err as any).code == Code.ABORTED) return true;
|
|
29
30
|
if (
|
package/src/core/ll_client.ts
CHANGED
|
@@ -28,6 +28,8 @@ import type * as grpcTypes from '../proto-grpc/github.com/milaboratory/pl/plapi/
|
|
|
28
28
|
import { type PlApiPaths, type PlRestClientType, createClient, parseResponseError } from '../proto-rest';
|
|
29
29
|
import { notEmpty } from '@milaboratories/ts-helpers';
|
|
30
30
|
import { Code } from '../proto-grpc/google/rpc/code';
|
|
31
|
+
import { WebSocketBiDiStream } from './websocket_stream';
|
|
32
|
+
import { TxAPI_ClientMessage, TxAPI_ServerMessage } from '../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api';
|
|
31
33
|
|
|
32
34
|
export interface PlCallOps {
|
|
33
35
|
timeout?: number;
|
|
@@ -161,7 +163,7 @@ export class LLPlClient implements WireClientProviderFactory {
|
|
|
161
163
|
}
|
|
162
164
|
|
|
163
165
|
private initRestConnection(): void {
|
|
164
|
-
const dispatcher = defaultHttpDispatcher(this.conf.
|
|
166
|
+
const dispatcher = defaultHttpDispatcher(this.conf.grpcProxy, this._restInterceptors);
|
|
165
167
|
this._replaceWireConnection({ type: 'rest', Config: this.conf, Dispatcher: dispatcher, Middlewares: this._restMiddlewares });
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -479,17 +481,48 @@ export class LLPlClient implements WireClientProviderFactory {
|
|
|
479
481
|
let totalAbortSignal = abortSignal;
|
|
480
482
|
if (ops.abortSignal) totalAbortSignal = AbortSignal.any([totalAbortSignal, ops.abortSignal]);
|
|
481
483
|
|
|
484
|
+
const timeout = ops.timeout ?? (rw ? this.conf.defaultRWTransactionTimeout : this.conf.defaultROTransactionTimeout);
|
|
485
|
+
|
|
482
486
|
const cl = this.clientProvider.get();
|
|
483
|
-
if (
|
|
484
|
-
|
|
485
|
-
|
|
487
|
+
if (cl instanceof GrpcPlApiClient) {
|
|
488
|
+
return cl.tx({
|
|
489
|
+
abort: totalAbortSignal,
|
|
490
|
+
timeout,
|
|
491
|
+
});
|
|
486
492
|
}
|
|
487
493
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
timeout
|
|
491
|
-
|
|
492
|
-
|
|
494
|
+
const wireConn = this.wireConnection;
|
|
495
|
+
if (wireConn.type === 'rest') {
|
|
496
|
+
// For REST/WebSocket protocol, timeout needs to be converted to AbortSignal
|
|
497
|
+
if (timeout !== undefined) {
|
|
498
|
+
totalAbortSignal = AbortSignal.any([totalAbortSignal, AbortSignal.timeout(timeout)]);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// The gRPC transport has the auth interceptor that already handles it, but here we need to refresh the auth information to be safe.
|
|
502
|
+
this.refreshAuthInformationIfNeeded();
|
|
503
|
+
|
|
504
|
+
const wsUrl = this.conf.ssl
|
|
505
|
+
? `wss://${this.conf.hostAndPort}/v1/ws/tx`
|
|
506
|
+
: `ws://${this.conf.hostAndPort}/v1/ws/tx`;
|
|
507
|
+
|
|
508
|
+
return new WebSocketBiDiStream(wsUrl,
|
|
509
|
+
(msg) => TxAPI_ClientMessage.toBinary(msg),
|
|
510
|
+
(data) => TxAPI_ServerMessage.fromBinary(new Uint8Array(data)),
|
|
511
|
+
{
|
|
512
|
+
abortSignal: totalAbortSignal,
|
|
513
|
+
jwtToken: this.authInformation?.jwtToken,
|
|
514
|
+
dispatcher: wireConn.Dispatcher,
|
|
515
|
+
|
|
516
|
+
onComplete: async (stream) => stream.requests.send({
|
|
517
|
+
// Ask server to gracefully close the stream on its side, if not done yet.
|
|
518
|
+
requestId: 0,
|
|
519
|
+
request: { oneofKind: 'streamClose', streamClose: {} },
|
|
520
|
+
}),
|
|
521
|
+
},
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
throw new Error(`transactions are not supported for wire protocol ${this._wireProto}`);
|
|
493
526
|
});
|
|
494
527
|
}
|
|
495
528
|
|
|
@@ -6,6 +6,24 @@ import { test, expect } from 'vitest';
|
|
|
6
6
|
import { isTimeoutOrCancelError } from './errors';
|
|
7
7
|
import { Aborted } from '@milaboratories/ts-helpers';
|
|
8
8
|
|
|
9
|
+
test('check successful transaction', async () => {
|
|
10
|
+
const client = await getTestLLClient();
|
|
11
|
+
const tx = client.createTx(true);
|
|
12
|
+
|
|
13
|
+
const openResp = await tx.send({
|
|
14
|
+
oneofKind: 'txOpen',
|
|
15
|
+
txOpen: { name: 'test', writable: TxAPI_Open_Request_WritableTx.WRITABLE, enableFormattedErrors: false }
|
|
16
|
+
}, false);
|
|
17
|
+
const commitResp = await tx.send({
|
|
18
|
+
oneofKind: 'txCommit',
|
|
19
|
+
txCommit: {}
|
|
20
|
+
}, false);
|
|
21
|
+
|
|
22
|
+
expect(openResp.txOpen.tx?.isValid).toBeTruthy();
|
|
23
|
+
expect(commitResp.txCommit.success).toBeTruthy();
|
|
24
|
+
await tx.await();
|
|
25
|
+
});
|
|
26
|
+
|
|
9
27
|
test('transaction timeout test', async () => {
|
|
10
28
|
const client = await getTestLLClient();
|
|
11
29
|
const tx = client.createTx(true, { timeout: 500 });
|
|
@@ -236,6 +236,13 @@ export class LLPlTransaction {
|
|
|
236
236
|
(currentHandler as AnySingleResponseHandler).resolve(message.response);
|
|
237
237
|
currentHandler = undefined;
|
|
238
238
|
}
|
|
239
|
+
|
|
240
|
+
// After receiving a terminal response (txCommit or txDiscard), we proactively close the client stream.
|
|
241
|
+
// This ensures consistent behavior between the gRPC and WebSocket transports,
|
|
242
|
+
// since the server closes the connection automatically upon transaction completion in both cases.
|
|
243
|
+
if (this.isTerminalResponse(message) && this.responseHandlerQueue.length === 0) {
|
|
244
|
+
await this.stream.requests.complete();
|
|
245
|
+
}
|
|
239
246
|
}
|
|
240
247
|
} catch (e: any) {
|
|
241
248
|
return this.assignErrorFactoryIfNotSet(() => {
|
|
@@ -332,4 +339,9 @@ export class LLPlTransaction {
|
|
|
332
339
|
this._completed = true;
|
|
333
340
|
await this.stream.requests.complete();
|
|
334
341
|
}
|
|
342
|
+
|
|
343
|
+
private isTerminalResponse(message: TxAPI_ServerMessage): boolean {
|
|
344
|
+
const kind = message.response.oneofKind;
|
|
345
|
+
return kind === 'txCommit' || kind === 'txDiscard';
|
|
346
|
+
}
|
|
335
347
|
}
|