@teepin/sdk 0.2.1 → 0.3.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 (134) hide show
  1. package/README.md +41 -36
  2. package/dist/cjs/client.js +42 -4
  3. package/dist/cjs/client.js.map +1 -1
  4. package/dist/cjs/direct/bytes.js +50 -0
  5. package/dist/cjs/direct/bytes.js.map +1 -0
  6. package/dist/cjs/direct/common.js +167 -0
  7. package/dist/cjs/direct/common.js.map +1 -0
  8. package/dist/cjs/direct/downloads-plan.js +127 -0
  9. package/dist/cjs/direct/downloads-plan.js.map +1 -0
  10. package/dist/cjs/direct/engine.js +405 -0
  11. package/dist/cjs/direct/engine.js.map +1 -0
  12. package/dist/cjs/direct/formats.js +44 -0
  13. package/dist/cjs/direct/formats.js.map +1 -0
  14. package/dist/cjs/direct/hpke.js +275 -0
  15. package/dist/cjs/direct/hpke.js.map +1 -0
  16. package/dist/cjs/direct/journal.js +80 -0
  17. package/dist/cjs/direct/journal.js.map +1 -0
  18. package/dist/cjs/direct/large.js +850 -0
  19. package/dist/cjs/direct/large.js.map +1 -0
  20. package/dist/cjs/direct/reads.js +1314 -0
  21. package/dist/cjs/direct/reads.js.map +1 -0
  22. package/dist/cjs/direct/sha256.js +122 -0
  23. package/dist/cjs/direct/sha256.js.map +1 -0
  24. package/dist/cjs/direct/tws1.js +480 -0
  25. package/dist/cjs/direct/tws1.js.map +1 -0
  26. package/dist/cjs/direct/uploads.js +1706 -0
  27. package/dist/cjs/direct/uploads.js.map +1 -0
  28. package/dist/cjs/errors.js +70 -13
  29. package/dist/cjs/errors.js.map +1 -1
  30. package/dist/cjs/index.js +5 -1
  31. package/dist/cjs/index.js.map +1 -1
  32. package/dist/cjs/links.js +195 -0
  33. package/dist/cjs/links.js.map +1 -0
  34. package/dist/cjs/node/downloads.js +835 -0
  35. package/dist/cjs/node/downloads.js.map +1 -0
  36. package/dist/cjs/node/journal.js +841 -0
  37. package/dist/cjs/node/journal.js.map +1 -0
  38. package/dist/cjs/node/uploads.js +219 -0
  39. package/dist/cjs/node/uploads.js.map +1 -0
  40. package/dist/cjs/node.js +26 -39
  41. package/dist/cjs/node.js.map +1 -1
  42. package/dist/cjs/storage.js +214 -184
  43. package/dist/cjs/storage.js.map +1 -1
  44. package/dist/cjs/transfer.js +18 -351
  45. package/dist/cjs/transfer.js.map +1 -1
  46. package/dist/esm/client.js +42 -4
  47. package/dist/esm/client.js.map +1 -1
  48. package/dist/esm/direct/bytes.js +42 -0
  49. package/dist/esm/direct/bytes.js.map +1 -0
  50. package/dist/esm/direct/common.js +155 -0
  51. package/dist/esm/direct/common.js.map +1 -0
  52. package/dist/esm/direct/downloads-plan.js +117 -0
  53. package/dist/esm/direct/downloads-plan.js.map +1 -0
  54. package/dist/esm/direct/engine.js +385 -0
  55. package/dist/esm/direct/engine.js.map +1 -0
  56. package/dist/esm/direct/formats.js +40 -0
  57. package/dist/esm/direct/formats.js.map +1 -0
  58. package/dist/esm/direct/hpke.js +262 -0
  59. package/dist/esm/direct/hpke.js.map +1 -0
  60. package/dist/esm/direct/journal.js +75 -0
  61. package/dist/esm/direct/journal.js.map +1 -0
  62. package/dist/esm/direct/large.js +816 -0
  63. package/dist/esm/direct/large.js.map +1 -0
  64. package/dist/esm/direct/reads.js +1275 -0
  65. package/dist/esm/direct/reads.js.map +1 -0
  66. package/dist/esm/direct/sha256.js +118 -0
  67. package/dist/esm/direct/sha256.js.map +1 -0
  68. package/dist/esm/direct/tws1.js +460 -0
  69. package/dist/esm/direct/tws1.js.map +1 -0
  70. package/dist/esm/direct/uploads.js +1660 -0
  71. package/dist/esm/direct/uploads.js.map +1 -0
  72. package/dist/esm/errors.js +64 -11
  73. package/dist/esm/errors.js.map +1 -1
  74. package/dist/esm/index.js +1 -0
  75. package/dist/esm/index.js.map +1 -1
  76. package/dist/esm/links.js +189 -0
  77. package/dist/esm/links.js.map +1 -0
  78. package/dist/esm/node/downloads.js +829 -0
  79. package/dist/esm/node/downloads.js.map +1 -0
  80. package/dist/esm/node/journal.js +824 -0
  81. package/dist/esm/node/journal.js.map +1 -0
  82. package/dist/esm/node/uploads.js +187 -0
  83. package/dist/esm/node/uploads.js.map +1 -0
  84. package/dist/esm/node.js +17 -14
  85. package/dist/esm/node.js.map +1 -1
  86. package/dist/esm/storage.js +214 -184
  87. package/dist/esm/storage.js.map +1 -1
  88. package/dist/esm/transfer.js +19 -349
  89. package/dist/esm/transfer.js.map +1 -1
  90. package/dist/types/client.d.ts +45 -4
  91. package/dist/types/client.d.ts.map +1 -1
  92. package/dist/types/direct/bytes.d.ts +11 -0
  93. package/dist/types/direct/bytes.d.ts.map +1 -0
  94. package/dist/types/direct/common.d.ts +47 -0
  95. package/dist/types/direct/common.d.ts.map +1 -0
  96. package/dist/types/direct/downloads-plan.d.ts +75 -0
  97. package/dist/types/direct/downloads-plan.d.ts.map +1 -0
  98. package/dist/types/direct/engine.d.ts +191 -0
  99. package/dist/types/direct/engine.d.ts.map +1 -0
  100. package/dist/types/direct/formats.d.ts +9 -0
  101. package/dist/types/direct/formats.d.ts.map +1 -0
  102. package/dist/types/direct/hpke.d.ts +55 -0
  103. package/dist/types/direct/hpke.d.ts.map +1 -0
  104. package/dist/types/direct/journal.d.ts +55 -0
  105. package/dist/types/direct/journal.d.ts.map +1 -0
  106. package/dist/types/direct/large.d.ts +170 -0
  107. package/dist/types/direct/large.d.ts.map +1 -0
  108. package/dist/types/direct/reads.d.ts +306 -0
  109. package/dist/types/direct/reads.d.ts.map +1 -0
  110. package/dist/types/direct/sha256.d.ts +16 -0
  111. package/dist/types/direct/sha256.d.ts.map +1 -0
  112. package/dist/types/direct/tws1.d.ts +157 -0
  113. package/dist/types/direct/tws1.d.ts.map +1 -0
  114. package/dist/types/direct/uploads.d.ts +461 -0
  115. package/dist/types/direct/uploads.d.ts.map +1 -0
  116. package/dist/types/errors.d.ts +47 -8
  117. package/dist/types/errors.d.ts.map +1 -1
  118. package/dist/types/index.d.ts +1 -0
  119. package/dist/types/index.d.ts.map +1 -1
  120. package/dist/types/links.d.ts +86 -0
  121. package/dist/types/links.d.ts.map +1 -0
  122. package/dist/types/node/downloads.d.ts +73 -0
  123. package/dist/types/node/downloads.d.ts.map +1 -0
  124. package/dist/types/node/journal.d.ts +162 -0
  125. package/dist/types/node/journal.d.ts.map +1 -0
  126. package/dist/types/node/uploads.d.ts +86 -0
  127. package/dist/types/node/uploads.d.ts.map +1 -0
  128. package/dist/types/node.d.ts +4 -2
  129. package/dist/types/node.d.ts.map +1 -1
  130. package/dist/types/storage.d.ts +107 -64
  131. package/dist/types/storage.d.ts.map +1 -1
  132. package/dist/types/transfer.d.ts +14 -23
  133. package/dist/types/transfer.d.ts.map +1 -1
  134. package/package.json +2 -2
