@danceroutine/tango-core 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -0
  3. package/dist/{TangoError-NPkVPfuH.js → TangoError-DdQVQNZU.js} +5 -1
  4. package/dist/TangoError-DdQVQNZU.js.map +1 -0
  5. package/dist/errors/AuthenticationError.d.ts +6 -1
  6. package/dist/errors/ConflictError.d.ts +6 -1
  7. package/dist/errors/NotFoundError.d.ts +6 -1
  8. package/dist/errors/PermissionDenied.d.ts +6 -1
  9. package/dist/errors/TangoError.d.ts +13 -1
  10. package/dist/errors/ValidationError.d.ts +5 -1
  11. package/dist/errors/factories/HttpErrorFactory.d.ts +20 -11
  12. package/dist/errors/factories/index.d.ts +1 -1
  13. package/dist/errors/index.d.ts +2 -2
  14. package/dist/errors/index.js +4 -4
  15. package/dist/{errors-CzSQXdgI.js → errors-_tWsmNyZ.js} +81 -34
  16. package/dist/errors-_tWsmNyZ.js.map +1 -0
  17. package/dist/http/TangoBody.d.ts +43 -18
  18. package/dist/http/TangoHeaders.d.ts +10 -5
  19. package/dist/http/TangoQueryParams.d.ts +69 -0
  20. package/dist/http/TangoRequest.d.ts +82 -0
  21. package/dist/http/TangoResponse.d.ts +184 -49
  22. package/dist/http/index.d.ts +2 -1
  23. package/dist/http/index.js +4 -4
  24. package/dist/{http-DOLwwAYt.js → http-D20MQa6p.js} +675 -295
  25. package/dist/http-D20MQa6p.js.map +1 -0
  26. package/dist/index.d.ts +9 -7
  27. package/dist/index.js +7 -6
  28. package/dist/logging/ConsoleLogger.d.ts +15 -0
  29. package/dist/logging/Logger.d.ts +13 -0
  30. package/dist/logging/getLogger.d.ts +23 -0
  31. package/dist/logging/index.d.ts +3 -0
  32. package/dist/logging/index.js +3 -0
  33. package/dist/logging-BWeD4HOO.js +48 -0
  34. package/dist/logging-BWeD4HOO.js.map +1 -0
  35. package/dist/runtime/binary/isArrayBuffer.d.ts +3 -0
  36. package/dist/runtime/binary/isBlob.d.ts +3 -0
  37. package/dist/runtime/binary/isUint8Array.d.ts +3 -0
  38. package/dist/runtime/date/isDate.d.ts +3 -0
  39. package/dist/runtime/error/isError.d.ts +3 -0
  40. package/dist/runtime/index.d.ts +1 -1
  41. package/dist/runtime/index.js +2 -2
  42. package/dist/runtime/object/index.d.ts +1 -0
  43. package/dist/runtime/object/isNil.d.ts +4 -0
  44. package/dist/runtime/object/isObject.d.ts +3 -0
  45. package/dist/runtime/web/isFile.d.ts +3 -0
  46. package/dist/runtime/web/isFormData.d.ts +3 -0
  47. package/dist/runtime/web/isReadableStream.d.ts +3 -0
  48. package/dist/runtime/web/isURLSearchParams.d.ts +3 -0
  49. package/dist/{runtime-DPpCYEe_.js → runtime-B8KkgD3R.js} +14 -4
  50. package/dist/runtime-B8KkgD3R.js.map +1 -0
  51. package/dist/sql/SqlDialect.d.ts +5 -0
  52. package/dist/sql/SqlIdentifierRole.d.ts +13 -0
  53. package/dist/sql/SqlSafetyEngine.d.ts +50 -0
  54. package/dist/sql/TrustedSqlFragment.d.ts +5 -0
  55. package/dist/sql/ValidatedSqlIdentifier.d.ts +7 -0
  56. package/dist/sql/index.d.ts +13 -0
  57. package/dist/sql/index.js +3 -0
  58. package/dist/sql/isTrustedSqlFragment.d.ts +5 -0
  59. package/dist/sql/quoteSqlIdentifier.d.ts +6 -0
  60. package/dist/sql/trustedSql.d.ts +5 -0
  61. package/dist/sql/validateSqlIdentifier.d.ts +6 -0
  62. package/dist/sql-D3frkfy-.js +116 -0
  63. package/dist/sql-D3frkfy-.js.map +1 -0
  64. package/package.json +65 -54
  65. package/dist/TangoError-NPkVPfuH.js.map +0 -1
  66. package/dist/errors/factories/HttpErrorFactory.js +0 -91
  67. package/dist/errors-CzSQXdgI.js.map +0 -1
  68. package/dist/http/TangoHeaders.js +0 -396
  69. package/dist/http/TangoResponse.js +0 -556
  70. package/dist/http-DOLwwAYt.js.map +0 -1
  71. package/dist/result/Err.d.ts +0 -21
  72. package/dist/result/Ok.d.ts +0 -21
  73. package/dist/result/Result.d.ts +0 -33
  74. package/dist/result/index.d.ts +0 -8
  75. package/dist/result/index.js +0 -3
  76. package/dist/result-CBqw9Hlg.js +0 -82
  77. package/dist/result-CBqw9Hlg.js.map +0 -1
  78. package/dist/runtime-DPpCYEe_.js.map +0 -1
