@makegov/tango-node 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.editorconfig +13 -0
  2. package/.eslintrc.cjs +20 -0
  3. package/.prettierrc +8 -0
  4. package/CHANGELOG.md +15 -0
  5. package/LICENSE +21 -0
  6. package/README.md +331 -0
  7. package/dist/client.d.ts +48 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +321 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/config.d.ts +11 -0
  12. package/dist/config.d.ts.map +1 -0
  13. package/dist/config.js +21 -0
  14. package/dist/config.js.map +1 -0
  15. package/dist/errors.d.ts +39 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +78 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/index.d.ts +6 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +6 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/models/Agency.d.ts +8 -0
  24. package/dist/models/Agency.d.ts.map +1 -0
  25. package/dist/models/Agency.js +2 -0
  26. package/dist/models/Agency.js.map +1 -0
  27. package/dist/models/Contract.d.ts +17 -0
  28. package/dist/models/Contract.d.ts.map +1 -0
  29. package/dist/models/Contract.js +2 -0
  30. package/dist/models/Contract.js.map +1 -0
  31. package/dist/models/Department.d.ts +5 -0
  32. package/dist/models/Department.d.ts.map +1 -0
  33. package/dist/models/Department.js +2 -0
  34. package/dist/models/Department.js.map +1 -0
  35. package/dist/models/Entity.d.ts +11 -0
  36. package/dist/models/Entity.d.ts.map +1 -0
  37. package/dist/models/Entity.js +2 -0
  38. package/dist/models/Entity.js.map +1 -0
  39. package/dist/models/Forecast.d.ts +12 -0
  40. package/dist/models/Forecast.d.ts.map +1 -0
  41. package/dist/models/Forecast.js +2 -0
  42. package/dist/models/Forecast.js.map +1 -0
  43. package/dist/models/Grant.d.ts +10 -0
  44. package/dist/models/Grant.d.ts.map +1 -0
  45. package/dist/models/Grant.js +2 -0
  46. package/dist/models/Grant.js.map +1 -0
  47. package/dist/models/Location.d.ts +17 -0
  48. package/dist/models/Location.d.ts.map +1 -0
  49. package/dist/models/Location.js +2 -0
  50. package/dist/models/Location.js.map +1 -0
  51. package/dist/models/Notice.d.ts +9 -0
  52. package/dist/models/Notice.d.ts.map +1 -0
  53. package/dist/models/Notice.js +2 -0
  54. package/dist/models/Notice.js.map +1 -0
  55. package/dist/models/Opportunity.d.ts +11 -0
  56. package/dist/models/Opportunity.d.ts.map +1 -0
  57. package/dist/models/Opportunity.js +2 -0
  58. package/dist/models/Opportunity.js.map +1 -0
  59. package/dist/models/RecipientProfile.d.ts +12 -0
  60. package/dist/models/RecipientProfile.d.ts.map +1 -0
  61. package/dist/models/RecipientProfile.js +2 -0
  62. package/dist/models/RecipientProfile.js.map +1 -0
  63. package/dist/models/index.d.ts +11 -0
  64. package/dist/models/index.d.ts.map +1 -0
  65. package/dist/models/index.js +2 -0
  66. package/dist/models/index.js.map +1 -0
  67. package/dist/shapes/explicitSchemas.d.ts +24 -0
  68. package/dist/shapes/explicitSchemas.d.ts.map +1 -0
  69. package/dist/shapes/explicitSchemas.js +1818 -0
  70. package/dist/shapes/explicitSchemas.js.map +1 -0
  71. package/dist/shapes/factory.d.ts +29 -0
  72. package/dist/shapes/factory.d.ts.map +1 -0
  73. package/dist/shapes/factory.js +93 -0
  74. package/dist/shapes/factory.js.map +1 -0
  75. package/dist/shapes/generator.d.ts +53 -0
  76. package/dist/shapes/generator.d.ts.map +1 -0
  77. package/dist/shapes/generator.js +117 -0
  78. package/dist/shapes/generator.js.map +1 -0
  79. package/dist/shapes/index.d.ts +8 -0
  80. package/dist/shapes/index.d.ts.map +1 -0
  81. package/dist/shapes/index.js +8 -0
  82. package/dist/shapes/index.js.map +1 -0
  83. package/dist/shapes/parser.d.ts +36 -0
  84. package/dist/shapes/parser.d.ts.map +1 -0
  85. package/dist/shapes/parser.js +169 -0
  86. package/dist/shapes/parser.js.map +1 -0
  87. package/dist/shapes/schema.d.ts +30 -0
  88. package/dist/shapes/schema.d.ts.map +1 -0
  89. package/dist/shapes/schema.js +63 -0
  90. package/dist/shapes/schema.js.map +1 -0
  91. package/dist/shapes/schemaTypes.d.ts +42 -0
  92. package/dist/shapes/schemaTypes.d.ts.map +1 -0
  93. package/dist/shapes/schemaTypes.js +2 -0
  94. package/dist/shapes/schemaTypes.js.map +1 -0
  95. package/dist/shapes/types.d.ts +46 -0
  96. package/dist/shapes/types.d.ts.map +1 -0
  97. package/dist/shapes/types.js +25 -0
  98. package/dist/shapes/types.js.map +1 -0
  99. package/dist/types.d.ts +28 -0
  100. package/dist/types.d.ts.map +1 -0
  101. package/dist/types.js +2 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/utils/dates.d.ts +14 -0
  104. package/dist/utils/dates.d.ts.map +1 -0
  105. package/dist/utils/dates.js +25 -0
  106. package/dist/utils/dates.js.map +1 -0
  107. package/dist/utils/http.d.ts +23 -0
  108. package/dist/utils/http.d.ts.map +1 -0
  109. package/dist/utils/http.js +158 -0
  110. package/dist/utils/http.js.map +1 -0
  111. package/dist/utils/number.d.ts +14 -0
  112. package/dist/utils/number.d.ts.map +1 -0
  113. package/dist/utils/number.js +14 -0
  114. package/dist/utils/number.js.map +1 -0
  115. package/dist/utils/unflatten.d.ts +11 -0
  116. package/dist/utils/unflatten.d.ts.map +1 -0
  117. package/dist/utils/unflatten.js +49 -0
  118. package/dist/utils/unflatten.js.map +1 -0
  119. package/docs/API_REFERENCE.md +201 -0
  120. package/docs/DYNAMIC_MODELS.md +127 -0
  121. package/docs/SHAPES.md +94 -0
  122. package/eslint.config.js +37 -0
  123. package/package.json +73 -0
  124. package/tsconfig.json +21 -0
  125. package/vitest.config.ts +18 -0
