@runappsai/sdk 0.0.0-stage → 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.
- package/LICENSE +21 -0
- package/README.md +274 -2
- package/dist/activity-tracker.d.ts +113 -0
- package/dist/activity-tracker.d.ts.map +1 -0
- package/dist/activity-tracker.js +176 -0
- package/dist/activity-tracker.js.map +1 -0
- package/dist/audio.d.ts +129 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +267 -0
- package/dist/audio.js.map +1 -0
- package/dist/browser-auth.d.ts +179 -0
- package/dist/browser-auth.d.ts.map +1 -0
- package/dist/browser-auth.js +1361 -0
- package/dist/browser-auth.js.map +1 -0
- package/dist/chat.d.ts +221 -0
- package/dist/chat.d.ts.map +1 -0
- package/dist/chat.js +155 -0
- package/dist/chat.js.map +1 -0
- package/dist/client.d.ts +155 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +157 -0
- package/dist/client.js.map +1 -0
- package/dist/computer.d.ts +57 -0
- package/dist/computer.d.ts.map +1 -0
- package/dist/computer.js +21 -0
- package/dist/computer.js.map +1 -0
- package/dist/embeddings.d.ts +64 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +30 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/errors.d.ts +24 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +29 -0
- package/dist/errors.js.map +1 -0
- package/dist/event-wrap.d.ts +59 -0
- package/dist/event-wrap.d.ts.map +1 -0
- package/dist/event-wrap.js +142 -0
- package/dist/event-wrap.js.map +1 -0
- package/dist/events.d.ts +119 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +87 -0
- package/dist/events.js.map +1 -0
- package/dist/files.d.ts +218 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +212 -0
- package/dist/files.js.map +1 -0
- package/dist/image.d.ts +185 -0
- package/dist/image.d.ts.map +1 -0
- package/dist/image.js +148 -0
- package/dist/image.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/media.d.ts +56 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +133 -0
- package/dist/media.js.map +1 -0
- package/dist/model_options.d.ts +165 -0
- package/dist/model_options.d.ts.map +1 -0
- package/dist/model_options.js +59 -0
- package/dist/model_options.js.map +1 -0
- package/dist/models.d.ts +88 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +55 -0
- package/dist/models.js.map +1 -0
- package/dist/sdk.umd.js +4 -0
- package/dist/sdk.umd.js.map +1 -0
- package/dist/transport.d.ts +120 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +214 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +329 -0
- package/dist/validate.js.map +1 -0
- package/dist/video.d.ts +134 -0
- package/dist/video.d.ts.map +1 -0
- package/dist/video.js +60 -0
- package/dist/video.js.map +1 -0
- package/package.json +54 -4
- package/src/activity-tracker.ts +251 -0
- package/src/audio.ts +440 -0
- package/src/browser-auth.ts +1529 -0
- package/src/chat.ts +368 -0
- package/src/client.ts +250 -0
- package/src/computer.ts +80 -0
- package/src/embeddings.ts +97 -0
- package/src/errors.ts +29 -0
- package/src/event-wrap.ts +168 -0
- package/src/events.ts +172 -0
- package/src/files.ts +347 -0
- package/src/image.ts +339 -0
- package/src/index.ts +151 -0
- package/src/media.ts +147 -0
- package/src/model_options.ts +220 -0
- package/src/models.ts +121 -0
- package/src/transport.ts +292 -0
- package/src/types.ts +31 -0
- package/src/validate.ts +364 -0
- package/src/video.ts +215 -0
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed runtime event bus for SDK call telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Every LLM-ish service method (chat, embeddings, image, audio, video,
|
|
5
|
+
* computer) emits start / streamDelta / end / error events on the
|
|
6
|
+
* shared `client.events` bus so UI overlays (e.g. the bottom-right
|
|
7
|
+
* identity badge with its activity ring) can render real-time state
|
|
8
|
+
* without business code having to thread anything through.
|
|
9
|
+
*
|
|
10
|
+
* Goals:
|
|
11
|
+
* - **Typed**: handler signatures are inferred from the event name.
|
|
12
|
+
* - **Zero-cost when nobody listens**: no listeners → emit is a few
|
|
13
|
+
* property reads. The browser `EventTarget` does the heavy lifting.
|
|
14
|
+
* - **Service-agnostic**: events carry just enough context (id /
|
|
15
|
+
* model / capability / token counts) for a generic UI to render
|
|
16
|
+
* them without knowing which service fired the call.
|
|
17
|
+
*
|
|
18
|
+
* Why a custom wrapper instead of `EventTarget` directly:
|
|
19
|
+
* - The native API is untyped (`addEventListener("foo", anyHandler)`).
|
|
20
|
+
* - Browser CustomEvent's `detail` payload trick is awkward to read.
|
|
21
|
+
* - Subscribers want an `off()` closure, not a separate
|
|
22
|
+
* `removeEventListener` call with the same callback reference.
|
|
23
|
+
*/
|
|
24
|
+
/** Capability bucket of the calling service, mirroring /v1/models. */
|
|
25
|
+
export type SDKCapability = "text" | "vision" | "embedding" | "image_generation" | "image_edit" | "text_to_speech" | "speech_to_text" | "video_generation" | "computer_use";
|
|
26
|
+
/** Fired the moment a request hits the wire. */
|
|
27
|
+
export interface RequestStartEvent {
|
|
28
|
+
/** Stable id correlating start → end/error events for one call. */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Model name the caller asked for (NOT the upstream id). */
|
|
31
|
+
model: string;
|
|
32
|
+
/** Capability bucket — drives badge icon / colour. */
|
|
33
|
+
capability: SDKCapability;
|
|
34
|
+
/** ms since epoch when the request left the SDK. */
|
|
35
|
+
startedAt: number;
|
|
36
|
+
/** `true` for stream:true requests so the UI can pre-pick the
|
|
37
|
+
* pulsing-ring animation instead of swapping mid-call. */
|
|
38
|
+
streaming: boolean;
|
|
39
|
+
}
|
|
40
|
+
/** Fired on every stream chunk that carried new content tokens. */
|
|
41
|
+
export interface RequestStreamDeltaEvent {
|
|
42
|
+
/** Matches the corresponding RequestStartEvent.id. */
|
|
43
|
+
id: string;
|
|
44
|
+
/** Approximate completion tokens in THIS chunk (one delta event
|
|
45
|
+
* per visible SSE chunk; usage roll-up arrives in end). */
|
|
46
|
+
deltaTokens: number;
|
|
47
|
+
/** Cumulative completion tokens since stream start. */
|
|
48
|
+
totalTokens: number;
|
|
49
|
+
}
|
|
50
|
+
/** Fired once when the call settles successfully. */
|
|
51
|
+
export interface RequestEndEvent {
|
|
52
|
+
id: string;
|
|
53
|
+
model: string;
|
|
54
|
+
capability: SDKCapability;
|
|
55
|
+
/** Wall-clock from start to settle. */
|
|
56
|
+
latencyMs: number;
|
|
57
|
+
/** Final completion tokens (matches the gateway's usage.completion_tokens
|
|
58
|
+
* when the response carries it; 0 otherwise). */
|
|
59
|
+
totalTokens: number;
|
|
60
|
+
/** USD cost as reported by the gateway's `usage.total_cost`. May be
|
|
61
|
+
* undefined for endpoints that don't return cost (rare). */
|
|
62
|
+
costUSD?: number;
|
|
63
|
+
/** OpenAI-style stop / length / tool_calls / content_filter, or
|
|
64
|
+
* undefined for non-chat endpoints. */
|
|
65
|
+
finishReason?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Fired once when the call fails — at start, mid-stream, or anywhere. */
|
|
68
|
+
export interface RequestErrorEvent {
|
|
69
|
+
id: string;
|
|
70
|
+
model: string;
|
|
71
|
+
capability: SDKCapability;
|
|
72
|
+
latencyMs: number;
|
|
73
|
+
/** The thrown Error (typically `APIError` with statusCode / body). */
|
|
74
|
+
error: Error;
|
|
75
|
+
/** HTTP status when known (forwarded from APIError when present). */
|
|
76
|
+
statusCode?: number;
|
|
77
|
+
}
|
|
78
|
+
/** Compile-time map of event name → payload shape. Drives handler
|
|
79
|
+
* inference in `on()` / `emit()` so callers can't subscribe to a
|
|
80
|
+
* typo'd name or read a non-existent field. */
|
|
81
|
+
export interface SDKEventMap {
|
|
82
|
+
"request:start": RequestStartEvent;
|
|
83
|
+
"request:streamDelta": RequestStreamDeltaEvent;
|
|
84
|
+
"request:end": RequestEndEvent;
|
|
85
|
+
"request:error": RequestErrorEvent;
|
|
86
|
+
}
|
|
87
|
+
/** Disposer returned by `on()` — call to unsubscribe. */
|
|
88
|
+
export type Unsubscribe = () => void;
|
|
89
|
+
/**
|
|
90
|
+
* Lightweight typed event bus. Backed by `EventTarget` so memory
|
|
91
|
+
* leaks from un-disposed listeners surface in the standard DevTools
|
|
92
|
+
* memory profile path. In Node we fall back to a plain Map<string,
|
|
93
|
+
* Set<handler>> since `EventTarget` was only added in Node 15+ and we
|
|
94
|
+
* still support 14 in places (the SDK targets 18+ but tests sometimes
|
|
95
|
+
* stub globals).
|
|
96
|
+
*/
|
|
97
|
+
export declare class SDKEvents {
|
|
98
|
+
private readonly handlers;
|
|
99
|
+
/**
|
|
100
|
+
* Subscribe to an event. Returns a disposer; calling the disposer
|
|
101
|
+
* removes the subscription. Multiple subscriptions to the same
|
|
102
|
+
* event are supported.
|
|
103
|
+
*/
|
|
104
|
+
on<K extends keyof SDKEventMap>(name: K, handler: (event: SDKEventMap[K]) => void): Unsubscribe;
|
|
105
|
+
/**
|
|
106
|
+
* Fire an event. Handlers run synchronously in subscription order.
|
|
107
|
+
* Throws inside one handler don't prevent the others from running —
|
|
108
|
+
* the bus catches and logs (console.error) so a buggy listener can't
|
|
109
|
+
* take down the rest of the UI.
|
|
110
|
+
*/
|
|
111
|
+
emit<K extends keyof SDKEventMap>(name: K, payload: SDKEventMap[K]): void;
|
|
112
|
+
/** Drop all subscriptions. Tests call this between cases. */
|
|
113
|
+
clear(): void;
|
|
114
|
+
}
|
|
115
|
+
/** Generate a short opaque ID for correlating start/end events.
|
|
116
|
+
* Not a UUID — sub-microsecond uniqueness within a session is
|
|
117
|
+
* enough, and we want to keep the wire log compact. */
|
|
118
|
+
export declare function newRequestId(): string;
|
|
119
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,sEAAsE;AACtE,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,QAAQ,GACR,WAAW,GACX,kBAAkB,GAClB,YAAY,GACZ,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,GAClB,cAAc,CAAC;AAEnB,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,mEAAmE;IACnE,EAAE,EAAE,MAAM,CAAC;IACX,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,UAAU,EAAE,aAAa,CAAC;IAC1B,oDAAoD;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB;+DAC2D;IAC3D,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,mEAAmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,sDAAsD;IACtD,EAAE,EAAE,MAAM,CAAC;IACX;gEAC4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB;sDACkD;IAClD,WAAW,EAAE,MAAM,CAAC;IACpB;iEAC6D;IAC7D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;4CACwC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,aAAa,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,KAAK,EAAE,KAAK,CAAC;IACb,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;gDAEgD;AAChD,MAAM,WAAW,WAAW;IAC1B,eAAe,EAAE,iBAAiB,CAAC;IACnC,qBAAqB,EAAE,uBAAuB,CAAC;IAC/C,aAAa,EAAE,eAAe,CAAC;IAC/B,eAAe,EAAE,iBAAiB,CAAC;CACpC;AAED,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC;;;;;;;GAOG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiE;IAE1F;;;;OAIG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,WAAW;IAa/F;;;;;OAKG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;IAezE,6DAA6D;IAC7D,KAAK,IAAI,IAAI;CAGd;AAED;;wDAEwD;AACxD,wBAAgB,YAAY,IAAI,MAAM,CAOrC"}
|
package/dist/events.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed runtime event bus for SDK call telemetry.
|
|
3
|
+
*
|
|
4
|
+
* Every LLM-ish service method (chat, embeddings, image, audio, video,
|
|
5
|
+
* computer) emits start / streamDelta / end / error events on the
|
|
6
|
+
* shared `client.events` bus so UI overlays (e.g. the bottom-right
|
|
7
|
+
* identity badge with its activity ring) can render real-time state
|
|
8
|
+
* without business code having to thread anything through.
|
|
9
|
+
*
|
|
10
|
+
* Goals:
|
|
11
|
+
* - **Typed**: handler signatures are inferred from the event name.
|
|
12
|
+
* - **Zero-cost when nobody listens**: no listeners → emit is a few
|
|
13
|
+
* property reads. The browser `EventTarget` does the heavy lifting.
|
|
14
|
+
* - **Service-agnostic**: events carry just enough context (id /
|
|
15
|
+
* model / capability / token counts) for a generic UI to render
|
|
16
|
+
* them without knowing which service fired the call.
|
|
17
|
+
*
|
|
18
|
+
* Why a custom wrapper instead of `EventTarget` directly:
|
|
19
|
+
* - The native API is untyped (`addEventListener("foo", anyHandler)`).
|
|
20
|
+
* - Browser CustomEvent's `detail` payload trick is awkward to read.
|
|
21
|
+
* - Subscribers want an `off()` closure, not a separate
|
|
22
|
+
* `removeEventListener` call with the same callback reference.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Lightweight typed event bus. Backed by `EventTarget` so memory
|
|
26
|
+
* leaks from un-disposed listeners surface in the standard DevTools
|
|
27
|
+
* memory profile path. In Node we fall back to a plain Map<string,
|
|
28
|
+
* Set<handler>> since `EventTarget` was only added in Node 15+ and we
|
|
29
|
+
* still support 14 in places (the SDK targets 18+ but tests sometimes
|
|
30
|
+
* stub globals).
|
|
31
|
+
*/
|
|
32
|
+
export class SDKEvents {
|
|
33
|
+
handlers = new Map();
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to an event. Returns a disposer; calling the disposer
|
|
36
|
+
* removes the subscription. Multiple subscriptions to the same
|
|
37
|
+
* event are supported.
|
|
38
|
+
*/
|
|
39
|
+
on(name, handler) {
|
|
40
|
+
let set = this.handlers.get(name);
|
|
41
|
+
if (!set) {
|
|
42
|
+
set = new Set();
|
|
43
|
+
this.handlers.set(name, set);
|
|
44
|
+
}
|
|
45
|
+
const wrapped = handler;
|
|
46
|
+
set.add(wrapped);
|
|
47
|
+
return () => {
|
|
48
|
+
this.handlers.get(name)?.delete(wrapped);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Fire an event. Handlers run synchronously in subscription order.
|
|
53
|
+
* Throws inside one handler don't prevent the others from running —
|
|
54
|
+
* the bus catches and logs (console.error) so a buggy listener can't
|
|
55
|
+
* take down the rest of the UI.
|
|
56
|
+
*/
|
|
57
|
+
emit(name, payload) {
|
|
58
|
+
const set = this.handlers.get(name);
|
|
59
|
+
if (!set || set.size === 0)
|
|
60
|
+
return;
|
|
61
|
+
for (const h of set) {
|
|
62
|
+
try {
|
|
63
|
+
h(payload);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
// Listener bug — don't propagate, but surface in the console
|
|
67
|
+
// so developers notice during integration.
|
|
68
|
+
// eslint-disable-next-line no-console
|
|
69
|
+
console.error("[runapps sdk] event listener for", name, "threw:", err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Drop all subscriptions. Tests call this between cases. */
|
|
74
|
+
clear() {
|
|
75
|
+
this.handlers.clear();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Generate a short opaque ID for correlating start/end events.
|
|
79
|
+
* Not a UUID — sub-microsecond uniqueness within a session is
|
|
80
|
+
* enough, and we want to keep the wire log compact. */
|
|
81
|
+
export function newRequestId() {
|
|
82
|
+
return ("req_" +
|
|
83
|
+
Date.now().toString(36) +
|
|
84
|
+
"_" +
|
|
85
|
+
Math.random().toString(36).slice(2, 8));
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAmFH;;;;;;;GAOG;AACH,MAAM,OAAO,SAAS;IACH,QAAQ,GAAG,IAAI,GAAG,EAAsD,CAAC;IAE1F;;;;OAIG;IACH,EAAE,CAA8B,IAAO,EAAE,OAAwC;QAC/E,IAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;QACD,MAAM,OAAO,GAAG,OAAqC,CAAC;QACtD,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,IAAI,CAA8B,IAAO,EAAE,OAAuB;QAChE,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QACnC,KAAK,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;YACpB,IAAI,CAAC;gBACH,CAAC,CAAC,OAAO,CAAC,CAAC;YACb,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,6DAA6D;gBAC7D,2CAA2C;gBAC3C,sCAAsC;gBACtC,OAAO,CAAC,KAAK,CAAC,kCAAkC,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF;AAED;;wDAEwD;AACxD,MAAM,UAAU,YAAY;IAC1B,OAAO,CACL,MAAM;QACN,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvB,GAAG;QACH,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CACvC,CAAC;AACJ,CAAC"}
|
package/dist/files.d.ts
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { Transport } from "./transport.js";
|
|
2
|
+
export interface FileObject {
|
|
3
|
+
path: string;
|
|
4
|
+
size: number;
|
|
5
|
+
content_type?: string;
|
|
6
|
+
etag?: string;
|
|
7
|
+
last_modified?: string;
|
|
8
|
+
url: string;
|
|
9
|
+
}
|
|
10
|
+
export interface FileListResult {
|
|
11
|
+
files: FileObject[];
|
|
12
|
+
next_cursor?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface PutOptions {
|
|
15
|
+
/** Override the MIME type guessed from the path extension. */
|
|
16
|
+
contentType?: string;
|
|
17
|
+
/** Refuse to overwrite an existing object (gateway returns 409). */
|
|
18
|
+
ifNoneMatch?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Write only if the stored object still has this ETag — the one
|
|
21
|
+
* `read()` / `stat()` / a previous `put()` returned. Someone else
|
|
22
|
+
* having written in between makes the gateway answer 412 instead
|
|
23
|
+
* of silently overwriting their version; re-read, merge, put again.
|
|
24
|
+
*/
|
|
25
|
+
ifMatch?: string;
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
/** The bytes at a path together with the version they are. */
|
|
29
|
+
export interface FileRead {
|
|
30
|
+
blob: Blob;
|
|
31
|
+
contentType: string;
|
|
32
|
+
/** Pass back as `ifMatch` to write only if nobody else has since. */
|
|
33
|
+
etag?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface ListOptions {
|
|
36
|
+
prefix?: string;
|
|
37
|
+
cursor?: string;
|
|
38
|
+
limit?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Shell-style pattern, post-filtered after the underlying S3 list:
|
|
41
|
+
* * any chars except `/`
|
|
42
|
+
* ** any chars including `/`
|
|
43
|
+
* ? single non-slash char
|
|
44
|
+
* [abc] character class
|
|
45
|
+
*
|
|
46
|
+
* Examples:
|
|
47
|
+
* client.files.list({ glob: "*.png" })
|
|
48
|
+
* client.files.list({ glob: "projects/*\/assets/*.png" })
|
|
49
|
+
* client.files.list({ prefix: "projects/", glob: "**\/*.wav" })
|
|
50
|
+
*
|
|
51
|
+
* S3 has no native glob — the gateway extracts the longest literal
|
|
52
|
+
* head of the pattern as the underlying prefix scan, then filters
|
|
53
|
+
* the rest in process. Patterns must match the WHOLE path; no
|
|
54
|
+
* implicit anchoring needed.
|
|
55
|
+
*/
|
|
56
|
+
glob?: string;
|
|
57
|
+
signal?: AbortSignal;
|
|
58
|
+
}
|
|
59
|
+
export interface PutFromURLOptions {
|
|
60
|
+
contentType?: string;
|
|
61
|
+
ifNoneMatch?: boolean;
|
|
62
|
+
ifMatch?: string;
|
|
63
|
+
signal?: AbortSignal;
|
|
64
|
+
}
|
|
65
|
+
export type BatchOp = {
|
|
66
|
+
op: "put_url";
|
|
67
|
+
path: string;
|
|
68
|
+
src_url: string;
|
|
69
|
+
content_type?: string;
|
|
70
|
+
if_none_match?: boolean;
|
|
71
|
+
if_match?: string;
|
|
72
|
+
} | {
|
|
73
|
+
op: "del";
|
|
74
|
+
path: string;
|
|
75
|
+
} | {
|
|
76
|
+
op: "move";
|
|
77
|
+
from: string;
|
|
78
|
+
to: string;
|
|
79
|
+
} | {
|
|
80
|
+
op: "copy";
|
|
81
|
+
from: string;
|
|
82
|
+
to: string;
|
|
83
|
+
} | {
|
|
84
|
+
op: "exists";
|
|
85
|
+
path: string;
|
|
86
|
+
} | {
|
|
87
|
+
op: "stat";
|
|
88
|
+
path: string;
|
|
89
|
+
};
|
|
90
|
+
export interface BatchResult {
|
|
91
|
+
ok: boolean;
|
|
92
|
+
error?: string;
|
|
93
|
+
object?: FileObject;
|
|
94
|
+
exists?: boolean;
|
|
95
|
+
}
|
|
96
|
+
export declare class FilesService {
|
|
97
|
+
private readonly transport;
|
|
98
|
+
/**
|
|
99
|
+
* URL prefix for every file call.
|
|
100
|
+
*
|
|
101
|
+
* When the client was constructed with `project`, requests go to the
|
|
102
|
+
* project-scoped form — `/v1/p/<project>/files/...` — instead of the
|
|
103
|
+
* bare `/v1/files/...`.
|
|
104
|
+
*
|
|
105
|
+
* This matters because the unscoped URL is byte-identical across apps:
|
|
106
|
+
* two bundles built from the same template both read
|
|
107
|
+
* `projects/index.json`, so anything keying on URL alone (starting
|
|
108
|
+
* with the browser's HTTP cache, which ignores request headers and is
|
|
109
|
+
* partitioned by registrable domain, not by subdomain) cannot tell
|
|
110
|
+
* them apart. Putting the project in the path makes the URL unique
|
|
111
|
+
* per app. The gateway still authorizes purely from the token and
|
|
112
|
+
* rejects a scope that disagrees with it, so this is a correctness
|
|
113
|
+
* measure, not a permission one.
|
|
114
|
+
*/
|
|
115
|
+
private readonly base;
|
|
116
|
+
constructor(transport: Transport, project?: string);
|
|
117
|
+
/**
|
|
118
|
+
* Upload `body` to `path`. Returns the resulting FileObject — its
|
|
119
|
+
* `url` is the stable public address for that path.
|
|
120
|
+
*
|
|
121
|
+
* `body` accepts anything `fetch` accepts as a request body: Blob,
|
|
122
|
+
* File, ArrayBuffer, ArrayBufferView, FormData (rare here),
|
|
123
|
+
* ReadableStream, or a string.
|
|
124
|
+
*/
|
|
125
|
+
put(path: string, body: BodyInit, opts?: PutOptions): Promise<FileObject>;
|
|
126
|
+
/** Convenience: upload string content with sensible content-type. */
|
|
127
|
+
putString(path: string, content: string, opts?: PutOptions): Promise<FileObject>;
|
|
128
|
+
/**
|
|
129
|
+
* Download the bytes at `path` as a Blob. Use `URL.createObjectURL`
|
|
130
|
+
* to get a temporary local URL, or persist the bytes elsewhere.
|
|
131
|
+
* Bundles that just need to render an asset should use the FileObject's
|
|
132
|
+
* `.url` directly instead — that's a stable public URL with no token.
|
|
133
|
+
*/
|
|
134
|
+
get(path: string, init?: {
|
|
135
|
+
signal?: AbortSignal;
|
|
136
|
+
}): Promise<Blob>;
|
|
137
|
+
/**
|
|
138
|
+
* Like `get`, but also returns the ETag of the bytes, so a later
|
|
139
|
+
* `put(path, body, { ifMatch: etag })` writes only if the file is
|
|
140
|
+
* still the version that was read.
|
|
141
|
+
*/
|
|
142
|
+
read(path: string, init?: {
|
|
143
|
+
signal?: AbortSignal;
|
|
144
|
+
}): Promise<FileRead>;
|
|
145
|
+
/**
|
|
146
|
+
* Read a JSON file and its version in one call. A missing file is
|
|
147
|
+
* `{ value: undefined, etag: undefined }`, so a first save can pass
|
|
148
|
+
* `ifNoneMatch: true` and a later one `ifMatch: etag`.
|
|
149
|
+
*/
|
|
150
|
+
readJSON<T = unknown>(path: string, init?: {
|
|
151
|
+
signal?: AbortSignal;
|
|
152
|
+
}): Promise<{
|
|
153
|
+
value: T | undefined;
|
|
154
|
+
etag?: string;
|
|
155
|
+
}>;
|
|
156
|
+
/** Stat: HEAD-only object metadata. */
|
|
157
|
+
stat(path: string, init?: {
|
|
158
|
+
signal?: AbortSignal;
|
|
159
|
+
}): Promise<FileObject>;
|
|
160
|
+
/** Returns true iff the path resolves to an existing object. */
|
|
161
|
+
exists(path: string, init?: {
|
|
162
|
+
signal?: AbortSignal;
|
|
163
|
+
}): Promise<boolean>;
|
|
164
|
+
/** Delete the object at `path`. Idempotent. */
|
|
165
|
+
del(path: string, init?: {
|
|
166
|
+
signal?: AbortSignal;
|
|
167
|
+
}): Promise<void>;
|
|
168
|
+
/** List objects. Pass an empty / omitted prefix for the root. */
|
|
169
|
+
list(opts?: ListOptions): Promise<FileListResult>;
|
|
170
|
+
/**
|
|
171
|
+
* Bulk delete: removes every object whose path starts with `prefix`
|
|
172
|
+
* AND (when set) matches `glob`. Returns the count of deleted
|
|
173
|
+
* objects. Refuses an empty prefix + empty glob — the caller must
|
|
174
|
+
* be explicit about wiping the project namespace.
|
|
175
|
+
*
|
|
176
|
+
* client.files.deleteMany({ prefix: "tmp/" }) // wipe a directory
|
|
177
|
+
* client.files.deleteMany({ glob: "**\/*.tmp" }) // wipe by pattern
|
|
178
|
+
* client.files.deleteMany({ prefix: "logs/", glob: "*.bak" })
|
|
179
|
+
*/
|
|
180
|
+
deleteMany(opts: {
|
|
181
|
+
prefix?: string;
|
|
182
|
+
glob?: string;
|
|
183
|
+
}, init?: {
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
}): Promise<{
|
|
186
|
+
deleted: number;
|
|
187
|
+
}>;
|
|
188
|
+
/** Atomic rename (copy + delete server-side). */
|
|
189
|
+
move(from: string, to: string, init?: {
|
|
190
|
+
signal?: AbortSignal;
|
|
191
|
+
}): Promise<FileObject>;
|
|
192
|
+
/** Server-side copy. */
|
|
193
|
+
copy(from: string, to: string, init?: {
|
|
194
|
+
signal?: AbortSignal;
|
|
195
|
+
}): Promise<FileObject>;
|
|
196
|
+
/**
|
|
197
|
+
* Ask the gateway to fetch `srcURL` and store the body at `path`.
|
|
198
|
+
* Saves a round trip vs downloading then re-uploading. `srcURL`
|
|
199
|
+
* must be a public http(s) URL (private/loopback hosts rejected).
|
|
200
|
+
*/
|
|
201
|
+
putFromURL(path: string, srcURL: string, opts?: PutFromURLOptions): Promise<FileObject>;
|
|
202
|
+
/**
|
|
203
|
+
* Pipeline several file ops in one round trip. Operations execute
|
|
204
|
+
* sequentially server-side; one op failing does not abort the rest.
|
|
205
|
+
* Inspect each result.ok individually.
|
|
206
|
+
*
|
|
207
|
+
* Auto-chunks: the gateway caps a single batch at 64 ops and returns
|
|
208
|
+
* 400 "too many ops" above that. This method splits long input into
|
|
209
|
+
* 30-op chunks, awaits each chunk in order, and concatenates the
|
|
210
|
+
* results — so callers can hand it 1000 ops in a single call.
|
|
211
|
+
* Chunks run serially (not in parallel) to preserve the
|
|
212
|
+
* "operations execute in submission order" contract.
|
|
213
|
+
*/
|
|
214
|
+
batch(ops: BatchOp[], init?: {
|
|
215
|
+
signal?: AbortSignal;
|
|
216
|
+
}): Promise<BatchResult[]>;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
|
package/dist/files.js
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
// Per-project file system at /v1/files/*. Files are stored under
|
|
2
|
+
// (user, project) on the gateway and addressed by POSIX-style paths.
|
|
3
|
+
// Each FileObject's `url` is a stable public address — embed it
|
|
4
|
+
// directly in `<img src>`, share it, persist it.
|
|
5
|
+
//
|
|
6
|
+
// The bundle's RunApps client uses `client.files` to access these
|
|
7
|
+
// endpoints; the underlying token must be a project-bound resource
|
|
8
|
+
// token (rrt_*) issued by the grant flow.
|
|
9
|
+
function pathToURL(base, p) {
|
|
10
|
+
const trimmed = p.replace(/^\/+/, "");
|
|
11
|
+
return base + "/" + trimmed.split("/").map(encodeURIComponent).join("/");
|
|
12
|
+
}
|
|
13
|
+
export class FilesService {
|
|
14
|
+
transport;
|
|
15
|
+
/**
|
|
16
|
+
* URL prefix for every file call.
|
|
17
|
+
*
|
|
18
|
+
* When the client was constructed with `project`, requests go to the
|
|
19
|
+
* project-scoped form — `/v1/p/<project>/files/...` — instead of the
|
|
20
|
+
* bare `/v1/files/...`.
|
|
21
|
+
*
|
|
22
|
+
* This matters because the unscoped URL is byte-identical across apps:
|
|
23
|
+
* two bundles built from the same template both read
|
|
24
|
+
* `projects/index.json`, so anything keying on URL alone (starting
|
|
25
|
+
* with the browser's HTTP cache, which ignores request headers and is
|
|
26
|
+
* partitioned by registrable domain, not by subdomain) cannot tell
|
|
27
|
+
* them apart. Putting the project in the path makes the URL unique
|
|
28
|
+
* per app. The gateway still authorizes purely from the token and
|
|
29
|
+
* rejects a scope that disagrees with it, so this is a correctness
|
|
30
|
+
* measure, not a permission one.
|
|
31
|
+
*/
|
|
32
|
+
base;
|
|
33
|
+
constructor(transport, project) {
|
|
34
|
+
this.transport = transport;
|
|
35
|
+
this.base = project
|
|
36
|
+
? "/v1/p/" + encodeURIComponent(project) + "/files"
|
|
37
|
+
: "/v1/files";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Upload `body` to `path`. Returns the resulting FileObject — its
|
|
41
|
+
* `url` is the stable public address for that path.
|
|
42
|
+
*
|
|
43
|
+
* `body` accepts anything `fetch` accepts as a request body: Blob,
|
|
44
|
+
* File, ArrayBuffer, ArrayBufferView, FormData (rare here),
|
|
45
|
+
* ReadableStream, or a string.
|
|
46
|
+
*/
|
|
47
|
+
async put(path, body, opts) {
|
|
48
|
+
const headers = {};
|
|
49
|
+
if (opts?.ifNoneMatch)
|
|
50
|
+
headers["If-None-Match"] = "*";
|
|
51
|
+
if (opts?.ifMatch)
|
|
52
|
+
headers["If-Match"] = `"${opts.ifMatch.replace(/"/g, "")}"`;
|
|
53
|
+
return this.transport.putBytes(pathToURL(this.base, path), body, {
|
|
54
|
+
contentType: opts?.contentType,
|
|
55
|
+
headers: Object.keys(headers).length ? headers : undefined,
|
|
56
|
+
signal: opts?.signal,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Convenience: upload string content with sensible content-type. */
|
|
60
|
+
async putString(path, content, opts) {
|
|
61
|
+
return this.put(path, content, {
|
|
62
|
+
...opts,
|
|
63
|
+
contentType: opts?.contentType ?? "text/plain; charset=utf-8",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Download the bytes at `path` as a Blob. Use `URL.createObjectURL`
|
|
68
|
+
* to get a temporary local URL, or persist the bytes elsewhere.
|
|
69
|
+
* Bundles that just need to render an asset should use the FileObject's
|
|
70
|
+
* `.url` directly instead — that's a stable public URL with no token.
|
|
71
|
+
*/
|
|
72
|
+
async get(path, init) {
|
|
73
|
+
return (await this.read(path, init)).blob;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Like `get`, but also returns the ETag of the bytes, so a later
|
|
77
|
+
* `put(path, body, { ifMatch: etag })` writes only if the file is
|
|
78
|
+
* still the version that was read.
|
|
79
|
+
*/
|
|
80
|
+
async read(path, init) {
|
|
81
|
+
const { data, contentType, etag } = await this.transport.getRaw(pathToURL(this.base, path), init);
|
|
82
|
+
return {
|
|
83
|
+
blob: new Blob([new Uint8Array(data)], { type: contentType || "application/octet-stream" }),
|
|
84
|
+
contentType,
|
|
85
|
+
etag,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Read a JSON file and its version in one call. A missing file is
|
|
90
|
+
* `{ value: undefined, etag: undefined }`, so a first save can pass
|
|
91
|
+
* `ifNoneMatch: true` and a later one `ifMatch: etag`.
|
|
92
|
+
*/
|
|
93
|
+
async readJSON(path, init) {
|
|
94
|
+
let r;
|
|
95
|
+
try {
|
|
96
|
+
r = await this.read(path, init);
|
|
97
|
+
}
|
|
98
|
+
catch (e) {
|
|
99
|
+
if (e?.statusCode === 404 ||
|
|
100
|
+
e?.status === 404) {
|
|
101
|
+
return { value: undefined, etag: undefined };
|
|
102
|
+
}
|
|
103
|
+
throw e;
|
|
104
|
+
}
|
|
105
|
+
return { value: JSON.parse(await r.blob.text()), etag: r.etag };
|
|
106
|
+
}
|
|
107
|
+
/** Stat: HEAD-only object metadata. */
|
|
108
|
+
async stat(path, init) {
|
|
109
|
+
const { status, headers } = await this.transport.head(pathToURL(this.base, path), init);
|
|
110
|
+
if (status === 404) {
|
|
111
|
+
throw Object.assign(new Error("not found"), { status: 404 });
|
|
112
|
+
}
|
|
113
|
+
if (status >= 400) {
|
|
114
|
+
throw Object.assign(new Error("stat failed"), { status });
|
|
115
|
+
}
|
|
116
|
+
const size = parseInt(headers.get("content-length") ?? "0", 10);
|
|
117
|
+
return {
|
|
118
|
+
path: headers.get("x-file-path") ?? path,
|
|
119
|
+
size,
|
|
120
|
+
content_type: headers.get("content-type") ?? undefined,
|
|
121
|
+
etag: (headers.get("etag") ?? "").replace(/"/g, "") || undefined,
|
|
122
|
+
url: headers.get("x-file-url") ?? "",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/** Returns true iff the path resolves to an existing object. */
|
|
126
|
+
async exists(path, init) {
|
|
127
|
+
const { status } = await this.transport.head(pathToURL(this.base, path), init);
|
|
128
|
+
return status >= 200 && status < 400;
|
|
129
|
+
}
|
|
130
|
+
/** Delete the object at `path`. Idempotent. */
|
|
131
|
+
async del(path, init) {
|
|
132
|
+
await this.transport.deletePath(pathToURL(this.base, path), init);
|
|
133
|
+
}
|
|
134
|
+
/** List objects. Pass an empty / omitted prefix for the root. */
|
|
135
|
+
async list(opts) {
|
|
136
|
+
const params = new URLSearchParams();
|
|
137
|
+
if (opts?.prefix)
|
|
138
|
+
params.set("prefix", opts.prefix);
|
|
139
|
+
if (opts?.cursor)
|
|
140
|
+
params.set("cursor", opts.cursor);
|
|
141
|
+
if (opts?.limit)
|
|
142
|
+
params.set("limit", String(opts.limit));
|
|
143
|
+
if (opts?.glob)
|
|
144
|
+
params.set("glob", opts.glob);
|
|
145
|
+
const qs = params.toString();
|
|
146
|
+
return this.transport.getJSON(this.base + (qs ? "?" + qs : ""), opts?.signal ? { signal: opts.signal } : undefined);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Bulk delete: removes every object whose path starts with `prefix`
|
|
150
|
+
* AND (when set) matches `glob`. Returns the count of deleted
|
|
151
|
+
* objects. Refuses an empty prefix + empty glob — the caller must
|
|
152
|
+
* be explicit about wiping the project namespace.
|
|
153
|
+
*
|
|
154
|
+
* client.files.deleteMany({ prefix: "tmp/" }) // wipe a directory
|
|
155
|
+
* client.files.deleteMany({ glob: "**\/*.tmp" }) // wipe by pattern
|
|
156
|
+
* client.files.deleteMany({ prefix: "logs/", glob: "*.bak" })
|
|
157
|
+
*/
|
|
158
|
+
async deleteMany(opts, init) {
|
|
159
|
+
return this.transport.postJSON(this.base + "/delete", { prefix: opts.prefix ?? "", glob: opts.glob ?? "" }, init);
|
|
160
|
+
}
|
|
161
|
+
/** Atomic rename (copy + delete server-side). */
|
|
162
|
+
async move(from, to, init) {
|
|
163
|
+
return this.transport.postJSON(this.base + "/move", { from, to }, init);
|
|
164
|
+
}
|
|
165
|
+
/** Server-side copy. */
|
|
166
|
+
async copy(from, to, init) {
|
|
167
|
+
return this.transport.postJSON(this.base + "/copy", { from, to }, init);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Ask the gateway to fetch `srcURL` and store the body at `path`.
|
|
171
|
+
* Saves a round trip vs downloading then re-uploading. `srcURL`
|
|
172
|
+
* must be a public http(s) URL (private/loopback hosts rejected).
|
|
173
|
+
*/
|
|
174
|
+
async putFromURL(path, srcURL, opts) {
|
|
175
|
+
return this.transport.postJSON(this.base + "/put-url", {
|
|
176
|
+
path,
|
|
177
|
+
src_url: srcURL,
|
|
178
|
+
content_type: opts?.contentType,
|
|
179
|
+
if_none_match: opts?.ifNoneMatch,
|
|
180
|
+
if_match: opts?.ifMatch,
|
|
181
|
+
}, opts?.signal ? { signal: opts.signal } : undefined);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Pipeline several file ops in one round trip. Operations execute
|
|
185
|
+
* sequentially server-side; one op failing does not abort the rest.
|
|
186
|
+
* Inspect each result.ok individually.
|
|
187
|
+
*
|
|
188
|
+
* Auto-chunks: the gateway caps a single batch at 64 ops and returns
|
|
189
|
+
* 400 "too many ops" above that. This method splits long input into
|
|
190
|
+
* 30-op chunks, awaits each chunk in order, and concatenates the
|
|
191
|
+
* results — so callers can hand it 1000 ops in a single call.
|
|
192
|
+
* Chunks run serially (not in parallel) to preserve the
|
|
193
|
+
* "operations execute in submission order" contract.
|
|
194
|
+
*/
|
|
195
|
+
async batch(ops, init) {
|
|
196
|
+
if (ops.length === 0)
|
|
197
|
+
return [];
|
|
198
|
+
const chunkSize = 30;
|
|
199
|
+
if (ops.length <= chunkSize) {
|
|
200
|
+
const resp = await this.transport.postJSON(this.base + "/batch", { ops }, init);
|
|
201
|
+
return resp.results;
|
|
202
|
+
}
|
|
203
|
+
const all = [];
|
|
204
|
+
for (let i = 0; i < ops.length; i += chunkSize) {
|
|
205
|
+
const chunk = ops.slice(i, i + chunkSize);
|
|
206
|
+
const resp = await this.transport.postJSON(this.base + "/batch", { ops: chunk }, init);
|
|
207
|
+
all.push(...resp.results);
|
|
208
|
+
}
|
|
209
|
+
return all;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
|