@@ -1,396 +0,0 @@
1
- import { isArrayBuffer, isBlob, isUint8Array } from '../runtime/index';
2
- /**
3
- * TangoHeaders extends the Web Headers class, adding ergonomic helpers
4
- * for common HTTP header patterns, convenience features, and a consistent API for
5
- * setting, appending, and deleting headers and cookies. This is designed to be
6
- * used by TangoResponse and other Tango HTTP utilities.
7
- * Additionally, provides helpers for safely setting Content-Length based on a known body,
8
- * and setting Content-Disposition for inline or attachment filenames.
9
- *
10
- * Includes helpers for request tracing and correlation headers:
11
- * - X-Request-Id
12
- * - traceparent (W3C Trace Context)
13
- * - Server-Timing
14
- * - X-Response-Time
15
- */
16
- export class TangoHeaders extends Headers {
17
- static BRAND = 'tango.http.headers';
18
- __tangoBrand = TangoHeaders.BRAND;
19
- constructor(headers) {
20
- super(headers);
21
- }
22
- static isTangoHeaders(value) {
23
- return (typeof value === 'object' &&
24
- value !== null &&
25
- value.__tangoBrand === TangoHeaders.BRAND);
26
- }
27
- static hasNumberSize(value) {
28
- return typeof value === 'object' && value !== null && typeof value.size === 'number';
29
- }
30
- static hasNumberLength(value) {
31
- return (typeof value === 'object' && value !== null && typeof value.length === 'number');
32
- }
33
- static isNodeBuffer(value) {
34
- const maybeBuffer = Buffer;
35
- return (typeof Buffer !== 'undefined' && typeof maybeBuffer.isBuffer === 'function' && maybeBuffer.isBuffer(value));
36
- }
37
- /**
38
- * Sets the Content-Disposition header with type "inline" and the specified filename.
39
- * This is useful to indicate that the content should be displayed inline in the browser,
40
- * but with a suggested filename for saving.
41
- *
42
- * @param filename The filename to include in the Content-Disposition header.
43
- */
44
- setContentDispositionInline(filename) {
45
- const encoded = encodeURIComponent(filename);
46
- this.set('Content-Disposition', `inline; filename="${encoded}"; filename*=UTF-8''${encoded}`);
47
- }
48
- /**
49
- * Sets the Content-Disposition header with type "attachment" and the specified filename.
50
- * This is useful to indicate that the response should be downloaded as a file.
51
- *
52
- * @param filename The filename to include in the Content-Disposition header.
53
- */
54
- setContentDispositionAttachment(filename) {
55
- const encoded = encodeURIComponent(filename);
56
- this.set('Content-Disposition', `attachment; filename="${encoded}"; filename*=UTF-8''${encoded}`);
57
- }
58
- clone() {
59
- const copy = new TangoHeaders();
60
- for (const [name, value] of this.entries()) {
61
- copy.append(name, value);
62
- }
63
- return copy;
64
- }
65
- /**
66
- * Set a header, replacing the existing value.
67
- */
68
- setHeader(name, value) {
69
- this.set(name, value);
70
- }
71
- /**
72
- * Append a header, adding to existing value(s) for the name.
73
- */
74
- appendHeader(name, value) {
75
- this.append(name, value);
76
- }
77
- /**
78
- * Get a header value (first value if header is repeated).
79
- */
80
- getHeader(name) {
81
- return this.get(name);
82
- }
83
- /**
84
- * Check if a header exists.
85
- */
86
- hasHeader(name) {
87
- return this.has(name);
88
- }
89
- /**
90
- * Delete a header.
91
- */
92
- deleteHeader(name) {
93
- this.delete(name);
94
- }
95
- /**
96
- * Ensure a header is present only once and fully replaces the old value.
97
- */
98
- ensureUnique(name, value) {
99
- this.delete(name);
100
- this.set(name, value);
101
- }
102
- /**
103
- * Add a field (or fields) to the Vary header, merging with existing values.
104
- */
105
- vary(...fields) {
106
- if (fields.length === 0)
107
- return;
108
- const key = 'Vary';
109
- const prev = this.get(key);
110
- const nextSet = new Set();
111
- if (prev) {
112
- for (const v of prev.split(',')) {
113
- if (v.trim())
114
- nextSet.add(v.trim());
115
- }
116
- }
117
- for (const f of fields) {
118
- if (f.trim())
119
- nextSet.add(f.trim());
120
- }
121
- this.set(key, Array.from(nextSet).join(', '));
122
- }
123
- /**
124
- * Set a cookie header (for Set-Cookie).
125
- * @param name
126
- * @param value
127
- * @param options
128
- */
129
- setCookie(name, value, options) {
130
- this.append('Set-Cookie', TangoHeaders.serializeCookie(name, value, options));
131
- }
132
- /**
133
- * Append (additionally) a new cookie.
134
- */
135
- appendCookie(name, value, options) {
136
- this.append('Set-Cookie', TangoHeaders.serializeCookie(name, value, options));
137
- }
138
- /**
139
- * Delete a cookie ("unset" it via expired date).
140
- */
141
- deleteCookie(name, options) {
142
- this.setCookie(name, '', {
143
- ...options,
144
- expires: new Date(0),
145
- maxAge: 0,
146
- });
147
- }
148
- /**
149
- * Serialize a cookie for the Set-Cookie header line.
150
- */
151
- static serializeCookie(name, value, options = {}) {
152
- let cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value ?? '');
153
- if (options.domain)
154
- cookie += `; Domain=${options.domain}`;
155
- if (options.path)
156
- cookie += `; Path=${options.path}`;
157
- else
158
- cookie += '; Path=/';
159
- if (options.expires)
160
- cookie += `; Expires=${options.expires.toUTCString()}`;
161
- if (typeof options.maxAge === 'number')
162
- cookie += `; Max-Age=${options.maxAge}`;
163
- if (options.secure)
164
- cookie += '; Secure';
165
- if (options.httpOnly)
166
- cookie += '; HttpOnly';
167
- if (options.sameSite)
168
- cookie += `; SameSite=${options.sameSite}`;
169
- if (options.priority)
170
- cookie += `; Priority=${options.priority}`;
171
- if (options.partitioned)
172
- cookie += '; Partitioned';
173
- return cookie;
174
- }
175
- /**
176
- * Add or override Cache-Control header with helpers for common policies.
177
- */
178
- cacheControl(control) {
179
- if (typeof control === 'string') {
180
- this.set('Cache-Control', control);
181
- return;
182
- }
183
- // Compose Cache-Control from object
184
- const parts = [];
185
- for (const [k, v] of Object.entries(control)) {
186
- if (typeof v === 'boolean') {
187
- if (v)
188
- parts.push(k.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase()));
189
- }
190
- else if (typeof v === 'number' || (typeof v === 'string' && v.length > 0)) {
191
- parts.push(`${k.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase())}=${v}`);
192
- }
193
- }
194
- if (parts.length) {
195
- this.set('Cache-Control', parts.join(', '));
196
- }
197
- }
198
- /**
199
- * Set the Location header.
200
- */
201
- location(url) {
202
- this.set('Location', url);
203
- }
204
- /**
205
- * Set the Content-Type header.
206
- */
207
- contentType(mime) {
208
- this.set('Content-Type', mime);
209
- }
210
- /**
211
- * Attempt to guess and set the Content-Type header from a file (string or Blob) and optional filename.
212
- *
213
- * @param file File-like input (string or Blob)
214
- * @param filename Optional filename to help guess mime type by extension
215
- */
216
- setContentTypeByFile(file, filename) {
217
- // do not overwrite explicit content-type
218
- if (this.has('Content-Type'))
219
- return;
220
- // If file is a string and a filename is provided or can be guessed from file path
221
- if (typeof file === 'string' && filename) {
222
- // Guess type by extension
223
- const ext = filename.split('.').pop()?.toLowerCase() ?? '';
224
- const map = {
225
- txt: 'text/plain',
226
- text: 'text/plain',
227
- html: 'text/html',
228
- css: 'text/css',
229
- js: 'application/javascript',
230
- json: 'application/json',
231
- jpg: 'image/jpeg',
232
- jpeg: 'image/jpeg',
233
- png: 'image/png',
234
- gif: 'image/gif',
235
- webp: 'image/webp',
236
- pdf: 'application/pdf',
237
- svg: 'image/svg+xml',
238
- ico: 'image/x-icon',
239
- md: 'text/markdown',
240
- };
241
- const mime = map[ext];
242
- if (mime) {
243
- this.set('Content-Type', mime);
244
- }
245
- else {
246
- this.set('Content-Type', 'application/octet-stream');
247
- }
248
- return;
249
- }
250
- // If it's a Blob, use its type, or fallback
251
- if (isBlob(file)) {
252
- if (file.type && file.type !== '') {
253
- this.set('Content-Type', file.type);
254
- }
255
- else {
256
- this.set('Content-Type', 'application/octet-stream');
257
- }
258
- return;
259
- }
260
- // Fallback
261
- this.set('Content-Type', 'application/octet-stream');
262
- }
263
- /**
264
- * Sets the Content-Length header, inferring it from the body if not explicitly provided.
265
- * If the body is a string, ArrayBuffer, Uint8Array, Blob/Buffer (or has a .size or .length property),
266
- * the length is computed. For unsupported types, does nothing.
267
- * Mirrors logic from TangoResponse.ts, but generalized for use anywhere.
268
- */
269
- setContentLengthFromBody(body) {
270
- if (this.has('Content-Length') || body === null || body === undefined) {
271
- return;
272
- }
273
- let len;
274
- if (typeof body === 'string') {
275
- len = new TextEncoder().encode(body).length;
276
- }
277
- else if (isArrayBuffer(body)) {
278
- len = body.byteLength;
279
- }
280
- else if (isUint8Array(body)) {
281
- len = body.byteLength;
282
- }
283
- else if (isBlob(body)) {
284
- len = body.size;
285
- }
286
- // Node.js Buffer support (Buffer.isBuffer and .length)
287
- else if (TangoHeaders.isNodeBuffer(body)) {
288
- len = body.length;
289
- }
290
- // Generic "size" (number) property
291
- else if (TangoHeaders.hasNumberSize(body)) {
292
- len = body.size;
293
- }
294
- // Generic "length" (number) property but not string/ArrayBuffer/Uint8Array/etc
295
- else if (TangoHeaders.hasNumberLength(body) &&
296
- typeof body !== 'string' &&
297
- !isArrayBuffer(body) &&
298
- !isUint8Array(body)) {
299
- len = body.length;
300
- }
301
- if (typeof len === 'number') {
302
- this.set('Content-Length', len.toString());
303
- }
304
- }
305
- /**
306
- * Set or update the X-Request-Id header. Used for associating a request/response with a unique id.
307
- * @param id The request id value to set.
308
- */
309
- withRequestId(id) {
310
- this.set('X-Request-Id', id);
311
- return this;
312
- }
313
- /**
314
- * Set or update the traceparent header. Used for distributed trace context propagation (W3C Trace Context).
315
- * @param value The traceparent value (should be a valid traceparent header value).
316
- */
317
- withTraceParent(value) {
318
- this.set('traceparent', value);
319
- return this;
320
- }
321
- /**
322
- * Set or append a value to the Server-Timing header.
323
- * For a single metric, supply name, and optionally dur and desc.
324
- * For advanced usage (many metrics), set the value directly or use appendServerTimingRaw.
325
- *
326
- * @param name Name of the server-timing metric (e.g., 'total', 'db').
327
- * @param dur Optional duration in ms.
328
- * @param desc Optional string description.
329
- */
330
- withServerTiming(name, dur, desc) {
331
- // Build the metric string per spec: <metric>=<value>;dur=<duration>;desc="<description>"
332
- let metric = name;
333
- if (typeof dur === 'number') {
334
- metric += `;dur=${dur}`;
335
- }
336
- if (desc) {
337
- metric += `;desc="${desc.replace(/"/g, '\\"')}"`;
338
- }
339
- // Set or append. If header exists, append, separated by comma.
340
- const prev = this.get('Server-Timing');
341
- if (prev && prev.length > 0) {
342
- this.set('Server-Timing', prev + ', ' + metric);
343
- }
344
- else {
345
- this.set('Server-Timing', metric);
346
- }
347
- return this;
348
- }
349
- /**
350
- * Directly append a raw Server-Timing metric value.
351
- * This is useful for advanced cases where you have multiple metrics assembled.
352
- * @param value The server-timing value chunk to append.
353
- */
354
- appendServerTimingRaw(value) {
355
- const prev = this.get('Server-Timing');
356
- if (prev && prev.length > 0) {
357
- this.set('Server-Timing', prev + ', ' + value);
358
- }
359
- else {
360
- this.set('Server-Timing', value);
361
- }
362
- return this;
363
- }
364
- /**
365
- * Set or update X-Response-Time header.
366
- * @param ms Elapsed time in milliseconds (number or string for flexibility).
367
- */
368
- withResponseTime(ms) {
369
- this.set('X-Response-Time', typeof ms === 'number' ? `${ms}ms` : ms);
370
- return this;
371
- }
372
- /**
373
- * Return the current value of X-Request-Id.
374
- */
375
- getRequestId() {
376
- return this.get('X-Request-Id');
377
- }
378
- /**
379
- * Return the current value of traceparent header.
380
- */
381
- getTraceParent() {
382
- return this.get('traceparent');
383
- }
384
- /**
385
- * Return the current Server-Timing string, if set.
386
- */
387
- getServerTiming() {
388
- return this.get('Server-Timing');
389
- }
390
- /**
391
- * Return the current X-Response-Time string, if set.
392
- */
393
- getResponseTime() {
394
- return this.get('X-Response-Time');
395
- }
396
- }