@neo-edi/sdk 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/README.md +203 -0
- package/dist/index.d.mts +289 -0
- package/dist/index.d.ts +289 -0
- package/dist/index.js +512 -0
- package/dist/index.mjs +480 -0
- package/package.json +34 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var NeoEdiError = class extends Error {
|
|
3
|
+
constructor(message, code, statusCode, details) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.details = details;
|
|
8
|
+
this.name = "NeoEdiError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var NeoEdiApiError = class extends NeoEdiError {
|
|
12
|
+
constructor(message, statusCode, code, details) {
|
|
13
|
+
super(message, code, statusCode, details);
|
|
14
|
+
this.statusCode = statusCode;
|
|
15
|
+
this.name = "NeoEdiApiError";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var NeoEdiNetworkError = class extends NeoEdiError {
|
|
19
|
+
constructor(message, cause) {
|
|
20
|
+
super(message, "NETWORK_ERROR");
|
|
21
|
+
this.cause = cause;
|
|
22
|
+
this.name = "NeoEdiNetworkError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var NeoEdiValidationError = class extends NeoEdiError {
|
|
26
|
+
constructor(message, details) {
|
|
27
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
28
|
+
this.name = "NeoEdiValidationError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// src/http.ts
|
|
33
|
+
var HttpClient = class {
|
|
34
|
+
constructor(config) {
|
|
35
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
36
|
+
this.apiKey = config.apiKey;
|
|
37
|
+
this.timeout = config.timeout ?? 3e4;
|
|
38
|
+
this.defaultHeaders = {
|
|
39
|
+
"Content-Type": "application/json",
|
|
40
|
+
"X-SDK-Version": "0.1.0",
|
|
41
|
+
...config.headers
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
async request(options) {
|
|
45
|
+
const url = this.buildUrl(options.path, options.query);
|
|
46
|
+
const headers = {
|
|
47
|
+
...this.defaultHeaders,
|
|
48
|
+
"X-API-Key": this.apiKey,
|
|
49
|
+
...options.headers
|
|
50
|
+
};
|
|
51
|
+
const controller = new AbortController();
|
|
52
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
53
|
+
try {
|
|
54
|
+
let body;
|
|
55
|
+
let finalHeaders = headers;
|
|
56
|
+
if (options.body instanceof FormData) {
|
|
57
|
+
body = options.body;
|
|
58
|
+
const { "Content-Type": _, ...restHeaders } = finalHeaders;
|
|
59
|
+
finalHeaders = restHeaders;
|
|
60
|
+
} else if (options.body) {
|
|
61
|
+
body = JSON.stringify(options.body);
|
|
62
|
+
}
|
|
63
|
+
const response = await fetch(url, {
|
|
64
|
+
method: options.method,
|
|
65
|
+
headers: finalHeaders,
|
|
66
|
+
body,
|
|
67
|
+
signal: controller.signal
|
|
68
|
+
});
|
|
69
|
+
clearTimeout(timeoutId);
|
|
70
|
+
const data = await this.parseResponse(response);
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new NeoEdiApiError(
|
|
73
|
+
data.error || `Request failed with status ${response.status}`,
|
|
74
|
+
response.status,
|
|
75
|
+
data.code,
|
|
76
|
+
data.details
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return data;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
clearTimeout(timeoutId);
|
|
82
|
+
if (error instanceof NeoEdiApiError) {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (error instanceof Error) {
|
|
86
|
+
if (error.name === "AbortError") {
|
|
87
|
+
throw new NeoEdiNetworkError("Request timeout", error);
|
|
88
|
+
}
|
|
89
|
+
throw new NeoEdiNetworkError(error.message, error);
|
|
90
|
+
}
|
|
91
|
+
throw new NeoEdiNetworkError("Unknown error occurred");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
buildUrl(path, query) {
|
|
95
|
+
const url = new URL(path, this.baseUrl);
|
|
96
|
+
if (query) {
|
|
97
|
+
Object.entries(query).forEach(([key, value]) => {
|
|
98
|
+
if (value !== void 0) {
|
|
99
|
+
url.searchParams.append(key, String(value));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return url.toString();
|
|
104
|
+
}
|
|
105
|
+
async parseResponse(response) {
|
|
106
|
+
const contentType = response.headers.get("content-type");
|
|
107
|
+
if (contentType?.includes("application/json")) {
|
|
108
|
+
try {
|
|
109
|
+
return await response.json();
|
|
110
|
+
} catch {
|
|
111
|
+
return { error: "Invalid JSON response" };
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const text = await response.text();
|
|
115
|
+
return { data: text };
|
|
116
|
+
}
|
|
117
|
+
// Convenience methods
|
|
118
|
+
get(path, query) {
|
|
119
|
+
return this.request({ method: "GET", path, query });
|
|
120
|
+
}
|
|
121
|
+
post(path, body) {
|
|
122
|
+
return this.request({ method: "POST", path, body });
|
|
123
|
+
}
|
|
124
|
+
patch(path, body) {
|
|
125
|
+
return this.request({ method: "PATCH", path, body });
|
|
126
|
+
}
|
|
127
|
+
put(path, body) {
|
|
128
|
+
return this.request({ method: "PUT", path, body });
|
|
129
|
+
}
|
|
130
|
+
delete(path) {
|
|
131
|
+
return this.request({ method: "DELETE", path });
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// src/resources/customers.ts
|
|
136
|
+
var CustomersResource = class {
|
|
137
|
+
constructor(http) {
|
|
138
|
+
this.http = http;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* List all customers
|
|
142
|
+
*/
|
|
143
|
+
async list(params) {
|
|
144
|
+
const response = await this.http.get(
|
|
145
|
+
"/api/v1/customers",
|
|
146
|
+
params
|
|
147
|
+
);
|
|
148
|
+
if (!response.success) {
|
|
149
|
+
throw new Error(response.error);
|
|
150
|
+
}
|
|
151
|
+
return response.data;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Get a customer by ID
|
|
155
|
+
*/
|
|
156
|
+
async get(customerId) {
|
|
157
|
+
const response = await this.http.get(
|
|
158
|
+
`/api/v1/customers/${encodeURIComponent(customerId)}`
|
|
159
|
+
);
|
|
160
|
+
if (!response.success) {
|
|
161
|
+
throw new Error(response.error);
|
|
162
|
+
}
|
|
163
|
+
return response.data;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create a new customer
|
|
167
|
+
*/
|
|
168
|
+
async create(data) {
|
|
169
|
+
const response = await this.http.post(
|
|
170
|
+
"/api/v1/customers",
|
|
171
|
+
data
|
|
172
|
+
);
|
|
173
|
+
if (!response.success) {
|
|
174
|
+
throw new Error(response.error);
|
|
175
|
+
}
|
|
176
|
+
return response.data;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Update a customer
|
|
180
|
+
*/
|
|
181
|
+
async update(customerId, data) {
|
|
182
|
+
const response = await this.http.patch(
|
|
183
|
+
`/api/v1/customers/${encodeURIComponent(customerId)}`,
|
|
184
|
+
data
|
|
185
|
+
);
|
|
186
|
+
if (!response.success) {
|
|
187
|
+
throw new Error(response.error);
|
|
188
|
+
}
|
|
189
|
+
return response.data;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Delete a customer
|
|
193
|
+
*/
|
|
194
|
+
async delete(customerId) {
|
|
195
|
+
await this.http.delete(
|
|
196
|
+
`/api/v1/customers/${encodeURIComponent(customerId)}`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/resources/partner-customers.ts
|
|
202
|
+
var PartnerCustomersResource = class {
|
|
203
|
+
constructor(http) {
|
|
204
|
+
this.http = http;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Register customer-partner relationships
|
|
208
|
+
* Creates or activates relationships between a customer and multiple trading partners
|
|
209
|
+
*/
|
|
210
|
+
async register(data) {
|
|
211
|
+
const response = await this.http.post(
|
|
212
|
+
"/api/v1/partner-customers",
|
|
213
|
+
data
|
|
214
|
+
);
|
|
215
|
+
if (!response.success) {
|
|
216
|
+
throw new Error(response.error);
|
|
217
|
+
}
|
|
218
|
+
return response.data;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// src/resources/mapping-templates.ts
|
|
223
|
+
var MappingTemplatesResource = class {
|
|
224
|
+
constructor(http) {
|
|
225
|
+
this.http = http;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* List all mapping templates
|
|
229
|
+
*/
|
|
230
|
+
async list(params) {
|
|
231
|
+
const response = await this.http.get(
|
|
232
|
+
"/api/v1/mapping-templates",
|
|
233
|
+
params
|
|
234
|
+
);
|
|
235
|
+
if (!response.success) {
|
|
236
|
+
throw new Error(response.error);
|
|
237
|
+
}
|
|
238
|
+
return response.data;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Get a mapping template by ID
|
|
242
|
+
*/
|
|
243
|
+
async get(templateId) {
|
|
244
|
+
const response = await this.http.get(
|
|
245
|
+
`/api/v1/mapping-templates/${encodeURIComponent(templateId)}`
|
|
246
|
+
);
|
|
247
|
+
if (!response.success) {
|
|
248
|
+
throw new Error(response.error);
|
|
249
|
+
}
|
|
250
|
+
return response.data;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Create a new mapping template
|
|
254
|
+
*/
|
|
255
|
+
async create(data) {
|
|
256
|
+
const response = await this.http.post(
|
|
257
|
+
"/api/v1/mapping-templates",
|
|
258
|
+
data
|
|
259
|
+
);
|
|
260
|
+
if (!response.success) {
|
|
261
|
+
throw new Error(response.error);
|
|
262
|
+
}
|
|
263
|
+
return response.data;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Update a mapping template
|
|
267
|
+
*/
|
|
268
|
+
async update(templateId, data) {
|
|
269
|
+
const response = await this.http.put(
|
|
270
|
+
`/api/v1/mapping-templates/${encodeURIComponent(templateId)}`,
|
|
271
|
+
data
|
|
272
|
+
);
|
|
273
|
+
if (!response.success) {
|
|
274
|
+
throw new Error(response.error);
|
|
275
|
+
}
|
|
276
|
+
return response.data;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Delete a mapping template
|
|
280
|
+
*/
|
|
281
|
+
async delete(templateId) {
|
|
282
|
+
await this.http.delete(
|
|
283
|
+
`/api/v1/mapping-templates/${encodeURIComponent(templateId)}`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/resources/ingest.ts
|
|
289
|
+
var IngestResource = class {
|
|
290
|
+
constructor(http) {
|
|
291
|
+
this.http = http;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Upload a CSV file for ingestion
|
|
295
|
+
*
|
|
296
|
+
* @example Browser
|
|
297
|
+
* ```typescript
|
|
298
|
+
* const input = document.querySelector('input[type="file"]');
|
|
299
|
+
* const file = input.files[0];
|
|
300
|
+
* await client.ingest.uploadCsv(file, options);
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* @example Node.js
|
|
304
|
+
* ```typescript
|
|
305
|
+
* import { readFileSync } from 'fs';
|
|
306
|
+
* const buffer = readFileSync('data.csv');
|
|
307
|
+
* const blob = new Blob([buffer], { type: 'text/csv' });
|
|
308
|
+
* await client.ingest.uploadCsv(blob, options);
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
async uploadCsv(file, options) {
|
|
312
|
+
const formData = new FormData();
|
|
313
|
+
formData.append("file", file, "data.csv");
|
|
314
|
+
formData.append("partnerId", options.partnerId);
|
|
315
|
+
formData.append("mappingConfig", JSON.stringify(options.mappingConfig));
|
|
316
|
+
if (options.transformationConfig) {
|
|
317
|
+
formData.append(
|
|
318
|
+
"transformationConfig",
|
|
319
|
+
JSON.stringify(options.transformationConfig)
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
const response = await this.http.request({
|
|
323
|
+
method: "POST",
|
|
324
|
+
path: "/api/v1/ingest-csv",
|
|
325
|
+
body: formData
|
|
326
|
+
});
|
|
327
|
+
if (!response.success) {
|
|
328
|
+
throw new Error(response.error);
|
|
329
|
+
}
|
|
330
|
+
return response.data;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Perform a dry run of CSV ingestion (validation only)
|
|
334
|
+
*/
|
|
335
|
+
async dryRun(file, options) {
|
|
336
|
+
const formData = new FormData();
|
|
337
|
+
formData.append("file", file, "data.csv");
|
|
338
|
+
formData.append("partnerId", options.partnerId);
|
|
339
|
+
formData.append("mappingConfig", JSON.stringify(options.mappingConfig));
|
|
340
|
+
if (options.transformationConfig) {
|
|
341
|
+
formData.append(
|
|
342
|
+
"transformationConfig",
|
|
343
|
+
JSON.stringify(options.transformationConfig)
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
const response = await this.http.request({
|
|
347
|
+
method: "POST",
|
|
348
|
+
path: "/api/v1/ingest-csv/dry-run",
|
|
349
|
+
body: formData
|
|
350
|
+
});
|
|
351
|
+
if (!response.success) {
|
|
352
|
+
throw new Error(response.error);
|
|
353
|
+
}
|
|
354
|
+
return response.data;
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// src/resources/graphql.ts
|
|
359
|
+
var GraphQLResource = class {
|
|
360
|
+
constructor(http) {
|
|
361
|
+
this.http = http;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Execute a GraphQL query
|
|
365
|
+
*/
|
|
366
|
+
async query(query, variables, options) {
|
|
367
|
+
const response = await this.http.post("/api/v1/graphql", {
|
|
368
|
+
query,
|
|
369
|
+
variables,
|
|
370
|
+
operationName: options?.operationName
|
|
371
|
+
});
|
|
372
|
+
if (response.errors && response.errors.length > 0) {
|
|
373
|
+
const errorMessage = response.errors.map((e) => e.message).join("; ");
|
|
374
|
+
throw new Error(`GraphQL Error: ${errorMessage}`);
|
|
375
|
+
}
|
|
376
|
+
if (!response.data) {
|
|
377
|
+
throw new Error("No data returned from GraphQL query");
|
|
378
|
+
}
|
|
379
|
+
return response.data;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Execute a GraphQL mutation
|
|
383
|
+
*/
|
|
384
|
+
async mutate(mutation, variables, options) {
|
|
385
|
+
return this.query(mutation, variables, options);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
var Queries = {
|
|
389
|
+
/**
|
|
390
|
+
* Get EDI 852 headers with filtering
|
|
391
|
+
*/
|
|
392
|
+
GET_EDI_852_HEADERS: `
|
|
393
|
+
query GetEdi852Headers($filter: Edi852HeaderFilterInput) {
|
|
394
|
+
edi852Headers(filter: $filter) {
|
|
395
|
+
id
|
|
396
|
+
documentId
|
|
397
|
+
reportTypeCode
|
|
398
|
+
reportDate
|
|
399
|
+
vendorPartnerId
|
|
400
|
+
retailerPartnerId
|
|
401
|
+
status
|
|
402
|
+
totalLocations
|
|
403
|
+
totalItems
|
|
404
|
+
createdAt
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
`,
|
|
408
|
+
/**
|
|
409
|
+
* Get a single EDI 852 header by ID with locations
|
|
410
|
+
*/
|
|
411
|
+
GET_EDI_852_HEADER_BY_ID: `
|
|
412
|
+
query GetEdi852HeaderById($id: ID!) {
|
|
413
|
+
edi852Header(id: $id) {
|
|
414
|
+
id
|
|
415
|
+
documentId
|
|
416
|
+
reportTypeCode
|
|
417
|
+
reportDate
|
|
418
|
+
vendorPartnerId
|
|
419
|
+
retailerPartnerId
|
|
420
|
+
status
|
|
421
|
+
totalLocations
|
|
422
|
+
totalItems
|
|
423
|
+
metadata
|
|
424
|
+
createdAt
|
|
425
|
+
locations {
|
|
426
|
+
id
|
|
427
|
+
locationNumber
|
|
428
|
+
locationName
|
|
429
|
+
activityDate
|
|
430
|
+
items {
|
|
431
|
+
id
|
|
432
|
+
upc
|
|
433
|
+
productDescription
|
|
434
|
+
quantityOnHand
|
|
435
|
+
quantitySold
|
|
436
|
+
unitCost
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
`,
|
|
442
|
+
/**
|
|
443
|
+
* Get trading partner execution summaries
|
|
444
|
+
*/
|
|
445
|
+
GET_EXECUTION_SUMMARIES: `
|
|
446
|
+
query GetExecutionSummaries($partnerId: ID!) {
|
|
447
|
+
tradingPartnerExecutionSummaries(partnerId: $partnerId) {
|
|
448
|
+
tradingPartnerId
|
|
449
|
+
customerId
|
|
450
|
+
lastExecutedAt
|
|
451
|
+
documentCount
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
`
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
// src/client.ts
|
|
458
|
+
var NeoEdiClient = class {
|
|
459
|
+
constructor(config) {
|
|
460
|
+
this.http = new HttpClient({
|
|
461
|
+
baseUrl: config.baseUrl,
|
|
462
|
+
apiKey: config.apiKey,
|
|
463
|
+
timeout: config.timeout,
|
|
464
|
+
headers: config.headers
|
|
465
|
+
});
|
|
466
|
+
this.customers = new CustomersResource(this.http);
|
|
467
|
+
this.partnerCustomers = new PartnerCustomersResource(this.http);
|
|
468
|
+
this.mappingTemplates = new MappingTemplatesResource(this.http);
|
|
469
|
+
this.ingest = new IngestResource(this.http);
|
|
470
|
+
this.graphql = new GraphQLResource(this.http);
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
export {
|
|
474
|
+
NeoEdiApiError,
|
|
475
|
+
NeoEdiClient,
|
|
476
|
+
NeoEdiError,
|
|
477
|
+
NeoEdiNetworkError,
|
|
478
|
+
NeoEdiValidationError,
|
|
479
|
+
Queries
|
|
480
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@neo-edi/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for the Neo-EDI platform",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@neo-edi/types": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^20",
|
|
23
|
+
"tsup": "^8.0.0",
|
|
24
|
+
"typescript": "^5.0.0"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@neo-edi/types": "0.1.0"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
31
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
32
|
+
"test": "echo 'No tests yet'"
|
|
33
|
+
}
|
|
34
|
+
}
|