@@ -0,0 +1,155 @@
1
+ // Copyright 2026 TEEPIN Project
2
+ // Licensed under the Apache License, Version 2.0
3
+ const str = (v) => (v === undefined || v === null ? '' : String(v));
4
+ const unquote = (etag) => etag.trim().replace(/^W\//, '').replace(/^"|"$/g, '');
5
+ const hex = (b) => Array.from(b, (x) => x.toString(16).padStart(2, '0')).join('');
6
+ /** base64 of bytes (btoa is in Node 16+ and every browser). */
7
+ export function toBase64(b) {
8
+ let s = '';
9
+ for (let i = 0; i < b.length; i += 0x8000)
10
+ s += String.fromCharCode(...b.subarray(i, i + 0x8000));
11
+ return btoa(s);
12
+ }
13
+ /** A UUIDv7 (RFC 9562): a millisecond timestamp, then random bits, so ids sort by time. */
14
+ export function uuid7() {
15
+ const b = crypto.getRandomValues(new Uint8Array(16));
16
+ let ms = Date.now();
17
+ for (let i = 5; i >= 0; i--) {
18
+ b[i] = ms % 256;
19
+ ms = Math.floor(ms / 256);
20
+ }
21
+ b[6] = (b[6] & 0x0f) | 0x70;
22
+ b[8] = (b[8] & 0x3f) | 0x80;
23
+ const h = hex(b);
24
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
25
+ }
26
+ /** An object from the API's JSON. */
27
+ export function objectFrom(raw) {
28
+ const d = (raw ?? {});
29
+ const date = (v) => new Date(str(v) || NaN);
30
+ return {
31
+ id: str(d.id),
32
+ key: str(d.key),
33
+ size: Number(d.size_bytes ?? 0),
34
+ contentType: d.content_type || undefined,
35
+ metadata: d.metadata,
36
+ checksumSha256: d.checksum_sha256 || undefined,
37
+ etag: unquote(str(d.etag)),
38
+ partCount: Number(d.part_count ?? 0),
39
+ status: str(d.status),
40
+ backend: str(d.backend),
41
+ createdAt: date(d.created_at),
42
+ updatedAt: date(d.updated_at),
43
+ uploadedAt: d.uploaded_at ? date(d.uploaded_at) : undefined,
44
+ };
45
+ }
46
+ /** A promise with its resolve at hand. */
47
+ export class Deferred {
48
+ promise;
49
+ resolve;
50
+ settled = false;
51
+ constructor() {
52
+ this.promise = new Promise((resolve) => {
53
+ this.resolve = () => {
54
+ this.settled = true;
55
+ resolve();
56
+ };
57
+ });
58
+ }
59
+ }
60
+ /** Resolves when p does, or rejects with signal's reason when it aborts first. */
61
+ export function abortable(p, signal) {
62
+ if (!signal)
63
+ return p;
64
+ if (signal.aborted) {
65
+ p.catch(() => undefined);
66
+ return Promise.reject(signal.reason);
67
+ }
68
+ return new Promise((resolve, reject) => {
69
+ const onAbort = () => reject(signal.reason);
70
+ signal.addEventListener('abort', onAbort, { once: true });
71
+ p.then((v) => {
72
+ signal.removeEventListener('abort', onAbort);
73
+ resolve(v);
74
+ }, (err) => {
75
+ signal.removeEventListener('abort', onAbort);
76
+ reject(err);
77
+ });
78
+ });
79
+ }
80
+ /** A controller that aborts with parent's reason, and a function that unlinks it once it is no longer needed. */
81
+ export function linked(parent) {
82
+ const ctl = new AbortController();
83
+ if (!parent)
84
+ return { ctl, unlink: () => { } };
85
+ if (parent.aborted) {
86
+ ctl.abort(parent.reason);
87
+ return { ctl, unlink: () => { } };
88
+ }
89
+ const onAbort = () => ctl.abort(parent.reason);
90
+ parent.addEventListener('abort', onAbort, { once: true });
91
+ return { ctl, unlink: () => parent.removeEventListener('abort', onAbort) };
92
+ }
93
+ /** Waits for every task to end, at most ms; whether they all did. Tasks never reject here (callers catch their own). */
94
+ export async function joinCapped(tasks, ms) {
95
+ let timer;
96
+ const capped = new Promise((resolve) => {
97
+ timer = setTimeout(() => resolve(false), ms);
98
+ });
99
+ try {
100
+ return await Promise.race([Promise.allSettled(tasks).then(() => true), capped]);
101
+ }
102
+ finally {
103
+ clearTimeout(timer);
104
+ }
105
+ }
106
+ /** Bytes done of a total, reported as they go. */
107
+ export class Progress {
108
+ total;
109
+ fn;
110
+ done = 0;
111
+ constructor(total, fn) {
112
+ this.total = total;
113
+ this.fn = fn;
114
+ }
115
+ add(n) {
116
+ if (!this.fn || n === 0)
117
+ return;
118
+ this.done += n;
119
+ this.fn(this.done, this.total);
120
+ }
121
+ }
122
+ /** A semaphore whose waits an abort ends. */
123
+ export class Slots {
124
+ free;
125
+ waiters = [];
126
+ constructor(free) {
127
+ this.free = free;
128
+ }
129
+ async acquire(signal) {
130
+ for (;;) {
131
+ signal?.throwIfAborted();
132
+ if (this.free > 0) {
133
+ this.free--;
134
+ return;
135
+ }
136
+ let wake;
137
+ const woken = new Promise((resolve) => (wake = resolve));
138
+ this.waiters.push(wake);
139
+ try {
140
+ await abortable(woken, signal);
141
+ }
142
+ catch (err) {
143
+ this.waiters = this.waiters.filter((w) => w !== wake);
144
+ if (this.free > 0)
145
+ this.waiters.shift()?.(); // a wake-up this call may have taken goes on to the next
146
+ throw err;
147
+ }
148
+ }
149
+ }
150
+ release() {
151
+ this.free++;
152
+ this.waiters.shift()?.();
153
+ }
154
+ }
155
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../../src/direct/common.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ // Copyright 2026 TEEPIN Project
2
+ // Licensed under the Apache License, Version 2.0
3
+ // Batch downloads' rules that need no file system (TEEPIN-STORAGE-DESIGN.md sections 19.15 and 22.5): a port of the
4
+ // path rules and the plan in teepin-python's _downloads.py.
5
+ //
6
+ // - Which key can be a path under the destination: every part of it a file name; on Windows also without the
7
+ // characters Windows refuses, a device name (with any extension) or a trailing dot or space, on every OS, since a
8
+ // folder may be shared with Windows.
9
+ // - Which keys would land on one file: the same path, or paths that differ only by case where names ignore case.
10
+ // - Which errors leave an object to go on with later (pending), and which stop a whole run.
11
+ //
12
+ // The node entry (src/node/downloads.ts) does the rest: the files, the links, the journal. Nothing here imports a Node
13
+ // module.
14
+ import { APIError, ConnectionError, ForbiddenError, PaymentRequiredError, TeepinError, UnauthorizedError } from '../errors.js';
15
+ /** A download is written into its target's path with this added, and renamed into place only when whole. */
16
+ export const PART_SUFFIX = '.teepin-part';
17
+ /** A key that can't be written under the destination: not a safe path, a link leading out, or another key's file. */
18
+ export class UnsafePathError extends TeepinError {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = 'UnsafePathError';
22
+ }
23
+ }
24
+ /** Windows' device names: a file named one of these, with any extension, is the device (CON, COM1, ...). */
25
+ const WIN_RESERVED = new Set(['CON', 'PRN', 'AUX', 'NUL', ...['COM', 'LPT'].flatMap((p) => [...'0123456789\u00b9\u00b2\u00b3'].map((d) => p + d))]);
26
+ /** Characters Windows doesn't allow in a file name. */
27
+ const WIN_BAD = new Set([...'<>:"|?*\\', ...Array.from({ length: 31 }, (_, i) => String.fromCharCode(i + 1))]);
28
+ /**
29
+ * Why one part of a key's path can't be a file name ("" when it can). On Windows also: its forbidden characters, a
30
+ * device name with or without an extension, and a trailing dot or space, which Windows drops, so the file would land
31
+ * under another name than the key's.
32
+ */
33
+ export function partProblem(part, windows) {
34
+ if (part === '' || part === '.' || part === '..')
35
+ return "an empty, '.' or '..' part";
36
+ if (part.includes('\0'))
37
+ return 'a NUL character';
38
+ if (windows) {
39
+ if ([...part].some((c) => WIN_BAD.has(c)))
40
+ return "a character Windows doesn't allow in a file name";
41
+ if (part.endsWith('.') || part.endsWith(' '))
42
+ return 'a name ending in a dot or a space, which Windows would drop';
43
+ if (WIN_RESERVED.has(part.split('.')[0].replace(/ +$/, '').toUpperCase()))
44
+ return 'a name Windows keeps for a device';
45
+ }
46
+ return '';
47
+ }
48
+ /** A key's path split into its parts, each checked by partProblem: UnsafePathError for the first that can't be a name. */
49
+ export function keyParts(rel, windows) {
50
+ const parts = rel.split('/');
51
+ for (const part of parts) {
52
+ const why = partProblem(part, windows);
53
+ if (why)
54
+ throw new UnsafePathError(`${JSON.stringify(rel)} isn't a safe path under the destination: ${why}`);
55
+ }
56
+ return parts;
57
+ }
58
+ /** A path as names that ignore case see it (NFC, then upper and lower case, so a sharp s and "SS" are one), or as it is. */
59
+ export function fold(path, insensitive) {
60
+ return insensitive ? path.normalize('NFC').toUpperCase().toLowerCase() : path;
61
+ }
62
+ /**
63
+ * (key, relative path) pairs to the plan: folder markers skipped, keys that aren't safe paths failed, and a key whose
64
+ * file another key already takes failed.
65
+ */
66
+ export async function plan(pairs, paths) {
67
+ const entries = [];
68
+ const collisions = [];
69
+ const seen = new Map(); // folded path -> the key going there
70
+ for (const [key, rel] of pairs) {
71
+ if (rel === '' || rel.endsWith('/')) {
72
+ const path = paths.shown(rel);
73
+ entries.push({ key, path, result: { key, path, status: 'skipped', resumed: false } });
74
+ continue;
75
+ }
76
+ let path;
77
+ try {
78
+ path = await paths.target(keyParts(rel, paths.windows), rel);
79
+ }
80
+ catch (err) {
81
+ if (!(err instanceof UnsafePathError))
82
+ throw err;
83
+ const shown = paths.shown(rel);
84
+ entries.push({ key, path: shown, result: { key, path: shown, status: 'failed', error: err, resumed: false } });
85
+ continue;
86
+ }
87
+ // Two keys that differ only by case are one file where names ignore case, and so would their part files be.
88
+ const norm = fold(path, paths.insensitive);
89
+ const other = seen.get(norm);
90
+ if (other !== undefined) {
91
+ if (other === key)
92
+ continue; // the same key twice
93
+ const error = new UnsafePathError(`${JSON.stringify(key)} and ${JSON.stringify(other)} both go to ${path}`);
94
+ collisions.push({ key, path, status: 'failed', error, resumed: false });
95
+ continue;
96
+ }
97
+ seen.set(norm, key);
98
+ entries.push({ key, path });
99
+ }
100
+ return { entries, collisions };
101
+ }
102
+ /** Errors that stop a whole run: what's left stays pending, to run again (a revoked key, no credit). */
103
+ export function stopsRun(err) {
104
+ return err instanceof UnauthorizedError || err instanceof ForbiddenError || err instanceof PaymentRequiredError;
105
+ }
106
+ /**
107
+ * An error after which an object's download is kept, to go on with later: one that stops the run, the network, a busy
108
+ * or failing service, or the local disk (full, say: any failed system call).
109
+ */
110
+ export function passing(err) {
111
+ if (stopsRun(err) || err instanceof ConnectionError)
112
+ return true;
113
+ if (err instanceof APIError)
114
+ return err.statusCode === 429 || err.statusCode >= 500;
115
+ return err instanceof Error && 'syscall' in err && typeof err.syscall === 'string';
116
+ }
117
+ //# sourceMappingURL=downloads-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloads-plan.js","sourceRoot":"","sources":["../../../src/direct/downloads-plan.ts"],"names":[],"mappings":"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"}