@@ -0,0 +1,30 @@
1
+ import type { FieldSchema, FieldSchemaMap } from "./schemaTypes.js";
2
+ export interface ModelSchema {
3
+ modelName: string;
4
+ fields: FieldSchemaMap;
5
+ }
6
+ /**
7
+ * Registry of model schemas for validation and type inference.
8
+ *
9
+ * This mirrors tango.shapes.schema.SchemaRegistry in the Python SDK, but
10
+ * is simplified for the Node/TypeScript environment.
11
+ */
12
+ export declare class SchemaRegistry {
13
+ private readonly schemas;
14
+ private explicitRegistered;
15
+ constructor();
16
+ private ensureExplicitSchemas;
17
+ /**
18
+ * Get the schema for a model by name.
19
+ */
20
+ getSchema(modelName: string): ModelSchema;
21
+ /**
22
+ * Get the schema for a specific field on a model.
23
+ */
24
+ getField(modelName: string, fieldName: string): FieldSchema;
25
+ /**
26
+ * List all field names for a model.
27
+ */
28
+ listFieldNames(modelName: string): string[];
29
+ }
30
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/shapes/schema.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGpE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED;;;;;GAKG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,kBAAkB,CAAS;;IAMnC,OAAO,CAAC,qBAAqB;IAW7B;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW;IASzC;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW;IAkB3D;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;CAI5C"}
@@ -0,0 +1,63 @@
1
+ import { ShapeValidationError } from "../errors.js";
2
+ import { EXPLICIT_SCHEMAS } from "./explicitSchemas.js";
3
+ /**
4
+ * Registry of model schemas for validation and type inference.
5
+ *
6
+ * This mirrors tango.shapes.schema.SchemaRegistry in the Python SDK, but
7
+ * is simplified for the Node/TypeScript environment.
8
+ */
9
+ export class SchemaRegistry {
10
+ constructor() {
11
+ this.schemas = new Map();
12
+ this.explicitRegistered = false;
13
+ this.ensureExplicitSchemas();
14
+ }
15
+ ensureExplicitSchemas() {
16
+ if (this.explicitRegistered)
17
+ return;
18
+ const entries = Object.entries(EXPLICIT_SCHEMAS);
19
+ for (const [modelName, fields] of entries) {
20
+ this.schemas.set(modelName, { modelName, fields });
21
+ }
22
+ this.explicitRegistered = true;
23
+ }
24
+ /**
25
+ * Get the schema for a model by name.
26
+ */
27
+ getSchema(modelName) {
28
+ this.ensureExplicitSchemas();
29
+ const schema = this.schemas.get(modelName);
30
+ if (!schema) {
31
+ throw new ShapeValidationError(`Unknown model: ${modelName}`);
32
+ }
33
+ return schema;
34
+ }
35
+ /**
36
+ * Get the schema for a specific field on a model.
37
+ */
38
+ getField(modelName, fieldName) {
39
+ const schema = this.getSchema(modelName);
40
+ const field = schema.fields[fieldName];
41
+ if (!field) {
42
+ const available = Object.keys(schema.fields).sort();
43
+ let msg = `Field "${fieldName}" does not exist on model "${modelName}".`;
44
+ if (available.length) {
45
+ const shown = available.slice(0, 20);
46
+ msg += ` Available fields: ${shown.join(", ")}`;
47
+ if (available.length > shown.length) {
48
+ msg += `, ... (${available.length - shown.length} more)`;
49
+ }
50
+ }
51
+ throw new ShapeValidationError(msg);
52
+ }
53
+ return field;
54
+ }
55
+ /**
56
+ * List all field names for a model.
57
+ */
58
+ listFieldNames(modelName) {
59
+ const schema = this.getSchema(modelName);
60
+ return Object.keys(schema.fields).sort();
61
+ }
62
+ }
63
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/shapes/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAOxD;;;;;GAKG;AACH,MAAM,OAAO,cAAc;IAIzB;QAHiB,YAAO,GAA6B,IAAI,GAAG,EAAE,CAAC;QACvD,uBAAkB,GAAG,KAAK,CAAC;QAGjC,IAAI,CAAC,qBAAqB,EAAE,CAAC;IAC/B,CAAC;IAEO,qBAAqB;QAC3B,IAAI,IAAI,CAAC,kBAAkB;YAAE,OAAO;QAEpC,MAAM,OAAO,GAA+B,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC7E,KAAK,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YAC1C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,SAAS,CAAC,SAAiB;QACzB,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,oBAAoB,CAAC,kBAAkB,SAAS,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACH,QAAQ,CAAC,SAAiB,EAAE,SAAiB;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACpD,IAAI,GAAG,GAAG,UAAU,SAAS,8BAA8B,SAAS,IAAI,CAAC;YACzE,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACrC,GAAG,IAAI,sBAAsB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChD,IAAI,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;oBACpC,GAAG,IAAI,UAAU,SAAS,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,QAAQ,CAAC;gBAC3D,CAAC;YACH,CAAC;YACD,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,SAAiB;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;CACF"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Schema information for a single model field.
3
+ *
4
+ * This mirrors tango.shapes.schema.FieldSchema in the Python SDK.
5
+ */
6
+ export interface FieldSchema {
7
+ /**
8
+ * Field name as exposed in the API payload and shape strings.
9
+ */
10
+ name: string;
11
+ /**
12
+ * Logical type name.
13
+ *
14
+ * For built-in types this will be values like:
15
+ * - "str", "int", "float", "bool", "Decimal"
16
+ * - "date", "datetime", "dict", "Any"
17
+ *
18
+ * For nested models this will be the model name, e.g. "Agency", "Location".
19
+ */
20
+ type: string;
21
+ /**
22
+ * Whether the field is optional (can be null / missing).
23
+ */
24
+ isOptional: boolean;
25
+ /**
26
+ * Whether the field is a list type.
27
+ */
28
+ isList: boolean;
29
+ /**
30
+ * Name of the nested model for nested objects, if any.
31
+ */
32
+ nestedModel?: string | null;
33
+ }
34
+ /**
35
+ * Map of field name -> schema information.
36
+ */
37
+ export type FieldSchemaMap = Record<string, FieldSchema>;
38
+ /**
39
+ * Map of model name -> field schema map.
40
+ */
41
+ export type ExplicitSchemas = Record<string, FieldSchemaMap>;
42
+ //# sourceMappingURL=schemaTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaTypes.d.ts","sourceRoot":"","sources":["../../src/shapes/schemaTypes.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,UAAU,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schemaTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaTypes.js","sourceRoot":"","sources":["../../src/shapes/schemaTypes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,46 @@
1
+ export interface FieldSpec {
2
+ /**
3
+ * Field name (e.g. "key", "piid", "recipient").
4
+ * The special name "*" indicates a wildcard selection.
5
+ */
6
+ name: string;
7
+ /**
8
+ * Optional alias for this field in the shaped response.
9
+ */
10
+ alias?: string | null;
11
+ /**
12
+ * Nested field specifications for nested objects.
13
+ */
14
+ nestedFields?: FieldSpec[];
15
+ /**
16
+ * Whether this field represents a wildcard selection.
17
+ */
18
+ isWildcard?: boolean;
19
+ }
20
+ export interface ShapeSpecOptions {
21
+ /**
22
+ * Whether the server should return flat (dotted) keys.
23
+ */
24
+ isFlat?: boolean;
25
+ /**
26
+ * Whether list fields should be flattened.
27
+ */
28
+ isFlatLists?: boolean;
29
+ }
30
+ /**
31
+ * Parsed representation of a Tango shape string.
32
+ *
33
+ * This mirrors the Python ShapeSpec class and is used to drive both
34
+ * schema validation and dynamic model generation.
35
+ */
36
+ export declare class ShapeSpec {
37
+ readonly fields: FieldSpec[];
38
+ readonly isFlat: boolean;
39
+ readonly isFlatLists: boolean;
40
+ constructor(fields: FieldSpec[], options?: ShapeSpecOptions);
41
+ /**
42
+ * Build a stable cache key for this shape when used with a base model.
43
+ */
44
+ getCacheKey(baseModelName: string): string;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shapes/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAEtB;;OAEG;IACH,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC;IAE3B;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;gBAElB,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,GAAE,gBAAqB;IAM/D;;OAEG;IACH,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;CAQ3C"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Parsed representation of a Tango shape string.
3
+ *
4
+ * This mirrors the Python ShapeSpec class and is used to drive both
5
+ * schema validation and dynamic model generation.
6
+ */
7
+ export class ShapeSpec {
8
+ constructor(fields, options = {}) {
9
+ this.fields = fields;
10
+ this.isFlat = options.isFlat ?? false;
11
+ this.isFlatLists = options.isFlatLists ?? false;
12
+ }
13
+ /**
14
+ * Build a stable cache key for this shape when used with a base model.
15
+ */
16
+ getCacheKey(baseModelName) {
17
+ const payload = {
18
+ fields: this.fields,
19
+ isFlat: this.isFlat,
20
+ isFlatLists: this.isFlatLists,
21
+ };
22
+ return `${baseModelName}:${JSON.stringify(payload)}`;
23
+ }
24
+ }
25
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/shapes/types.ts"],"names":[],"mappings":"AAmCA;;;;;GAKG;AACH,MAAM,OAAO,SAAS;IAKpB,YAAY,MAAmB,EAAE,UAA4B,EAAE;QAC7D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;QACtC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,KAAK,CAAC;IAClD,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,aAAqB;QAC/B,MAAM,OAAO,GAAG;YACd,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAC;QACF,OAAO,GAAG,aAAa,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IACvD,CAAC;CACF"}
@@ -0,0 +1,28 @@
1
+ export interface TangoClientOptions {
2
+ /**
3
+ * Tango API key. If omitted, the client will try to read TANGO_API_KEY
4
+ * from the environment (in Node environments).
5
+ */
6
+ apiKey?: string;
7
+ /**
8
+ * Base URL for the Tango API. Defaults to the public SaaS endpoint.
9
+ */
10
+ baseUrl?: string;
11
+ /**
12
+ * Request timeout in milliseconds. Defaults to 30000ms (30 seconds).
13
+ */
14
+ timeoutMs?: number;
15
+ /**
16
+ * Custom fetch implementation. If not provided, the global fetch will be used
17
+ * (Node 18+ or browser environments).
18
+ */
19
+ fetchImpl?: typeof fetch;
20
+ }
21
+ export interface PaginatedResponse<T> {
22
+ count: number;
23
+ next: string | null;
24
+ previous: string | null;
25
+ pageMetadata: Record<string, unknown> | null;
26
+ results: T[];
27
+ }
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7C,OAAO,EAAE,CAAC,EAAE,CAAC;CACd"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parse a date string from the Tango API into a JavaScript Date object.
3
+ * Returns null if the input is falsy or cannot be parsed.
4
+ *
5
+ * The Tango API generally returns ISO-8601 formatted strings, which Date can parse
6
+ * reliably in modern runtimes.
7
+ */
8
+ export declare function parseDate(value: unknown): Date | null;
9
+ /**
10
+ * Alias for parseDate for now. If the API starts returning timestamps with time
11
+ * components distinct from plain dates, this function can be specialized.
12
+ */
13
+ export declare function parseDateTime(value: unknown): Date | null;
14
+ //# sourceMappingURL=dates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../../src/utils/dates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAUrD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAEzD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Parse a date string from the Tango API into a JavaScript Date object.
3
+ * Returns null if the input is falsy or cannot be parsed.
4
+ *
5
+ * The Tango API generally returns ISO-8601 formatted strings, which Date can parse
6
+ * reliably in modern runtimes.
7
+ */
8
+ export function parseDate(value) {
9
+ if (typeof value !== "string" || !value.trim()) {
10
+ return null;
11
+ }
12
+ const d = new Date(value);
13
+ if (Number.isNaN(d.getTime())) {
14
+ return null;
15
+ }
16
+ return d;
17
+ }
18
+ /**
19
+ * Alias for parseDate for now. If the API starts returning timestamps with time
20
+ * components distinct from plain dates, this function can be specialized.
21
+ */
22
+ export function parseDateTime(value) {
23
+ return parseDate(value);
24
+ }
25
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../../src/utils/dates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,23 @@
1
+ export interface HttpClientOptions {
2
+ baseUrl?: string;
3
+ apiKey?: string | null;
4
+ timeoutMs?: number;
5
+ fetchImpl?: typeof fetch;
6
+ }
7
+ export interface RequestOptions {
8
+ method: "GET" | "POST";
9
+ path: string;
10
+ query?: Record<string, unknown>;
11
+ body?: unknown;
12
+ }
13
+ export declare class HttpClient {
14
+ readonly baseUrl: string;
15
+ readonly apiKey: string | null;
16
+ readonly timeoutMs: number;
17
+ private readonly fetchImpl;
18
+ constructor(options?: HttpClientOptions);
19
+ request<T = unknown>(options: RequestOptions): Promise<T>;
20
+ get<T = unknown>(path: string, query?: Record<string, unknown>): Promise<T>;
21
+ post<T = unknown>(path: string, body?: unknown): Promise<T>;
22
+ }
23
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAuCD,qBAAa,UAAU;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,OAAO,GAAE,iBAAsB;IAgBrC,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IAkH/D,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAI3E,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAG5D"}
@@ -0,0 +1,158 @@
1
+ import { TangoAPIError, TangoAuthError, TangoNotFoundError, TangoRateLimitError, TangoTimeoutError, TangoValidationError } from "../errors.js";
2
+ import { DEFAULT_BASE_URL } from "../config.js";
3
+ function isRecord(value) {
4
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+ function isSafePrimitive(value) {
7
+ const type = typeof value;
8
+ return type === "string" || type === "number" || type === "boolean" || type === "symbol" || type === "bigint";
9
+ }
10
+ function toParamValue(value) {
11
+ if (value instanceof Date)
12
+ return value.toISOString();
13
+ if (value === null || value === undefined)
14
+ return "";
15
+ if (isSafePrimitive(value))
16
+ return String(value);
17
+ return JSON.stringify(value);
18
+ }
19
+ function buildSearchParams(params) {
20
+ if (!params)
21
+ return "";
22
+ const search = new URLSearchParams();
23
+ for (const [key, value] of Object.entries(params)) {
24
+ if (value === undefined || value === null)
25
+ continue;
26
+ if (Array.isArray(value)) {
27
+ for (const item of value) {
28
+ if (item === undefined || item === null)
29
+ continue;
30
+ search.append(key, toParamValue(item));
31
+ }
32
+ }
33
+ else {
34
+ search.append(key, toParamValue(value));
35
+ }
36
+ }
37
+ const queryString = search.toString();
38
+ return queryString;
39
+ }
40
+ export class HttpClient {
41
+ constructor(options = {}) {
42
+ const { baseUrl = DEFAULT_BASE_URL, apiKey = null, timeoutMs = 30000, fetchImpl } = options;
43
+ this.baseUrl = baseUrl.replace(/\/+$/, "");
44
+ this.apiKey = apiKey;
45
+ this.timeoutMs = timeoutMs;
46
+ const globalFetch = typeof fetch !== "undefined" ? fetch : undefined;
47
+ if (!fetchImpl && !globalFetch) {
48
+ throw new Error("No fetch implementation available. Use Node 18+ (global fetch) or provide fetchImpl.");
49
+ }
50
+ this.fetchImpl = (fetchImpl ?? globalFetch);
51
+ }
52
+ async request(options) {
53
+ const { method, path, query, body } = options;
54
+ const url = new URL(path.replace(/^\//, ""), this.baseUrl.endsWith("/") ? `${this.baseUrl}` : `${this.baseUrl}/`);
55
+ const queryString = buildSearchParams(query);
56
+ if (queryString) {
57
+ url.search = queryString;
58
+ }
59
+ const headers = {
60
+ Accept: "application/json",
61
+ };
62
+ let jsonBody;
63
+ if (body !== undefined) {
64
+ headers["Content-Type"] = "application/json";
65
+ jsonBody = JSON.stringify(body);
66
+ }
67
+ if (this.apiKey) {
68
+ headers["X-API-KEY"] = this.apiKey;
69
+ }
70
+ let controller;
71
+ let timeoutId;
72
+ if (typeof AbortController !== "undefined" && this.timeoutMs > 0) {
73
+ controller = new AbortController();
74
+ timeoutId = setTimeout(() => {
75
+ controller?.abort();
76
+ }, this.timeoutMs);
77
+ }
78
+ let res;
79
+ try {
80
+ res = await this.fetchImpl(url.toString(), {
81
+ method,
82
+ headers,
83
+ body: jsonBody,
84
+ signal: controller?.signal,
85
+ });
86
+ }
87
+ catch (err) {
88
+ if (timeoutId)
89
+ clearTimeout(timeoutId);
90
+ const name = err?.name ?? null;
91
+ if (name === "AbortError") {
92
+ throw new TangoTimeoutError(`Request timed out after ${this.timeoutMs}ms`, 408, undefined);
93
+ }
94
+ const msg = err instanceof Error ? err.message : String(err);
95
+ throw new TangoAPIError(`Request failed: ${msg}`);
96
+ }
97
+ finally {
98
+ if (timeoutId)
99
+ clearTimeout(timeoutId);
100
+ }
101
+ let text;
102
+ let data = null;
103
+ try {
104
+ text = await res.text();
105
+ data = text ? JSON.parse(text) : null;
106
+ }
107
+ catch {
108
+ data = null;
109
+ }
110
+ if (res.status === 401) {
111
+ throw new TangoAuthError("Invalid API key or authentication required", res.status, data);
112
+ }
113
+ if (res.status === 404) {
114
+ throw new TangoNotFoundError("Resource not found", res.status, data);
115
+ }
116
+ if (res.status === 400) {
117
+ let msg = "Invalid request parameters";
118
+ if (data && typeof data === "object") {
119
+ const record = data;
120
+ let detail = record.detail ?? record.message ?? record.error ?? null;
121
+ if (!detail) {
122
+ const keys = Object.keys(record);
123
+ if (keys.length > 0) {
124
+ const first = record[keys[0]];
125
+ if (Array.isArray(first) && first.length > 0) {
126
+ detail = String(first[0]);
127
+ }
128
+ else if (typeof first === "string") {
129
+ detail = first;
130
+ }
131
+ }
132
+ }
133
+ if (detail) {
134
+ msg = `Invalid request parameters: ${detail}`;
135
+ }
136
+ }
137
+ throw new TangoValidationError(msg, res.status, data);
138
+ }
139
+ if (res.status === 429) {
140
+ throw new TangoRateLimitError("Rate limit exceeded", res.status, data);
141
+ }
142
+ if (!res.ok) {
143
+ throw new TangoAPIError(`API request failed with status ${res.status}`, res.status, data);
144
+ }
145
+ if (res.ok && isRecord(data) && typeof data.error === "string") {
146
+ // The API occasionally signals errors in a 200 payload; surface as TangoAPIError.
147
+ throw new TangoAPIError(data.error, res.status, data);
148
+ }
149
+ return (data ?? {});
150
+ }
151
+ get(path, query) {
152
+ return this.request({ method: "GET", path, query });
153
+ }
154
+ post(path, body) {
155
+ return this.request({ method: "POST", path, body });
156
+ }
157
+ }
158
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Representation of decimal values from the Tango API.
3
+ *
4
+ * For now we keep the raw string returned by the API to avoid precision issues
5
+ * associated with JavaScript's Number type. Callers can convert to a number or
6
+ * use a bigint/decimal library as needed.
7
+ */
8
+ export type DecimalLike = string;
9
+ /**
10
+ * Parse a decimal-like value from the API into a DecimalLike.
11
+ * Returns null if the value is not a string or number.
12
+ */
13
+ export declare function parseDecimal(value: unknown): DecimalLike | null;
14
+ //# sourceMappingURL=number.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/utils/number.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,IAAI,CAQ/D"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parse a decimal-like value from the API into a DecimalLike.
3
+ * Returns null if the value is not a string or number.
4
+ */
5
+ export function parseDecimal(value) {
6
+ if (typeof value === "number" && Number.isFinite(value)) {
7
+ return value.toString();
8
+ }
9
+ if (typeof value === "string" && value.trim() !== "") {
10
+ return value;
11
+ }
12
+ return null;
13
+ }
14
+ //# sourceMappingURL=number.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"number.js","sourceRoot":"","sources":["../../src/utils/number.ts"],"names":[],"mappings":"AASA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAc;IACzC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACrD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Unflatten a flat object with dot-separated keys into nested objects.
3
+ *
4
+ * Example:
5
+ * { "recipient.display_name": "Acme" } ->
6
+ * { recipient: { display_name: "Acme" } }
7
+ *
8
+ * This mirrors the behavior of the Python client's `_unflatten_response`.
9
+ */
10
+ export declare function unflattenResponse<T extends Record<string, unknown>>(data: T, joiner?: string): Record<string, unknown>;
11
+ //# sourceMappingURL=unflatten.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unflatten.d.ts","sourceRoot":"","sources":["../../src/utils/unflatten.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,SAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4CnH"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Unflatten a flat object with dot-separated keys into nested objects.
3
+ *
4
+ * Example:
5
+ * { "recipient.display_name": "Acme" } ->
6
+ * { recipient: { display_name: "Acme" } }
7
+ *
8
+ * This mirrors the behavior of the Python client's `_unflatten_response`.
9
+ */
10
+ export function unflattenResponse(data, joiner = ".") {
11
+ if (!data || typeof data !== "object") {
12
+ return data;
13
+ }
14
+ const keys = Object.keys(data);
15
+ const hasFlatKeys = keys.some((key) => key.includes(joiner));
16
+ if (!hasFlatKeys) {
17
+ return { ...data };
18
+ }
19
+ const result = {};
20
+ for (const [flatKey, value] of Object.entries(data)) {
21
+ if (!flatKey.includes(joiner)) {
22
+ result[flatKey] = value;
23
+ continue;
24
+ }
25
+ const parts = flatKey.split(joiner);
26
+ let current = result;
27
+ for (let i = 0; i < parts.length - 1; i += 1) {
28
+ const part = parts[i];
29
+ const existing = current[part];
30
+ if (existing == null) {
31
+ const next = {};
32
+ current[part] = next;
33
+ current = next;
34
+ }
35
+ else if (typeof existing === "object" && !Array.isArray(existing)) {
36
+ current = existing;
37
+ }
38
+ else {
39
+ // Collision: primitive where we expected an object. Overwrite with object.
40
+ const next = {};
41
+ current[part] = next;
42
+ current = next;
43
+ }
44
+ }
45
+ current[parts[parts.length - 1]] = value;
46
+ }
47
+ return result;
48
+ }
49
+ //# sourceMappingURL=unflatten.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unflatten.js","sourceRoot":"","sources":["../../src/utils/unflatten.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAoC,IAAO,EAAE,MAAM,GAAG,GAAG;IACxF,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,IAA0C,CAAC;IACpD,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAA4B,EAAE,CAAC;IAE3C,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;YACxB,SAAS;QACX,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,OAAO,GAA4B,MAAM,CAAC;QAE9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAE/B,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,IAAI,GAA4B,EAAE,CAAC;gBACzC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBACrB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;iBAAM,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpE,OAAO,GAAG,QAAmC,CAAC;YAChD,CAAC;iBAAM,CAAC;gBACN,2EAA2E;gBAC3E,MAAM,IAAI,GAA4B,EAAE,CAAC;gBACzC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;gBACrB,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;IAC3C,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}