@superdurable/connector-insightly 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +6 -2
- package/dex-connector.json +119 -0
- package/dist/chunk-PTFX7RNO.js +144 -0
- package/dist/connection-lTitKWJP.d.ts +103 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +286 -0
- package/dist/mock.d.ts +54 -0
- package/dist/mock.js +122 -0
- package/package.json +59 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Super Durable
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Insightly TypeScript connector
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This package implements `listContacts` and `getContact` for Dex TypeScript Flows. Both operations replay the Go reference's provider requests, branches, retries, and exact outputs through shared conformance scenarios. Results contain contact IDs, names, and email addresses; custom fields and links remain outside Flow results.
|
|
4
|
+
|
|
5
|
+
Sign in to [Insightly CRM](https://login.insightly.com/), open the top-right user icon > **User Settings > User Settings > API**, and copy your personal API key into Dex secret `api_key`. Copy the pod-specific v3.1 API URL into `baseUrl` if it differs from the displayed na1 default. See the [connector README](../README.md) and [contact discovery example](examples/contact-discovery/README.md).
|
|
6
|
+
|
|
7
|
+
JavaScript numbers cannot represent provider numeric IDs above the safe integer limit exactly; such responses select `invalidResponse`. Live Insightly authorization and responses have not been verified locally. Package release is independent of the Go and Python releases.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"connectorId": "insightly",
|
|
3
|
+
"releaseTag": "connectors/insightly/go/v0.1.0",
|
|
4
|
+
"package": {
|
|
5
|
+
"ecosystem": "npm",
|
|
6
|
+
"name": "@superdurable/connector-insightly",
|
|
7
|
+
"version": "0.1.0"
|
|
8
|
+
},
|
|
9
|
+
"factories": [
|
|
10
|
+
{
|
|
11
|
+
"name": "newListContactsStep",
|
|
12
|
+
"kind": "query",
|
|
13
|
+
"operationId": "listContacts",
|
|
14
|
+
"inputType": "ListContactsInput",
|
|
15
|
+
"description": "List contact IDs, names, and email addresses with a bounded page size and offset.",
|
|
16
|
+
"fields": {
|
|
17
|
+
"stepType": "stepType",
|
|
18
|
+
"annotations": "annotations",
|
|
19
|
+
"connectionName": "connectionName",
|
|
20
|
+
"resultAttribute": "resultAttribute"
|
|
21
|
+
},
|
|
22
|
+
"branches": [
|
|
23
|
+
{
|
|
24
|
+
"id": "listed",
|
|
25
|
+
"field": "listed",
|
|
26
|
+
"optional": false
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "providerRejected",
|
|
30
|
+
"field": "providerRejected",
|
|
31
|
+
"optional": true
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"id": "invalidResponse",
|
|
35
|
+
"field": "invalidResponse",
|
|
36
|
+
"optional": true
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": "defect",
|
|
40
|
+
"field": "defect",
|
|
41
|
+
"optional": true
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"inputSchema": {
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"description": "ListContactsInput selects a bounded Insightly contact page.",
|
|
47
|
+
"properties": {
|
|
48
|
+
"skip": {
|
|
49
|
+
"description": "Skip is the nonnegative number of contacts to omit before this page.",
|
|
50
|
+
"type": "integer"
|
|
51
|
+
},
|
|
52
|
+
"top": {
|
|
53
|
+
"description": "Top is the maximum page size, from 1 to 500 records.",
|
|
54
|
+
"type": "integer"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"required": [
|
|
58
|
+
"skip",
|
|
59
|
+
"top"
|
|
60
|
+
],
|
|
61
|
+
"type": "object"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "newGetContactStep",
|
|
66
|
+
"kind": "query",
|
|
67
|
+
"operationId": "getContact",
|
|
68
|
+
"inputType": "GetContactInput",
|
|
69
|
+
"description": "Read a contact's ID, names, and email address by ID.",
|
|
70
|
+
"fields": {
|
|
71
|
+
"stepType": "stepType",
|
|
72
|
+
"annotations": "annotations",
|
|
73
|
+
"connectionName": "connectionName",
|
|
74
|
+
"resultAttribute": "resultAttribute"
|
|
75
|
+
},
|
|
76
|
+
"branches": [
|
|
77
|
+
{
|
|
78
|
+
"id": "found",
|
|
79
|
+
"field": "found",
|
|
80
|
+
"optional": false
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"id": "notFound",
|
|
84
|
+
"field": "notFound",
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"id": "providerRejected",
|
|
89
|
+
"field": "providerRejected",
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "invalidResponse",
|
|
94
|
+
"field": "invalidResponse",
|
|
95
|
+
"optional": true
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"id": "defect",
|
|
99
|
+
"field": "defect",
|
|
100
|
+
"optional": true
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"inputSchema": {
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"description": "GetContactInput selects one Insightly contact by its numeric ID.",
|
|
106
|
+
"properties": {
|
|
107
|
+
"contactId": {
|
|
108
|
+
"description": "ContactID is the decimal ID shown in Insightly and returned by its API.",
|
|
109
|
+
"type": "string"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
"required": [
|
|
113
|
+
"contactId"
|
|
114
|
+
],
|
|
115
|
+
"type": "object"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// src/generated/models.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { SecretString } from "@superdurable/connectors-sdk";
|
|
4
|
+
var ConfigSchema = z.object({
|
|
5
|
+
"baseUrl": z.string().optional(),
|
|
6
|
+
"timeoutMs": z.number().int().optional(),
|
|
7
|
+
"maxResponseBytes": z.number().int().optional()
|
|
8
|
+
});
|
|
9
|
+
function decodeCredentials(value) {
|
|
10
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new TypeError("credential fields are invalid");
|
|
11
|
+
const fields = value;
|
|
12
|
+
const allowed = /* @__PURE__ */ new Set(["api_key"]);
|
|
13
|
+
if (Object.keys(fields).some((key) => !allowed.has(key))) throw new TypeError("stored credential does not match the connector credential fields");
|
|
14
|
+
if (fields["api_key"] != null && !(typeof fields["api_key"] === "string")) throw new TypeError("credential api_key has an invalid type");
|
|
15
|
+
const credentials = {};
|
|
16
|
+
if (!fields["api_key"]) throw new TypeError("credential api_key is required");
|
|
17
|
+
credentials.apiKey = new SecretString(fields["api_key"] ?? "");
|
|
18
|
+
return credentials;
|
|
19
|
+
}
|
|
20
|
+
var ListContactsInputSchema = z.strictObject({
|
|
21
|
+
"top": z.number().int(),
|
|
22
|
+
"skip": z.number().int()
|
|
23
|
+
});
|
|
24
|
+
var ContactMetadataSchema = z.strictObject({
|
|
25
|
+
"id": z.number().int(),
|
|
26
|
+
"firstName": z.string(),
|
|
27
|
+
"lastName": z.string(),
|
|
28
|
+
"emailAddress": z.string()
|
|
29
|
+
});
|
|
30
|
+
var ContactsPageSchema = z.strictObject({
|
|
31
|
+
"contacts": z.array(ContactMetadataSchema).nullable()
|
|
32
|
+
});
|
|
33
|
+
var GetContactInputSchema = z.strictObject({
|
|
34
|
+
"contactId": z.string()
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// src/generated/definitions.ts
|
|
38
|
+
var ConnectorID = "insightly";
|
|
39
|
+
var ProviderID = "insightly";
|
|
40
|
+
var ListContactsBranch = {
|
|
41
|
+
Listed: "listed",
|
|
42
|
+
ProviderRejected: "providerRejected",
|
|
43
|
+
InvalidResponse: "invalidResponse",
|
|
44
|
+
Defect: "defect"
|
|
45
|
+
};
|
|
46
|
+
var ListContactsBranchListed = ListContactsBranch.Listed;
|
|
47
|
+
var ListContactsBranchProviderRejected = ListContactsBranch.ProviderRejected;
|
|
48
|
+
var ListContactsBranchInvalidResponse = ListContactsBranch.InvalidResponse;
|
|
49
|
+
var ListContactsBranchDefect = ListContactsBranch.Defect;
|
|
50
|
+
var ListContactsDefinition = {
|
|
51
|
+
operation: { connectorId: ConnectorID, operationId: "listContacts" },
|
|
52
|
+
branches: [
|
|
53
|
+
{ id: "listed", description: "Insightly returned the requested contact page." },
|
|
54
|
+
{ id: "providerRejected", description: "Insightly conclusively rejected the request.", optional: true },
|
|
55
|
+
{ id: "invalidResponse", description: "Insightly returned malformed or oversized JSON.", optional: true },
|
|
56
|
+
{ id: "defect", description: "Invalid page input or connection credentials.", optional: true }
|
|
57
|
+
],
|
|
58
|
+
stepDefaults: { executeMethodTimeoutMs: 3e4, executeDurability: "async" }
|
|
59
|
+
};
|
|
60
|
+
var ListContactsMetadata = { kind: "query", idempotency: "none", authorization: "required" };
|
|
61
|
+
var GetContactBranch = {
|
|
62
|
+
Found: "found",
|
|
63
|
+
NotFound: "notFound",
|
|
64
|
+
ProviderRejected: "providerRejected",
|
|
65
|
+
InvalidResponse: "invalidResponse",
|
|
66
|
+
Defect: "defect"
|
|
67
|
+
};
|
|
68
|
+
var GetContactBranchFound = GetContactBranch.Found;
|
|
69
|
+
var GetContactBranchNotFound = GetContactBranch.NotFound;
|
|
70
|
+
var GetContactBranchProviderRejected = GetContactBranch.ProviderRejected;
|
|
71
|
+
var GetContactBranchInvalidResponse = GetContactBranch.InvalidResponse;
|
|
72
|
+
var GetContactBranchDefect = GetContactBranch.Defect;
|
|
73
|
+
var GetContactDefinition = {
|
|
74
|
+
operation: { connectorId: ConnectorID, operationId: "getContact" },
|
|
75
|
+
branches: [
|
|
76
|
+
{ id: "found", description: "Insightly returned the selected contact." },
|
|
77
|
+
{ id: "notFound", description: "No contact matches this ID.", optional: true },
|
|
78
|
+
{ id: "providerRejected", description: "Insightly conclusively rejected the request.", optional: true },
|
|
79
|
+
{ id: "invalidResponse", description: "Insightly returned malformed or oversized JSON.", optional: true },
|
|
80
|
+
{ id: "defect", description: "Invalid contact ID or unavailable credentials.", optional: true }
|
|
81
|
+
],
|
|
82
|
+
stepDefaults: { executeMethodTimeoutMs: 3e4, executeDurability: "async" }
|
|
83
|
+
};
|
|
84
|
+
var GetContactMetadata = { kind: "query", idempotency: "none", authorization: "required" };
|
|
85
|
+
|
|
86
|
+
// src/generated/connection.ts
|
|
87
|
+
import { ConnectionRefSchema } from "@superdurable/connectors-sdk";
|
|
88
|
+
var Connection = class {
|
|
89
|
+
/** Stable provider and connection name. */
|
|
90
|
+
reference;
|
|
91
|
+
/** Bound provider operations. */
|
|
92
|
+
operations;
|
|
93
|
+
/** Validate a reference and bind operations. */
|
|
94
|
+
constructor(reference, operations) {
|
|
95
|
+
this.reference = ConnectionRefSchema.parse(reference);
|
|
96
|
+
if (this.reference.provider !== ProviderID) throw new TypeError("connection provider differs from the manifest");
|
|
97
|
+
if (!operations) throw new TypeError("connector operations are required");
|
|
98
|
+
this.operations = operations;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function newConnection(client, reference) {
|
|
102
|
+
if (!client) throw new TypeError("connector client is required");
|
|
103
|
+
return new Connection(reference, client);
|
|
104
|
+
}
|
|
105
|
+
function newConnectionWithOperations(operations, reference) {
|
|
106
|
+
return new Connection(reference, operations);
|
|
107
|
+
}
|
|
108
|
+
function newProjectConnection(project, connectionName, createClient) {
|
|
109
|
+
if (!project) throw new TypeError("loaded project is required");
|
|
110
|
+
const reference = ConnectionRefSchema.parse({ provider: ProviderID, name: connectionName });
|
|
111
|
+
const selected = project.configuration.connections.find((entry) => entry.connectorId === ConnectorID && entry.connectionName === connectionName);
|
|
112
|
+
if (!selected || selected.provider !== ProviderID) throw new TypeError("project connection is not configured");
|
|
113
|
+
return newConnection(createClient(project, connectionName), reference);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
ConfigSchema,
|
|
118
|
+
decodeCredentials,
|
|
119
|
+
ListContactsInputSchema,
|
|
120
|
+
ContactMetadataSchema,
|
|
121
|
+
ContactsPageSchema,
|
|
122
|
+
GetContactInputSchema,
|
|
123
|
+
ConnectorID,
|
|
124
|
+
ProviderID,
|
|
125
|
+
ListContactsBranch,
|
|
126
|
+
ListContactsBranchListed,
|
|
127
|
+
ListContactsBranchProviderRejected,
|
|
128
|
+
ListContactsBranchInvalidResponse,
|
|
129
|
+
ListContactsBranchDefect,
|
|
130
|
+
ListContactsDefinition,
|
|
131
|
+
ListContactsMetadata,
|
|
132
|
+
GetContactBranch,
|
|
133
|
+
GetContactBranchFound,
|
|
134
|
+
GetContactBranchNotFound,
|
|
135
|
+
GetContactBranchProviderRejected,
|
|
136
|
+
GetContactBranchInvalidResponse,
|
|
137
|
+
GetContactBranchDefect,
|
|
138
|
+
GetContactDefinition,
|
|
139
|
+
GetContactMetadata,
|
|
140
|
+
Connection,
|
|
141
|
+
newConnection,
|
|
142
|
+
newConnectionWithOperations,
|
|
143
|
+
newProjectConnection
|
|
144
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { CredentialProvider, SecretString, Query, ConnectionRef } from '@superdurable/connectors-sdk';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
/** Connector configuration; omitted fields use manifest defaults. */
|
|
5
|
+
interface Config {
|
|
6
|
+
/** HTTPS Insightly CRM API v3.1 root. In Insightly CRM at https://login.insightly.com/, open the top-right user icon > User Settings > User Settings > API and copy your API URL including its pod; blank uses the displayed na1 default only for na1 accounts. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** Maximum duration of one API request as a positive Go duration such as 15s or 1m. Blank uses the displayed default; increase for slow responses. Runtime value in milliseconds. */
|
|
9
|
+
timeoutMs?: number;
|
|
10
|
+
/** Positive maximum complete API response in bytes. Blank uses the displayed default; increase for large contact pages. Oversized responses select invalidResponse. */
|
|
11
|
+
maxResponseBytes?: number;
|
|
12
|
+
}
|
|
13
|
+
/** Runtime schema for manifest configuration. */
|
|
14
|
+
declare const ConfigSchema: z.ZodObject<{
|
|
15
|
+
baseUrl: z.ZodOptional<z.ZodString>;
|
|
16
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
17
|
+
maxResponseBytes: z.ZodOptional<z.ZodNumber>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
/** Private connector credentials resolved by the host. */
|
|
20
|
+
interface Credentials {
|
|
21
|
+
/** Secret Insightly CRM API key. At https://login.insightly.com/, open the top-right user icon > User Settings > User Settings > API and copy the full key; blank makes calls select defect. Generate New API Key there to revoke the previous key. */
|
|
22
|
+
apiKey: SecretString;
|
|
23
|
+
}
|
|
24
|
+
/** Decode private credential fields for the connector. */
|
|
25
|
+
declare function decodeCredentials(value: unknown): Credentials;
|
|
26
|
+
/** Host-owned source of private connector credentials. */
|
|
27
|
+
type CredentialSource = CredentialProvider<Credentials>;
|
|
28
|
+
/** ListContactsInput selects a bounded Insightly contact page. */
|
|
29
|
+
interface ListContactsInput {
|
|
30
|
+
/** Top is the maximum page size, from 1 to 500 records. */
|
|
31
|
+
"top": number;
|
|
32
|
+
/** Skip is the nonnegative number of contacts to omit before this page. */
|
|
33
|
+
"skip": number;
|
|
34
|
+
}
|
|
35
|
+
/** ContactMetadata contains public contact identity without custom fields or links. */
|
|
36
|
+
interface ContactMetadata {
|
|
37
|
+
/** ID is the stable Insightly contact identifier. */
|
|
38
|
+
"id": number;
|
|
39
|
+
/** FirstName is the contact's given name when available. */
|
|
40
|
+
"firstName": string;
|
|
41
|
+
/** LastName is the contact's family name when available. */
|
|
42
|
+
"lastName": string;
|
|
43
|
+
/** EmailAddress is the contact's primary email address when available. */
|
|
44
|
+
"emailAddress": string;
|
|
45
|
+
}
|
|
46
|
+
/** ContactsPage contains the requested contact page. */
|
|
47
|
+
interface ContactsPage {
|
|
48
|
+
/** Contacts contains only contact identity fields. */
|
|
49
|
+
"contacts": Array<ContactMetadata> | null;
|
|
50
|
+
}
|
|
51
|
+
/** GetContactInput selects one Insightly contact by its numeric ID. */
|
|
52
|
+
interface GetContactInput {
|
|
53
|
+
/** ContactID is the decimal ID shown in Insightly and returned by its API. */
|
|
54
|
+
"contactId": string;
|
|
55
|
+
}
|
|
56
|
+
/** Runtime validator for ListContactsInput. */
|
|
57
|
+
declare const ListContactsInputSchema: z.ZodObject<{
|
|
58
|
+
top: z.ZodNumber;
|
|
59
|
+
skip: z.ZodNumber;
|
|
60
|
+
}, z.core.$strict>;
|
|
61
|
+
/** Runtime validator for ContactMetadata. */
|
|
62
|
+
declare const ContactMetadataSchema: z.ZodObject<{
|
|
63
|
+
id: z.ZodNumber;
|
|
64
|
+
firstName: z.ZodString;
|
|
65
|
+
lastName: z.ZodString;
|
|
66
|
+
emailAddress: z.ZodString;
|
|
67
|
+
}, z.core.$strict>;
|
|
68
|
+
/** Runtime validator for ContactsPage. */
|
|
69
|
+
declare const ContactsPageSchema: z.ZodObject<{
|
|
70
|
+
contacts: z.ZodNullable<z.ZodArray<z.ZodObject<{
|
|
71
|
+
id: z.ZodNumber;
|
|
72
|
+
firstName: z.ZodString;
|
|
73
|
+
lastName: z.ZodString;
|
|
74
|
+
emailAddress: z.ZodString;
|
|
75
|
+
}, z.core.$strict>>>;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
/** Runtime validator for GetContactInput. */
|
|
78
|
+
declare const GetContactInputSchema: z.ZodObject<{
|
|
79
|
+
contactId: z.ZodString;
|
|
80
|
+
}, z.core.$strict>;
|
|
81
|
+
|
|
82
|
+
/** Typed operations supplied by a client or mock. */
|
|
83
|
+
interface Operations {
|
|
84
|
+
/** Invoke listContacts through this connection. */
|
|
85
|
+
listContacts(): Query<ListContactsInput, ContactsPage>;
|
|
86
|
+
/** Invoke getContact through this connection. */
|
|
87
|
+
getContact(): Query<GetContactInput, ContactMetadata>;
|
|
88
|
+
}
|
|
89
|
+
/** Named connector connection kept outside Flow state. */
|
|
90
|
+
declare class Connection<BoundOperations = Operations> {
|
|
91
|
+
/** Stable provider and connection name. */
|
|
92
|
+
readonly reference: ConnectionRef;
|
|
93
|
+
/** Bound provider operations. */
|
|
94
|
+
readonly operations: BoundOperations;
|
|
95
|
+
/** Validate a reference and bind operations. */
|
|
96
|
+
constructor(reference: ConnectionRef, operations: BoundOperations);
|
|
97
|
+
}
|
|
98
|
+
/** Bind a client to a named connection. */
|
|
99
|
+
declare function newConnection(client: Operations, reference: ConnectionRef): Connection;
|
|
100
|
+
/** Bind scripted operations to a named connection. */
|
|
101
|
+
declare function newConnectionWithOperations(operations: Operations, reference: ConnectionRef): Connection;
|
|
102
|
+
|
|
103
|
+
export { type ContactMetadata as C, type GetContactInput as G, type ListContactsInput as L, type Operations as O, Connection as a, type ContactsPage as b, type Config as c, type Credentials as d, ConfigSchema as e, ContactMetadataSchema as f, ContactsPageSchema as g, type CredentialSource as h, GetContactInputSchema as i, ListContactsInputSchema as j, decodeCredentials as k, newConnectionWithOperations as l, newConnection as n };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { G as GetContactInput, C as ContactMetadata, a as Connection, L as ListContactsInput, b as ContactsPage, O as Operations, c as Config, d as Credentials } from './connection-lTitKWJP.js';
|
|
2
|
+
export { e as ConfigSchema, f as ContactMetadataSchema, g as ContactsPageSchema, h as CredentialSource, i as GetContactInputSchema, j as ListContactsInputSchema, k as decodeCredentials, n as newConnection, l as newConnectionWithOperations } from './connection-lTitKWJP.js';
|
|
3
|
+
import { QueryDefinition, QueryStepConfig, Query, QueryResult, QueryStep, CredentialProvider, Call, QueryAttempt, Failure } from '@superdurable/connectors-sdk';
|
|
4
|
+
import { StepTarget } from '@superdurable/dex';
|
|
5
|
+
import { LoadedProject } from '@superdurable/connectors-sdk/projectconfig';
|
|
6
|
+
import { ProviderRequest, ProviderResponse } from '@superdurable/connectors-sdk/providerhttp';
|
|
7
|
+
import 'zod';
|
|
8
|
+
|
|
9
|
+
/** Stable connector identity. */
|
|
10
|
+
declare const ConnectorID = "insightly";
|
|
11
|
+
/** Provider identity for named connections. */
|
|
12
|
+
declare const ProviderID = "insightly";
|
|
13
|
+
/** Branch IDs for listContacts. */
|
|
14
|
+
declare const ListContactsBranch: {
|
|
15
|
+
readonly Listed: "listed";
|
|
16
|
+
readonly ProviderRejected: "providerRejected";
|
|
17
|
+
readonly InvalidResponse: "invalidResponse";
|
|
18
|
+
readonly Defect: "defect";
|
|
19
|
+
};
|
|
20
|
+
/** Insightly returned the requested contact page. */
|
|
21
|
+
declare const ListContactsBranchListed: "listed";
|
|
22
|
+
/** Insightly conclusively rejected the request. */
|
|
23
|
+
declare const ListContactsBranchProviderRejected: "providerRejected";
|
|
24
|
+
/** Insightly returned malformed or oversized JSON. */
|
|
25
|
+
declare const ListContactsBranchInvalidResponse: "invalidResponse";
|
|
26
|
+
/** Invalid page input or connection credentials. */
|
|
27
|
+
declare const ListContactsBranchDefect: "defect";
|
|
28
|
+
/** Manifest definition for listContacts. */
|
|
29
|
+
declare const ListContactsDefinition: QueryDefinition;
|
|
30
|
+
/** Manifest metadata for listContacts. */
|
|
31
|
+
declare const ListContactsMetadata: {
|
|
32
|
+
readonly kind: "query";
|
|
33
|
+
readonly idempotency: "none";
|
|
34
|
+
readonly authorization: "required";
|
|
35
|
+
};
|
|
36
|
+
/** Branch IDs for getContact. */
|
|
37
|
+
declare const GetContactBranch: {
|
|
38
|
+
readonly Found: "found";
|
|
39
|
+
readonly NotFound: "notFound";
|
|
40
|
+
readonly ProviderRejected: "providerRejected";
|
|
41
|
+
readonly InvalidResponse: "invalidResponse";
|
|
42
|
+
readonly Defect: "defect";
|
|
43
|
+
};
|
|
44
|
+
/** Insightly returned the selected contact. */
|
|
45
|
+
declare const GetContactBranchFound: "found";
|
|
46
|
+
/** No contact matches this ID. */
|
|
47
|
+
declare const GetContactBranchNotFound: "notFound";
|
|
48
|
+
/** Insightly conclusively rejected the request. */
|
|
49
|
+
declare const GetContactBranchProviderRejected: "providerRejected";
|
|
50
|
+
/** Insightly returned malformed or oversized JSON. */
|
|
51
|
+
declare const GetContactBranchInvalidResponse: "invalidResponse";
|
|
52
|
+
/** Invalid contact ID or unavailable credentials. */
|
|
53
|
+
declare const GetContactBranchDefect: "defect";
|
|
54
|
+
/** Manifest definition for getContact. */
|
|
55
|
+
declare const GetContactDefinition: QueryDefinition;
|
|
56
|
+
/** Manifest metadata for getContact. */
|
|
57
|
+
declare const GetContactMetadata: {
|
|
58
|
+
readonly kind: "query";
|
|
59
|
+
readonly idempotency: "none";
|
|
60
|
+
readonly authorization: "required";
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/** Configuration for the listContacts connector Step. */
|
|
64
|
+
interface ListContactsStepConfig<StepInput, OperationInput = ListContactsInput, OperationOutput = ContactsPage> extends Omit<QueryStepConfig<StepInput, OperationInput, OperationOutput>, 'operation' | 'connection' | 'branches'> {
|
|
65
|
+
/** Named connection supplying this operation. */
|
|
66
|
+
connection: Connection<{
|
|
67
|
+
listContacts(): Query<OperationInput, OperationOutput>;
|
|
68
|
+
}>;
|
|
69
|
+
/** Name matching the bound connection. */
|
|
70
|
+
connectionName: string;
|
|
71
|
+
/** Insightly returned the requested contact page. */
|
|
72
|
+
listed: StepTarget<QueryResult<OperationOutput>>;
|
|
73
|
+
/** Insightly conclusively rejected the request. */
|
|
74
|
+
providerRejected?: StepTarget<QueryResult<OperationOutput>>;
|
|
75
|
+
/** Insightly returned malformed or oversized JSON. */
|
|
76
|
+
invalidResponse?: StepTarget<QueryResult<OperationOutput>>;
|
|
77
|
+
/** Invalid page input or connection credentials. */
|
|
78
|
+
defect?: StepTarget<QueryResult<OperationOutput>>;
|
|
79
|
+
}
|
|
80
|
+
/** Build the listContacts Step from a named connection. */
|
|
81
|
+
declare function newListContactsStep<StepInput, OperationInput = ListContactsInput, OperationOutput = ContactsPage>(configuration: ListContactsStepConfig<StepInput, OperationInput, OperationOutput>): QueryStep<StepInput, OperationInput, OperationOutput>;
|
|
82
|
+
/** Configuration for the getContact connector Step. */
|
|
83
|
+
interface GetContactStepConfig<StepInput, OperationInput = GetContactInput, OperationOutput = ContactMetadata> extends Omit<QueryStepConfig<StepInput, OperationInput, OperationOutput>, 'operation' | 'connection' | 'branches'> {
|
|
84
|
+
/** Named connection supplying this operation. */
|
|
85
|
+
connection: Connection<{
|
|
86
|
+
getContact(): Query<OperationInput, OperationOutput>;
|
|
87
|
+
}>;
|
|
88
|
+
/** Name matching the bound connection. */
|
|
89
|
+
connectionName: string;
|
|
90
|
+
/** Insightly returned the selected contact. */
|
|
91
|
+
found: StepTarget<QueryResult<OperationOutput>>;
|
|
92
|
+
/** No contact matches this ID. */
|
|
93
|
+
notFound?: StepTarget<QueryResult<OperationOutput>>;
|
|
94
|
+
/** Insightly conclusively rejected the request. */
|
|
95
|
+
providerRejected?: StepTarget<QueryResult<OperationOutput>>;
|
|
96
|
+
/** Insightly returned malformed or oversized JSON. */
|
|
97
|
+
invalidResponse?: StepTarget<QueryResult<OperationOutput>>;
|
|
98
|
+
/** Invalid contact ID or unavailable credentials. */
|
|
99
|
+
defect?: StepTarget<QueryResult<OperationOutput>>;
|
|
100
|
+
}
|
|
101
|
+
/** Build the getContact Step from a named connection. */
|
|
102
|
+
declare function newGetContactStep<StepInput, OperationInput = GetContactInput, OperationOutput = ContactMetadata>(configuration: GetContactStepConfig<StepInput, OperationInput, OperationOutput>): QueryStep<StepInput, OperationInput, OperationOutput>;
|
|
103
|
+
|
|
104
|
+
/** Open one named Insightly connection from Dex project configuration. */
|
|
105
|
+
declare function newProjectConnection(project: LoadedProject, connectionName: string): Connection<Operations>;
|
|
106
|
+
|
|
107
|
+
/** Optional transport dependency for provider tests and local Dex examples. */
|
|
108
|
+
interface InsightlyClientOptions {
|
|
109
|
+
/** One bounded provider request sender. */ send?: (request: ProviderRequest) => Promise<ProviderResponse>;
|
|
110
|
+
}
|
|
111
|
+
interface ProviderOutcome {
|
|
112
|
+
body?: Uint8Array;
|
|
113
|
+
branch?: string;
|
|
114
|
+
failure?: Failure;
|
|
115
|
+
retry?: boolean;
|
|
116
|
+
retryAfterMs?: number;
|
|
117
|
+
}
|
|
118
|
+
/** Authenticated Insightly CRM API v3.1 client with call-time credential resolution. */
|
|
119
|
+
declare class InsightlyClient {
|
|
120
|
+
private readonly credentials;
|
|
121
|
+
private readonly baseUrl;
|
|
122
|
+
private readonly timeoutMs;
|
|
123
|
+
private readonly maxResponseBytes;
|
|
124
|
+
private readonly sendRequest;
|
|
125
|
+
/** Validate connection configuration without contacting Insightly. */
|
|
126
|
+
constructor(config: Config, credentials: CredentialProvider<Credentials>, options?: InsightlyClientOptions);
|
|
127
|
+
/** Bind the contact page Query. */ listContacts(): ListContactsOperation;
|
|
128
|
+
/** Bind the contact metadata Query. */ getContact(): GetContactOperation;
|
|
129
|
+
/** Send one read and classify transport, HTTP, and response-size outcomes. */
|
|
130
|
+
exchange(call: Call, operation: string, path: string, hasNotFound: boolean): Promise<ProviderOutcome>;
|
|
131
|
+
}
|
|
132
|
+
/** List Insightly contact IDs, names, and email addresses. */
|
|
133
|
+
declare class ListContactsOperation implements Query<ListContactsInput, ContactsPage> {
|
|
134
|
+
private readonly client;
|
|
135
|
+
/** Bind the provider client. */ constructor(client: InsightlyClient);
|
|
136
|
+
/** Return the manifest-generated operation definition. */ definition(): QueryDefinition;
|
|
137
|
+
/** Read a bounded contact page. */
|
|
138
|
+
invoke(call: Call, input: ListContactsInput): Promise<QueryAttempt<ContactsPage>>;
|
|
139
|
+
}
|
|
140
|
+
/** Read one Insightly contact's identity without custom fields. */
|
|
141
|
+
declare class GetContactOperation implements Query<GetContactInput, ContactMetadata> {
|
|
142
|
+
private readonly client;
|
|
143
|
+
/** Bind the provider client. */ constructor(client: InsightlyClient);
|
|
144
|
+
/** Return the manifest-generated operation definition. */ definition(): QueryDefinition;
|
|
145
|
+
/** Validate the contact ID and read its metadata. */
|
|
146
|
+
invoke(call: Call, input: GetContactInput): Promise<QueryAttempt<ContactMetadata>>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export { Config, Connection, ConnectorID, ContactMetadata, ContactsPage, Credentials, GetContactBranch, GetContactBranchDefect, GetContactBranchFound, GetContactBranchInvalidResponse, GetContactBranchNotFound, GetContactBranchProviderRejected, GetContactDefinition, GetContactInput, GetContactMetadata, GetContactOperation, type GetContactStepConfig, InsightlyClient, type InsightlyClientOptions, ListContactsBranch, ListContactsBranchDefect, ListContactsBranchInvalidResponse, ListContactsBranchListed, ListContactsBranchProviderRejected, ListContactsDefinition, ListContactsInput, ListContactsMetadata, ListContactsOperation, type ListContactsStepConfig, ProviderID, newGetContactStep, newListContactsStep, newProjectConnection };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConfigSchema,
|
|
3
|
+
Connection,
|
|
4
|
+
ConnectorID,
|
|
5
|
+
ContactMetadataSchema,
|
|
6
|
+
ContactsPageSchema,
|
|
7
|
+
GetContactBranch,
|
|
8
|
+
GetContactBranchDefect,
|
|
9
|
+
GetContactBranchFound,
|
|
10
|
+
GetContactBranchInvalidResponse,
|
|
11
|
+
GetContactBranchNotFound,
|
|
12
|
+
GetContactBranchProviderRejected,
|
|
13
|
+
GetContactDefinition,
|
|
14
|
+
GetContactInputSchema,
|
|
15
|
+
GetContactMetadata,
|
|
16
|
+
ListContactsBranch,
|
|
17
|
+
ListContactsBranchDefect,
|
|
18
|
+
ListContactsBranchInvalidResponse,
|
|
19
|
+
ListContactsBranchListed,
|
|
20
|
+
ListContactsBranchProviderRejected,
|
|
21
|
+
ListContactsDefinition,
|
|
22
|
+
ListContactsInputSchema,
|
|
23
|
+
ListContactsMetadata,
|
|
24
|
+
ProviderID,
|
|
25
|
+
decodeCredentials,
|
|
26
|
+
newConnection,
|
|
27
|
+
newConnectionWithOperations,
|
|
28
|
+
newProjectConnection
|
|
29
|
+
} from "./chunk-PTFX7RNO.js";
|
|
30
|
+
|
|
31
|
+
// src/generated/steps.ts
|
|
32
|
+
import { goToBranch, newQueryStep } from "@superdurable/connectors-sdk";
|
|
33
|
+
function newListContactsStep(configuration) {
|
|
34
|
+
const { connection, connectionName, listed, providerRejected, invalidResponse, defect, ...stepConfiguration } = configuration;
|
|
35
|
+
if (!connection || connection.reference.name !== connectionName) throw new TypeError("Step connection name differs from the bound connection");
|
|
36
|
+
return newQueryStep({ ...stepConfiguration, operation: connection.operations.listContacts(), connection: connection.reference, branches: [
|
|
37
|
+
goToBranch("listed", listed),
|
|
38
|
+
...providerRejected ? [goToBranch("providerRejected", providerRejected)] : [],
|
|
39
|
+
...invalidResponse ? [goToBranch("invalidResponse", invalidResponse)] : [],
|
|
40
|
+
...defect ? [goToBranch("defect", defect)] : []
|
|
41
|
+
] });
|
|
42
|
+
}
|
|
43
|
+
function newGetContactStep(configuration) {
|
|
44
|
+
const { connection, connectionName, found, notFound, providerRejected, invalidResponse, defect, ...stepConfiguration } = configuration;
|
|
45
|
+
if (!connection || connection.reference.name !== connectionName) throw new TypeError("Step connection name differs from the bound connection");
|
|
46
|
+
return newQueryStep({ ...stepConfiguration, operation: connection.operations.getContact(), connection: connection.reference, branches: [
|
|
47
|
+
goToBranch("found", found),
|
|
48
|
+
...notFound ? [goToBranch("notFound", notFound)] : [],
|
|
49
|
+
...providerRejected ? [goToBranch("providerRejected", providerRejected)] : [],
|
|
50
|
+
...invalidResponse ? [goToBranch("invalidResponse", invalidResponse)] : [],
|
|
51
|
+
...defect ? [goToBranch("defect", defect)] : []
|
|
52
|
+
] });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/project.ts
|
|
56
|
+
import { SecretString as SecretString2 } from "@superdurable/connectors-sdk";
|
|
57
|
+
import { z } from "zod";
|
|
58
|
+
|
|
59
|
+
// src/client.ts
|
|
60
|
+
import { SecretString, newQueryBranch, newQueryRetry } from "@superdurable/connectors-sdk";
|
|
61
|
+
import { BodyTooLargeError, isHeaderSafeCredential, parseRetryAfter, sendProviderRequest, validateBaseURL } from "@superdurable/connectors-sdk/providerhttp";
|
|
62
|
+
var emptyPage = () => ({ contacts: [] });
|
|
63
|
+
var emptyContact = () => ({ id: 0, firstName: "", lastName: "", emailAddress: "" });
|
|
64
|
+
var userAgent = "Dex Connector (https://superdurable.io)";
|
|
65
|
+
var InsightlyClient = class {
|
|
66
|
+
/** Validate connection configuration without contacting Insightly. */
|
|
67
|
+
constructor(config, credentials, options = {}) {
|
|
68
|
+
this.credentials = credentials;
|
|
69
|
+
if (!credentials) throw new TypeError("Insightly credential source is required");
|
|
70
|
+
this.baseUrl = validateBaseURL(config.baseUrl || "https://api.na1.insightly.com/v3.1");
|
|
71
|
+
this.timeoutMs = config.timeoutMs || 15e3;
|
|
72
|
+
this.maxResponseBytes = config.maxResponseBytes || 1048576;
|
|
73
|
+
if (!Number.isSafeInteger(this.timeoutMs) || this.timeoutMs <= 0) throw new TypeError("Insightly timeout is invalid");
|
|
74
|
+
if (!Number.isSafeInteger(this.maxResponseBytes) || this.maxResponseBytes <= 0) throw new TypeError("Insightly response limit is invalid");
|
|
75
|
+
this.sendRequest = options.send ?? sendProviderRequest;
|
|
76
|
+
}
|
|
77
|
+
credentials;
|
|
78
|
+
baseUrl;
|
|
79
|
+
timeoutMs;
|
|
80
|
+
maxResponseBytes;
|
|
81
|
+
sendRequest;
|
|
82
|
+
/** Bind the contact page Query. */
|
|
83
|
+
listContacts() {
|
|
84
|
+
return new ListContactsOperation(this);
|
|
85
|
+
}
|
|
86
|
+
/** Bind the contact metadata Query. */
|
|
87
|
+
getContact() {
|
|
88
|
+
return new GetContactOperation(this);
|
|
89
|
+
}
|
|
90
|
+
/** Send one read and classify transport, HTTP, and response-size outcomes. */
|
|
91
|
+
async exchange(call, operation, path, hasNotFound) {
|
|
92
|
+
let apiKey;
|
|
93
|
+
try {
|
|
94
|
+
const resolved = await this.credentials.resolve(call, call.context.cancellationSignal);
|
|
95
|
+
apiKey = resolved.apiKey instanceof SecretString ? resolved.apiKey.reveal() : "";
|
|
96
|
+
} catch {
|
|
97
|
+
return { branch: "defect", failure: failure(operation, "AUTHENTICATION", "connection credentials are unavailable or invalid") };
|
|
98
|
+
}
|
|
99
|
+
if (!isHeaderSafeCredential(apiKey)) return { branch: "defect", failure: failure(operation, "AUTHENTICATION", "connection credentials are unavailable or invalid") };
|
|
100
|
+
const request = {
|
|
101
|
+
url: `${this.baseUrl}${path}`,
|
|
102
|
+
method: "GET",
|
|
103
|
+
headers: { Accept: "application/json", Authorization: `Basic ${Buffer.from(`${apiKey}:`).toString("base64")}`, "User-Agent": userAgent },
|
|
104
|
+
maxResponseBytes: this.maxResponseBytes,
|
|
105
|
+
timeoutMs: this.timeoutMs,
|
|
106
|
+
addressPolicy: this.baseUrl.startsWith("https://") ? "publicHTTPS" : "privateNetworkHTTP",
|
|
107
|
+
signal: call.context.cancellationSignal
|
|
108
|
+
};
|
|
109
|
+
let response;
|
|
110
|
+
try {
|
|
111
|
+
response = await this.sendRequest(request);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (error instanceof BodyTooLargeError && error.status !== void 0) {
|
|
114
|
+
response = { status: error.status, headers: error.headers ?? {}, body: new Uint8Array() };
|
|
115
|
+
if (response.status === 200) return { branch: "invalidResponse", failure: failure(operation, "RESPONSE_TOO_LARGE", "Insightly response exceeded the configured limit") };
|
|
116
|
+
} else return { retry: true, failure: failure(operation, "TRANSPORT", "Insightly request failed") };
|
|
117
|
+
}
|
|
118
|
+
const status = response.status;
|
|
119
|
+
const retryAfter = parseRetryAfter(headerValue(response.headers["retry-after"]));
|
|
120
|
+
if (status === 429) return { retry: true, retryAfterMs: retryAfter, failure: failure(operation, "RATE_LIMIT", "Insightly rate limited the request") };
|
|
121
|
+
if (status === 408 || status >= 500) return { retry: true, retryAfterMs: retryAfter, failure: failure(operation, "AVAILABILITY", "Insightly is temporarily unavailable") };
|
|
122
|
+
if (status === 404 && hasNotFound) return { branch: "notFound", failure: failure(operation, "NOT_FOUND", "Insightly did not find the resource") };
|
|
123
|
+
if (status !== 200) return { branch: "providerRejected", failure: failure(operation, "PROVIDER_REJECTION", `Insightly rejected HTTP ${status}`) };
|
|
124
|
+
if (response.body.length > this.maxResponseBytes) return { branch: "invalidResponse", failure: failure(operation, "RESPONSE_TOO_LARGE", "Insightly response exceeded the configured limit") };
|
|
125
|
+
return { body: response.body };
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var ListContactsOperation = class {
|
|
129
|
+
/** Bind the provider client. */
|
|
130
|
+
constructor(client) {
|
|
131
|
+
this.client = client;
|
|
132
|
+
}
|
|
133
|
+
client;
|
|
134
|
+
/** Return the manifest-generated operation definition. */
|
|
135
|
+
definition() {
|
|
136
|
+
return ListContactsDefinition;
|
|
137
|
+
}
|
|
138
|
+
/** Read a bounded contact page. */
|
|
139
|
+
async invoke(call, input) {
|
|
140
|
+
if (!input || !Number.isSafeInteger(input.top) || input.top < 1 || input.top > 500 || !Number.isSafeInteger(input.skip) || input.skip < 0) return terminal("listContacts", "defect", "VALIDATION", "contact page size or offset is invalid", emptyPage());
|
|
141
|
+
const result = await this.client.exchange(call, "listContacts", `/Contacts?skip=${input.skip}&top=${input.top}`, false);
|
|
142
|
+
if (result.retry) return newQueryRetry(result.failure, result.retryAfterMs);
|
|
143
|
+
if (result.failure) return newQueryBranch(result.branch, emptyPage(), {}, result.failure);
|
|
144
|
+
const document = parseJSON(result.body);
|
|
145
|
+
if (!Array.isArray(document)) return invalid("listContacts", emptyPage());
|
|
146
|
+
const contacts = [];
|
|
147
|
+
for (const item of document) {
|
|
148
|
+
const contact = decodeContact(item);
|
|
149
|
+
if (!contact) return invalid("listContacts", emptyPage());
|
|
150
|
+
contacts.push(contact);
|
|
151
|
+
}
|
|
152
|
+
return newQueryBranch("listed", { contacts });
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var GetContactOperation = class {
|
|
156
|
+
/** Bind the provider client. */
|
|
157
|
+
constructor(client) {
|
|
158
|
+
this.client = client;
|
|
159
|
+
}
|
|
160
|
+
client;
|
|
161
|
+
/** Return the manifest-generated operation definition. */
|
|
162
|
+
definition() {
|
|
163
|
+
return GetContactDefinition;
|
|
164
|
+
}
|
|
165
|
+
/** Validate the contact ID and read its metadata. */
|
|
166
|
+
async invoke(call, input) {
|
|
167
|
+
if (!isValidContactID(input)) return terminal("getContact", "defect", "VALIDATION", "contact ID must be a positive decimal identifier", emptyContact());
|
|
168
|
+
const result = await this.client.exchange(call, "getContact", `/Contacts/${input.contactId}`, true);
|
|
169
|
+
if (result.retry) return newQueryRetry(result.failure, result.retryAfterMs);
|
|
170
|
+
if (result.failure) return newQueryBranch(result.branch, emptyContact(), {}, result.failure);
|
|
171
|
+
const contact = decodeContact(parseJSON(result.body));
|
|
172
|
+
if (!contact) return invalid("getContact", emptyContact());
|
|
173
|
+
return newQueryBranch("found", contact);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
function isValidContactID(input) {
|
|
177
|
+
if (!input || typeof input.contactId !== "string" || !/^[0-9]+$/.test(input.contactId)) return false;
|
|
178
|
+
const significantDigits = input.contactId.replace(/^0+/, "");
|
|
179
|
+
return significantDigits.length > 0 && significantDigits.length <= 19 && BigInt(significantDigits) <= 9223372036854775807n;
|
|
180
|
+
}
|
|
181
|
+
function decodeContact(value) {
|
|
182
|
+
if (!isObject(value) || !isPositiveSafeInteger(value.CONTACT_ID)) return void 0;
|
|
183
|
+
const firstName = optionalString(value.FIRST_NAME);
|
|
184
|
+
const lastName = optionalString(value.LAST_NAME);
|
|
185
|
+
const emailAddress = optionalString(value.EMAIL_ADDRESS);
|
|
186
|
+
if (firstName === void 0 || lastName === void 0 || emailAddress === void 0) return void 0;
|
|
187
|
+
return { id: value.CONTACT_ID, firstName, lastName, emailAddress };
|
|
188
|
+
}
|
|
189
|
+
function optionalString(value) {
|
|
190
|
+
return value === void 0 || value === null ? "" : typeof value === "string" ? value : void 0;
|
|
191
|
+
}
|
|
192
|
+
function isPositiveSafeInteger(value) {
|
|
193
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
194
|
+
}
|
|
195
|
+
function failure(operation, kind, message) {
|
|
196
|
+
return { provider: "insightly", operation, kind, message };
|
|
197
|
+
}
|
|
198
|
+
function terminal(operation, branch, kind, message, empty) {
|
|
199
|
+
return newQueryBranch(branch, empty, {}, failure(operation, kind, message));
|
|
200
|
+
}
|
|
201
|
+
function invalid(operation, empty) {
|
|
202
|
+
return terminal(operation, "invalidResponse", "PROTOCOL", "Insightly returned invalid JSON", empty);
|
|
203
|
+
}
|
|
204
|
+
function isObject(value) {
|
|
205
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
206
|
+
}
|
|
207
|
+
function parseJSON(body) {
|
|
208
|
+
try {
|
|
209
|
+
return JSON.parse(Buffer.from(body).toString("utf8"));
|
|
210
|
+
} catch {
|
|
211
|
+
return void 0;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function headerValue(value) {
|
|
215
|
+
return Array.isArray(value) ? value[0] ?? "" : value ?? "";
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/project.ts
|
|
219
|
+
function newProjectConnection2(project, connectionName) {
|
|
220
|
+
return newProjectConnection(project, connectionName, (loadedProject, name) => {
|
|
221
|
+
const selected = loadedProject.configuration.connections.find((entry) => entry.connectorId === "insightly" && entry.connectionName === name);
|
|
222
|
+
if (!selected) throw new TypeError("Insightly project connection is not configured");
|
|
223
|
+
const fields = z.strictObject({ baseUrl: z.string().optional(), timeout: z.union([z.string(), z.number().int()]).optional(), maxResponseBytes: z.number().int().optional() }).parse(selected.configuration);
|
|
224
|
+
const timeoutMs = typeof fields.timeout === "number" ? Math.ceil(fields.timeout / 1e6) : fields.timeout ? parseDurationMilliseconds(fields.timeout) : void 0;
|
|
225
|
+
const config = {
|
|
226
|
+
...fields.baseUrl ? { baseUrl: fields.baseUrl } : {},
|
|
227
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {},
|
|
228
|
+
...fields.maxResponseBytes !== void 0 ? { maxResponseBytes: fields.maxResponseBytes } : {}
|
|
229
|
+
};
|
|
230
|
+
const credentials = {
|
|
231
|
+
async resolve(_call, signal) {
|
|
232
|
+
const privateFields = await loadedProject.resolveCredential("insightly", name, signal);
|
|
233
|
+
if (typeof privateFields.api_key !== "string") throw new TypeError("Insightly credentials are unavailable");
|
|
234
|
+
return { apiKey: new SecretString2(privateFields.api_key) };
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
return new InsightlyClient(config, credentials);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function parseDurationMilliseconds(value) {
|
|
241
|
+
let remaining = value;
|
|
242
|
+
let milliseconds = 0;
|
|
243
|
+
const units = { ns: 1e-6, us: 1e-3, "\xB5s": 1e-3, ms: 1, s: 1e3, m: 6e4, h: 36e5 };
|
|
244
|
+
while (remaining) {
|
|
245
|
+
const part = /^(\d+(?:\.\d+)?)(ns|us|µs|ms|s|m|h)/.exec(remaining);
|
|
246
|
+
if (!part) throw new TypeError("Insightly timeout must be a positive Go duration");
|
|
247
|
+
milliseconds += Number(part[1]) * (units[part[2] ?? ""] ?? NaN);
|
|
248
|
+
remaining = remaining.slice(part[0].length);
|
|
249
|
+
}
|
|
250
|
+
if (!Number.isSafeInteger(milliseconds) || milliseconds <= 0) throw new TypeError("Insightly timeout must resolve to positive whole milliseconds");
|
|
251
|
+
return milliseconds;
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
ConfigSchema,
|
|
255
|
+
Connection,
|
|
256
|
+
ConnectorID,
|
|
257
|
+
ContactMetadataSchema,
|
|
258
|
+
ContactsPageSchema,
|
|
259
|
+
GetContactBranch,
|
|
260
|
+
GetContactBranchDefect,
|
|
261
|
+
GetContactBranchFound,
|
|
262
|
+
GetContactBranchInvalidResponse,
|
|
263
|
+
GetContactBranchNotFound,
|
|
264
|
+
GetContactBranchProviderRejected,
|
|
265
|
+
GetContactDefinition,
|
|
266
|
+
GetContactInputSchema,
|
|
267
|
+
GetContactMetadata,
|
|
268
|
+
GetContactOperation,
|
|
269
|
+
InsightlyClient,
|
|
270
|
+
ListContactsBranch,
|
|
271
|
+
ListContactsBranchDefect,
|
|
272
|
+
ListContactsBranchInvalidResponse,
|
|
273
|
+
ListContactsBranchListed,
|
|
274
|
+
ListContactsBranchProviderRejected,
|
|
275
|
+
ListContactsDefinition,
|
|
276
|
+
ListContactsInputSchema,
|
|
277
|
+
ListContactsMetadata,
|
|
278
|
+
ListContactsOperation,
|
|
279
|
+
ProviderID,
|
|
280
|
+
decodeCredentials,
|
|
281
|
+
newConnection,
|
|
282
|
+
newConnectionWithOperations,
|
|
283
|
+
newGetContactStep,
|
|
284
|
+
newListContactsStep,
|
|
285
|
+
newProjectConnection2 as newProjectConnection
|
|
286
|
+
};
|
package/dist/mock.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { MockQuery, Case } from '@superdurable/connectors-sdk/connectormock';
|
|
2
|
+
import { Failure } from '@superdurable/connectors-sdk';
|
|
3
|
+
import { O as Operations, a as Connection, L as ListContactsInput, b as ContactsPage, G as GetContactInput, C as ContactMetadata } from './connection-lTitKWJP.js';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/** Go zero value for ListContactsInput in scripted cases. */
|
|
7
|
+
declare const emptyListContactsInput: ListContactsInput;
|
|
8
|
+
/** Go zero value for ContactMetadata in scripted cases. */
|
|
9
|
+
declare const emptyContactMetadata: ContactMetadata;
|
|
10
|
+
/** Go zero value for ContactsPage in scripted cases. */
|
|
11
|
+
declare const emptyContactsPage: ContactsPage;
|
|
12
|
+
/** Go zero value for GetContactInput in scripted cases. */
|
|
13
|
+
declare const emptyGetContactInput: GetContactInput;
|
|
14
|
+
/** Script the listed branch. */
|
|
15
|
+
declare function listContactsListed(value: ContactsPage, failure?: Failure): Case<ContactsPage>;
|
|
16
|
+
/** Script the providerRejected branch. */
|
|
17
|
+
declare function listContactsProviderRejected(value: ContactsPage, failure?: Failure): Case<ContactsPage>;
|
|
18
|
+
/** Script the invalidResponse branch. */
|
|
19
|
+
declare function listContactsInvalidResponse(value: ContactsPage, failure?: Failure): Case<ContactsPage>;
|
|
20
|
+
/** Script the defect branch. */
|
|
21
|
+
declare function listContactsDefect(value: ContactsPage, failure?: Failure): Case<ContactsPage>;
|
|
22
|
+
/** Script a safe retry of listContacts. */
|
|
23
|
+
declare function listContactsRetry(failure: Failure, retryAfterMs?: number): Case<ContactsPage>;
|
|
24
|
+
/** Manifest scenario listedDefault for listContacts. */
|
|
25
|
+
declare function listContactsListedDefault(): Case<ContactsPage>;
|
|
26
|
+
/** Default listContacts scenario. */
|
|
27
|
+
declare function listContactsDefault(): Case<ContactsPage>;
|
|
28
|
+
/** Script the found branch. */
|
|
29
|
+
declare function getContactFound(value: ContactMetadata, failure?: Failure): Case<ContactMetadata>;
|
|
30
|
+
/** Script the notFound branch. */
|
|
31
|
+
declare function getContactNotFound(value: ContactMetadata, failure?: Failure): Case<ContactMetadata>;
|
|
32
|
+
/** Script the providerRejected branch. */
|
|
33
|
+
declare function getContactProviderRejected(value: ContactMetadata, failure?: Failure): Case<ContactMetadata>;
|
|
34
|
+
/** Script the invalidResponse branch. */
|
|
35
|
+
declare function getContactInvalidResponse(value: ContactMetadata, failure?: Failure): Case<ContactMetadata>;
|
|
36
|
+
/** Script the defect branch. */
|
|
37
|
+
declare function getContactDefect(value: ContactMetadata, failure?: Failure): Case<ContactMetadata>;
|
|
38
|
+
/** Script a safe retry of getContact. */
|
|
39
|
+
declare function getContactRetry(failure: Failure, retryAfterMs?: number): Case<ContactMetadata>;
|
|
40
|
+
/** Manifest scenario foundDefault for getContact. */
|
|
41
|
+
declare function getContactFoundDefault(): Case<ContactMetadata>;
|
|
42
|
+
/** Default getContact scenario. */
|
|
43
|
+
declare function getContactDefault(): Case<ContactMetadata>;
|
|
44
|
+
/** Scripted operations and manifest defaults for this connector. */
|
|
45
|
+
declare class GeneratedMock implements Operations {
|
|
46
|
+
#private;
|
|
47
|
+
constructor(connectionName: string);
|
|
48
|
+
connection(): Connection;
|
|
49
|
+
listContacts(): MockQuery<ListContactsInput, ContactsPage>;
|
|
50
|
+
getContact(): MockQuery<GetContactInput, ContactMetadata>;
|
|
51
|
+
assertNoFailures(): void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { GeneratedMock, GeneratedMock as Mock, emptyContactMetadata, emptyContactsPage, emptyGetContactInput, emptyListContactsInput, getContactDefault, getContactDefect, getContactFound, getContactFoundDefault, getContactInvalidResponse, getContactNotFound, getContactProviderRejected, getContactRetry, listContactsDefault, listContactsDefect, listContactsInvalidResponse, listContactsListed, listContactsListedDefault, listContactsProviderRejected, listContactsRetry };
|
package/dist/mock.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ContactMetadataSchema,
|
|
3
|
+
ContactsPageSchema,
|
|
4
|
+
GetContactDefinition,
|
|
5
|
+
GetContactInputSchema,
|
|
6
|
+
ListContactsDefinition,
|
|
7
|
+
ListContactsInputSchema,
|
|
8
|
+
ProviderID,
|
|
9
|
+
newConnectionWithOperations
|
|
10
|
+
} from "./chunk-PTFX7RNO.js";
|
|
11
|
+
|
|
12
|
+
// src/generated/mock.ts
|
|
13
|
+
import { branchCase, retryCase, newQuery } from "@superdurable/connectors-sdk/connectormock";
|
|
14
|
+
var emptyListContactsInput = ListContactsInputSchema.parse({
|
|
15
|
+
"top": 0,
|
|
16
|
+
"skip": 0
|
|
17
|
+
});
|
|
18
|
+
var emptyContactMetadata = ContactMetadataSchema.parse({
|
|
19
|
+
"id": 0,
|
|
20
|
+
"firstName": "",
|
|
21
|
+
"lastName": "",
|
|
22
|
+
"emailAddress": ""
|
|
23
|
+
});
|
|
24
|
+
var emptyContactsPage = ContactsPageSchema.parse({
|
|
25
|
+
"contacts": []
|
|
26
|
+
});
|
|
27
|
+
var emptyGetContactInput = GetContactInputSchema.parse({
|
|
28
|
+
"contactId": ""
|
|
29
|
+
});
|
|
30
|
+
function listContactsListed(value, failure) {
|
|
31
|
+
return branchCase("listed", value, failure);
|
|
32
|
+
}
|
|
33
|
+
function listContactsProviderRejected(value, failure) {
|
|
34
|
+
return branchCase("providerRejected", value, failure);
|
|
35
|
+
}
|
|
36
|
+
function listContactsInvalidResponse(value, failure) {
|
|
37
|
+
return branchCase("invalidResponse", value, failure);
|
|
38
|
+
}
|
|
39
|
+
function listContactsDefect(value, failure) {
|
|
40
|
+
return branchCase("defect", value, failure);
|
|
41
|
+
}
|
|
42
|
+
function listContactsRetry(failure, retryAfterMs = 0) {
|
|
43
|
+
return retryCase(failure, retryAfterMs);
|
|
44
|
+
}
|
|
45
|
+
function listContactsListedDefault() {
|
|
46
|
+
const value = ContactsPageSchema.parse({ "contacts": [{ "emailAddress": "ada@example.com", "firstName": "Ada", "id": 45678, "lastName": "Lovelace" }] });
|
|
47
|
+
return branchCase("listed", value);
|
|
48
|
+
}
|
|
49
|
+
function listContactsDefault() {
|
|
50
|
+
return listContactsListedDefault();
|
|
51
|
+
}
|
|
52
|
+
function getContactFound(value, failure) {
|
|
53
|
+
return branchCase("found", value, failure);
|
|
54
|
+
}
|
|
55
|
+
function getContactNotFound(value, failure) {
|
|
56
|
+
return branchCase("notFound", value, failure);
|
|
57
|
+
}
|
|
58
|
+
function getContactProviderRejected(value, failure) {
|
|
59
|
+
return branchCase("providerRejected", value, failure);
|
|
60
|
+
}
|
|
61
|
+
function getContactInvalidResponse(value, failure) {
|
|
62
|
+
return branchCase("invalidResponse", value, failure);
|
|
63
|
+
}
|
|
64
|
+
function getContactDefect(value, failure) {
|
|
65
|
+
return branchCase("defect", value, failure);
|
|
66
|
+
}
|
|
67
|
+
function getContactRetry(failure, retryAfterMs = 0) {
|
|
68
|
+
return retryCase(failure, retryAfterMs);
|
|
69
|
+
}
|
|
70
|
+
function getContactFoundDefault() {
|
|
71
|
+
const value = ContactMetadataSchema.parse({ "emailAddress": "ada@example.com", "firstName": "Ada", "id": 45678, "lastName": "Lovelace" });
|
|
72
|
+
return branchCase("found", value);
|
|
73
|
+
}
|
|
74
|
+
function getContactDefault() {
|
|
75
|
+
return getContactFoundDefault();
|
|
76
|
+
}
|
|
77
|
+
var GeneratedMock = class {
|
|
78
|
+
#listContacts;
|
|
79
|
+
#getContact;
|
|
80
|
+
#connection;
|
|
81
|
+
constructor(connectionName) {
|
|
82
|
+
this.#listContacts = newQuery(ListContactsDefinition).default(listContactsDefault());
|
|
83
|
+
this.#getContact = newQuery(GetContactDefinition).default(getContactDefault());
|
|
84
|
+
this.#connection = newConnectionWithOperations(this, { provider: ProviderID, name: connectionName });
|
|
85
|
+
}
|
|
86
|
+
connection() {
|
|
87
|
+
return this.#connection;
|
|
88
|
+
}
|
|
89
|
+
listContacts() {
|
|
90
|
+
return this.#listContacts;
|
|
91
|
+
}
|
|
92
|
+
getContact() {
|
|
93
|
+
return this.#getContact;
|
|
94
|
+
}
|
|
95
|
+
assertNoFailures() {
|
|
96
|
+
this.#listContacts.assertNoFailures();
|
|
97
|
+
this.#getContact.assertNoFailures();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
GeneratedMock,
|
|
102
|
+
GeneratedMock as Mock,
|
|
103
|
+
emptyContactMetadata,
|
|
104
|
+
emptyContactsPage,
|
|
105
|
+
emptyGetContactInput,
|
|
106
|
+
emptyListContactsInput,
|
|
107
|
+
getContactDefault,
|
|
108
|
+
getContactDefect,
|
|
109
|
+
getContactFound,
|
|
110
|
+
getContactFoundDefault,
|
|
111
|
+
getContactInvalidResponse,
|
|
112
|
+
getContactNotFound,
|
|
113
|
+
getContactProviderRejected,
|
|
114
|
+
getContactRetry,
|
|
115
|
+
listContactsDefault,
|
|
116
|
+
listContactsDefect,
|
|
117
|
+
listContactsInvalidResponse,
|
|
118
|
+
listContactsListed,
|
|
119
|
+
listContactsListedDefault,
|
|
120
|
+
listContactsProviderRejected,
|
|
121
|
+
listContactsRetry
|
|
122
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superdurable/connector-insightly",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Insightly connector for Dex TypeScript applications",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/superdurable/dex-connectors-library.git",
|
|
9
|
+
"directory": "connectors/insightly/typescript"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": "^22.0.0 || ^24.0.0"
|
|
14
|
+
},
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./mock": {
|
|
23
|
+
"types": "./dist/mock.d.ts",
|
|
24
|
+
"import": "./dist/mock.js"
|
|
25
|
+
},
|
|
26
|
+
"./dex-connector.json": "./dex-connector.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"dex-connector.json"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"prepack": "npm run build",
|
|
36
|
+
"build": "tsup src/index.ts src/mock.ts --format esm --dts --clean",
|
|
37
|
+
"typecheck": "tsc --noEmit",
|
|
38
|
+
"lint": "eslint src test examples",
|
|
39
|
+
"test": "vitest run --exclude test/integration/**",
|
|
40
|
+
"test:integration": "vitest run test/integration/*.integration.test.ts",
|
|
41
|
+
"example:worker": "tsx examples/contact-discovery/worker.ts",
|
|
42
|
+
"example:start": "tsx examples/contact-discovery/start.ts",
|
|
43
|
+
"check": "npm run typecheck && npm run lint && npm test && npm run build"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@superdurable/connectors-sdk": "0.1.1",
|
|
47
|
+
"@superdurable/dex": ">=1.28.0",
|
|
48
|
+
"zod": "^4.1.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@eslint/js": "^9.0.0",
|
|
52
|
+
"@types/node": "^22.0.0",
|
|
53
|
+
"eslint": "^9.0.0",
|
|
54
|
+
"tsup": "^8.0.0",
|
|
55
|
+
"tsx": "^4.23.15",
|
|
56
|
+
"typescript": "^5.9.0",
|
|
57
|
+
"typescript-eslint": "^8.0.0",
|
|
58
|
+
"vitest": "^3.0.0",
|
|
59
|
+
"yaml": "^2.9.1"
|
|
60
|
+
}
|
|
61
|
+
}
|