@picahq/authkit-token 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.MD ADDED
@@ -0,0 +1,77 @@
1
+ # authkit-token
2
+
3
+ [![npm version](https://img.shields.io/npm/v/%40picahq%2Fauthkit-token)](https://npmjs.com/package/@picahq/authkit-token)
4
+
5
+ Secure token generation for [Pica's AuthKit](https://docs.picaos.com/authkit) using Node.js.
6
+
7
+ ## Install
8
+
9
+ With npm:
10
+
11
+ ```jsx
12
+ npm i @picahq/authkit-token
13
+ ```
14
+
15
+ With yarn:
16
+
17
+ ```jsx
18
+ yarn add @picahq/authkit-token
19
+ ```
20
+
21
+ ## Creating a token endpoint
22
+
23
+ You'll want to create an internal endpoint that's used to generate secure tokens for your frontend. You can do so by adding code that looks like the below snippet.
24
+
25
+ ```jsx
26
+ import { AuthKitToken } from "@picahq/authkit-token";
27
+
28
+ app.post("/authkit-token", async (request, response) => {
29
+ const authKitToken = new AuthKitToken("sk_live_1234");
30
+ const token = await authKitToken.create();
31
+
32
+ response.send(token);
33
+ });
34
+ ```
35
+
36
+ You can get your API key from the [Pica dashboard](https://dashboard.picaos.com/settings/api-keys).
37
+
38
+ If you pass an `identity` or `identityType` (`user`, `team`, `organization`, or `project`), you'll be able to query for all connections scoped to that identity. The identity is used to generate the unique Connection Key for the user once they successfully connect an account.
39
+
40
+ ## Frontend Implementation
41
+
42
+ To implement the AuthKit component in your frontend, you'll need to use the `@picahq/authkit` package. It's fully compatible with popular frameworks such as React, Next.js, Vue, Svelte, and more.
43
+
44
+ - **NPM package**: https://www.npmjs.com/package/@picahq/authkit
45
+ - **Documentation**: https://docs.picaos.com/authkit
46
+ - **Setup guide**: https://docs.picaos.com/authkit/setup
47
+
48
+ ## Diagram
49
+
50
+ ```mermaid
51
+ sequenceDiagram
52
+ participant User
53
+ participant YourApp as Your Application
54
+ participant YourBackend as Your Backend
55
+ participant Pica as Pica AuthKit
56
+ participant Integration as Third-party Integration
57
+
58
+ User->>YourApp: Clicks "Connect Integration"
59
+ YourApp->>Pica: Open AuthKit modal
60
+ Pica->>YourBackend: Request AuthKit token
61
+ YourBackend->>Pica: Generate token with user identity
62
+ Pica->>Pica: Display integrations list
63
+ User->>Pica: Select integration & authenticate
64
+ Pica->>Integration: OAuth handshake
65
+ Integration->>Pica: Access token
66
+ Pica->>Pica: Store encrypted credentials
67
+ Pica->>YourApp: Return connection details
68
+ YourApp->>User: Connection successful!
69
+ ```
70
+
71
+ ## Full Documentation
72
+
73
+ Please refer to the official [Pica AuthKit](https://docs.picaos.com/authkit) docs for a more holistic understanding of AuthKit.
74
+
75
+ ## License
76
+
77
+ GPL-3.0
@@ -0,0 +1,2 @@
1
+ export * from './src/client';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src/client"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,+CAA6B"}
@@ -0,0 +1,2 @@
1
+ export * from './link.api';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/apis/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC"}
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./link.api"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/apis/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,6CAA2B"}
@@ -0,0 +1,3 @@
1
+ import { CreateEventLinkPayload } from '../types';
2
+ export declare const createEventLinkTokenApi: (headers: Record<string, string>, url: string, payload?: CreateEventLinkPayload) => Promise<any>;
3
+ //# sourceMappingURL=link.api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.api.d.ts","sourceRoot":"","sources":["../../../src/apis/link.api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,sBAAsB,EAGvB,MAAM,UAAU,CAAC;AAgGlB,eAAO,MAAM,uBAAuB,YACzB,OAAO,MAAM,EAAE,MAAM,CAAC,OAC1B,MAAM,YACD,sBAAsB,iBAkBjC,CAAC"}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createEventLinkTokenApi = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ async function paginateAuthkitConnections(url, headers, payload, options = {}) {
9
+ const { limit = 100, maxConcurrentRequests = 3, maxRetries = 3 } = options;
10
+ const fetchAuthkitPage = async (page, pageLimit) => {
11
+ const response = await axios_1.default.post(`${url}/v1/authkit?limit=${pageLimit}&page=${page}`, payload || {}, { headers });
12
+ return response.data;
13
+ };
14
+ const firstResponse = await fetchAuthkitPage(1, limit);
15
+ const { pages, total } = firstResponse;
16
+ if (pages <= 1) {
17
+ return firstResponse;
18
+ }
19
+ const remainingPages = Array.from({ length: pages - 1 }, (_, i) => i + 2);
20
+ const fetchPageWithRetry = async (page) => {
21
+ let lastError;
22
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
23
+ try {
24
+ const response = await fetchAuthkitPage(page, limit);
25
+ return response;
26
+ }
27
+ catch (error) {
28
+ lastError = error;
29
+ if (attempt < maxRetries) {
30
+ await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)));
31
+ }
32
+ }
33
+ }
34
+ throw lastError;
35
+ };
36
+ const responses = [firstResponse];
37
+ for (let i = 0; i < remainingPages.length; i += maxConcurrentRequests) {
38
+ const batch = remainingPages.slice(i, i + maxConcurrentRequests);
39
+ const batchPromises = batch.map((page) => fetchPageWithRetry(page));
40
+ try {
41
+ const batchResults = await Promise.all(batchPromises);
42
+ responses.push(...batchResults);
43
+ }
44
+ catch (error) {
45
+ console.error(`Failed to fetch authkit batch starting at page ${batch[0]}:`, error);
46
+ throw error;
47
+ }
48
+ }
49
+ const allRows = responses.flatMap((response) => response.rows);
50
+ const latestResponse = responses[responses.length - 1];
51
+ return {
52
+ rows: allRows,
53
+ page: 1,
54
+ pages: 1,
55
+ total,
56
+ requestId: latestResponse.requestId,
57
+ };
58
+ }
59
+ const createEventLinkTokenApi = async (headers, url, payload) => {
60
+ try {
61
+ const authkitResponse = await paginateAuthkitConnections(url, headers, payload, { limit: 100, maxConcurrentRequests: 3, maxRetries: 3 });
62
+ return authkitResponse;
63
+ }
64
+ catch (error) {
65
+ if (axios_1.default.isAxiosError(error)) {
66
+ return error.response?.data;
67
+ }
68
+ }
69
+ };
70
+ exports.createEventLinkTokenApi = createEventLinkTokenApi;
71
+ //# sourceMappingURL=link.api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link.api.js","sourceRoot":"","sources":["../../../src/apis/link.api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ interface ClientConfig {
2
+ baseUrl?: string;
3
+ }
4
+ export declare class AuthKitToken {
5
+ private secret;
6
+ private configs;
7
+ constructor(secret: string, configs?: ClientConfig);
8
+ get _clientInfo(): {
9
+ secret: string;
10
+ configs: ClientConfig;
11
+ };
12
+ get _url(): string;
13
+ create(payload?: {
14
+ identity?: string;
15
+ identityType?: "user" | "team" | "organization" | "project";
16
+ group?: string;
17
+ label?: string;
18
+ }): Promise<any>;
19
+ }
20
+ export {};
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAGA,UAAU,YAAY;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAe;gBAElB,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAQtD,IAAI,WAAW;;;MAKd;IAED,IAAI,IAAI,WAMP;IAEK,MAAM,CAAC,OAAO,CAAC,EAAE;QAKrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;QAE5D,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB;CAQF"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthKitToken = void 0;
4
+ const getHeaders_1 = require("../logic/getHeaders");
5
+ const apis_1 = require("../apis");
6
+ class AuthKitToken {
7
+ constructor(secret, configs = {}) {
8
+ this.secret = secret;
9
+ this.configs = configs;
10
+ }
11
+ get _clientInfo() {
12
+ return {
13
+ secret: this.secret,
14
+ configs: this.configs,
15
+ };
16
+ }
17
+ get _url() {
18
+ if (this.configs.baseUrl) {
19
+ return this.configs.baseUrl;
20
+ }
21
+ return 'https://api.picaos.com';
22
+ }
23
+ async create(payload) {
24
+ const secret = this._clientInfo.secret;
25
+ const url = this._url;
26
+ const headers = (0, getHeaders_1.getHeaders)(secret);
27
+ const result = await (0, apis_1.createEventLinkTokenApi)(headers, url, payload);
28
+ return result;
29
+ }
30
+ }
31
+ exports.AuthKitToken = AuthKitToken;
32
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":";;;AAAA,oDAAiD;AACjD,kCAAkD;AAMlD,MAAa,YAAY;IAIvB,YAAY,MAAc,EAAE,UAAwB,EAAE;QACpD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAKD,IAAI,WAAW;QACb,OAAO;YACL,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;IACJ,CAAC;IAED,IAAI,IAAI;QACN,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YACxB,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;SAC7B;QAED,OAAO,wBAAwB,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAWZ;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACvC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,MAAM,OAAO,GAAG,IAAA,uBAAU,EAAC,MAAM,CAAC,CAAC;QAEnC,MAAM,MAAM,GAAG,MAAM,IAAA,8BAAuB,EAAC,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA9CD,oCA8CC"}
@@ -0,0 +1,5 @@
1
+ export declare const getHeaders: (secret: string) => {
2
+ 'X-Pica-Secret': string;
3
+ 'Content-Type': string;
4
+ };
5
+ //# sourceMappingURL=getHeaders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getHeaders.d.ts","sourceRoot":"","sources":["../../../src/logic/getHeaders.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,WAAY,MAAM;;;CAKxC,CAAC"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getHeaders = void 0;
4
+ const getHeaders = (secret) => {
5
+ return {
6
+ 'X-Pica-Secret': secret,
7
+ 'Content-Type': 'application/json',
8
+ };
9
+ };
10
+ exports.getHeaders = getHeaders;
11
+ //# sourceMappingURL=getHeaders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getHeaders.js","sourceRoot":"","sources":["../../../src/logic/getHeaders.ts"],"names":[],"mappings":";;;AAAO,MAAM,UAAU,GAAG,CAAC,MAAc,EAAE,EAAE;IAC3C,OAAO;QACL,eAAe,EAAE,MAAM;QACvB,cAAc,EAAE,kBAAkB;KACnC,CAAC;AACJ,CAAC,CAAC;AALW,QAAA,UAAU,cAKrB"}
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './src/client';
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@picahq/authkit-token",
3
+ "version": "1.0.0",
4
+ "description": "Secure token generation for Pica AuthKit using Node.js",
5
+ "main": "dist/index.js",
6
+ "scripts": {
7
+ "build": "tsc --build tsconfig.json",
8
+ "test": "jest --coverage"
9
+ },
10
+ "repository": {
11
+ "type": "git"
12
+ },
13
+ "keywords": [
14
+ "pica",
15
+ "integrations",
16
+ "authkit"
17
+ ],
18
+ "author": "@picahq",
19
+ "license": "GPL-3.0",
20
+ "dependencies": {
21
+ "@types/ramda": "^0.28.22",
22
+ "axios": "^1.6.7",
23
+ "jsonwebtoken": "^9.0.1",
24
+ "jwt-decode": "^3.1.2",
25
+ "lodash.ismatch": "^4.4.0",
26
+ "lodash.memoize": "^4.1.2",
27
+ "ramda": "^0.28.0",
28
+ "tslib": "^2.4.1"
29
+ },
30
+ "devDependencies": {
31
+ "@babel/core": "^7.17.9",
32
+ "@babel/preset-env": "^7.16.11",
33
+ "@babel/preset-typescript": "^7.16.7",
34
+ "@types/jsonwebtoken": "^9.0.2",
35
+ "@types/lodash.ismatch": "^4.4.7",
36
+ "@types/lodash.memoize": "^4.1.7",
37
+ "@types/uuid": "^8.3.4",
38
+ "babel-jest": "^27.5.1",
39
+ "jest": "^27.5.1",
40
+ "jsonwebtoken": "^9.0.1",
41
+ "ts-jest": "^27.1.4",
42
+ "typescript": "^4.6.4",
43
+ "uuid": "^9.0.0"
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "gitHead": "35ff51f0afc618c5413ef9bab778f4dcf582b8ab",
49
+ "homepage": "https://picaos.com",
50
+ "documentation": "https://docs.picaos.com/authkit"
51
+ }
@@ -0,0 +1 @@
1
+ export * from './link.api';
@@ -0,0 +1,123 @@
1
+ import axios from 'axios';
2
+ import {
3
+ CreateEventLinkPayload,
4
+ AuthkitResponse,
5
+ ConnectorPaginationOptions,
6
+ } from '../types';
7
+
8
+ /**
9
+ * Pagination helper function for authkit API that fetches all pages
10
+ * @param url - Base URL for the API
11
+ * @param headers - Request headers
12
+ * @param payload - Request payload
13
+ * @param options - Pagination options
14
+ * @returns Promise with all combined results
15
+ */
16
+ async function paginateAuthkitConnections(
17
+ url: string,
18
+ headers: Record<string, string>,
19
+ payload?: CreateEventLinkPayload,
20
+ options: ConnectorPaginationOptions = {},
21
+ ): Promise<AuthkitResponse> {
22
+ const { limit = 100, maxConcurrentRequests = 3, maxRetries = 3 } = options;
23
+
24
+ // Function to fetch a specific page
25
+ const fetchAuthkitPage = async (page: number, pageLimit: number): Promise<AuthkitResponse> => {
26
+ const response = await axios.post<AuthkitResponse>(
27
+ `${url}/v1/authkit?limit=${pageLimit}&page=${page}`,
28
+ payload || {},
29
+ { headers }
30
+ );
31
+ return response.data;
32
+ };
33
+
34
+ // First request to get total pages count
35
+ const firstResponse = await fetchAuthkitPage(1, limit);
36
+ const { pages, total } = firstResponse;
37
+
38
+ // If we got all data in first request, return it
39
+ if (pages <= 1) {
40
+ return firstResponse;
41
+ }
42
+
43
+ // Create array of remaining page numbers to fetch
44
+ const remainingPages = Array.from({ length: pages - 1 }, (_, i) => i + 2);
45
+
46
+ // Function to fetch a page with retry logic
47
+ const fetchPageWithRetry = async (page: number): Promise<AuthkitResponse> => {
48
+ let lastError: any;
49
+
50
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
51
+ try {
52
+ const response = await fetchAuthkitPage(page, limit);
53
+ return response;
54
+ } catch (error) {
55
+ lastError = error;
56
+ if (attempt < maxRetries) {
57
+ // Exponential backoff: wait 1s, 2s, 4s...
58
+ await new Promise((resolve) =>
59
+ setTimeout(resolve, 1000 * Math.pow(2, attempt - 1)),
60
+ );
61
+ }
62
+ }
63
+ }
64
+
65
+ throw lastError;
66
+ };
67
+
68
+ // Execute requests in batches to avoid overwhelming the API
69
+ const responses: AuthkitResponse[] = [firstResponse];
70
+
71
+ for (let i = 0; i < remainingPages.length; i += maxConcurrentRequests) {
72
+ const batch = remainingPages.slice(i, i + maxConcurrentRequests);
73
+ const batchPromises = batch.map((page) => fetchPageWithRetry(page));
74
+
75
+ try {
76
+ const batchResults = await Promise.all(batchPromises);
77
+ responses.push(...batchResults);
78
+ } catch (error) {
79
+ console.error(
80
+ `Failed to fetch authkit batch starting at page ${batch[0]}:`,
81
+ error,
82
+ );
83
+ throw error;
84
+ }
85
+ }
86
+
87
+ // Combine all results
88
+ const allRows = responses.flatMap((response) => response.rows);
89
+
90
+ // Get the latest requestId from the most recent response
91
+ const latestResponse = responses[responses.length - 1];
92
+
93
+ return {
94
+ rows: allRows,
95
+ page: 1, // Since we're returning all data, we're effectively on "page 1"
96
+ pages: 1,
97
+ total,
98
+ requestId: latestResponse.requestId, // Use the requestId from the latest response
99
+ };
100
+ }
101
+
102
+ export const createEventLinkTokenApi = async (
103
+ headers: Record<string, string>,
104
+ url: string,
105
+ payload?: CreateEventLinkPayload,
106
+ ) => {
107
+ try {
108
+
109
+ // Fetch all authkit connections with pagination support
110
+ const authkitResponse = await paginateAuthkitConnections(
111
+ url,
112
+ headers,
113
+ payload,
114
+ { limit: 100, maxConcurrentRequests: 3, maxRetries: 3 }
115
+ );
116
+
117
+ return authkitResponse;
118
+ } catch (error) {
119
+ if (axios.isAxiosError(error)) {
120
+ return error.response?.data;
121
+ }
122
+ }
123
+ };
@@ -0,0 +1,54 @@
1
+ import { getHeaders } from '../logic/getHeaders';
2
+ import { createEventLinkTokenApi } from '../apis';
3
+
4
+ interface ClientConfig {
5
+ baseUrl?: string;
6
+ }
7
+
8
+ export class AuthKitToken {
9
+ private secret: string;
10
+ private configs: ClientConfig;
11
+
12
+ constructor(secret: string, configs: ClientConfig = {}) {
13
+ this.secret = secret;
14
+ this.configs = configs;
15
+ }
16
+
17
+ /**
18
+ * Not for use outside the SDK lib
19
+ */
20
+ get _clientInfo() {
21
+ return {
22
+ secret: this.secret,
23
+ configs: this.configs,
24
+ };
25
+ }
26
+
27
+ get _url() {
28
+ if (this.configs.baseUrl) {
29
+ return this.configs.baseUrl;
30
+ }
31
+
32
+ return 'https://api.picaos.com';
33
+ }
34
+
35
+ async create(payload?: {
36
+ /**
37
+ * Unique identifier for the token.
38
+ * @remarks It is recommended to avoid using spaces and colons in this field as it may lead to unexpected behavior in some systems.
39
+ */
40
+ identity?: string;
41
+ identityType?: "user" | "team" | "organization" | "project";
42
+ /** @deprecated Use 'identity' instead */
43
+ group?: string;
44
+ /** @deprecated */
45
+ label?: string;
46
+ }) {
47
+ const secret = this._clientInfo.secret;
48
+ const url = this._url;
49
+ const headers = getHeaders(secret);
50
+
51
+ const result = await createEventLinkTokenApi(headers, url, payload);
52
+ return result;
53
+ }
54
+ }
@@ -0,0 +1,6 @@
1
+ export const getHeaders = (secret: string) => {
2
+ return {
3
+ 'X-Pica-Secret': secret,
4
+ 'Content-Type': 'application/json',
5
+ };
6
+ };