@alienplatform/sdk 1.3.3
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/.turbo/turbo-build.log +27 -0
- package/AGENTS.md +70 -0
- package/LICENSE.md +105 -0
- package/dist/bindings/artifact-registry.d.ts +88 -0
- package/dist/bindings/artifact-registry.d.ts.map +1 -0
- package/dist/bindings/build.d.ts +67 -0
- package/dist/bindings/build.d.ts.map +1 -0
- package/dist/bindings/function.d.ts +72 -0
- package/dist/bindings/function.d.ts.map +1 -0
- package/dist/bindings/index.d.ts +12 -0
- package/dist/bindings/index.d.ts.map +1 -0
- package/dist/bindings/kv.d.ts +113 -0
- package/dist/bindings/kv.d.ts.map +1 -0
- package/dist/bindings/queue.d.ts +81 -0
- package/dist/bindings/queue.d.ts.map +1 -0
- package/dist/bindings/service-account.d.ts +46 -0
- package/dist/bindings/service-account.d.ts.map +1 -0
- package/dist/bindings/storage.d.ts +164 -0
- package/dist/bindings/storage.d.ts.map +1 -0
- package/dist/bindings/vault.d.ts +65 -0
- package/dist/bindings/vault.d.ts.map +1 -0
- package/dist/channel.d.ts +39 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/commands/client.d.ts +47 -0
- package/dist/commands/client.d.ts.map +1 -0
- package/dist/commands/errors.d.ts +198 -0
- package/dist/commands/errors.d.ts.map +1 -0
- package/dist/commands/index.d.ts +23 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +387 -0
- package/dist/commands/types.d.ts +40 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands.d.ts +55 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/context.d.ts +182 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/dist.js +6021 -0
- package/dist/errors.d.ts +334 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/events.d.ts +163 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/generated/artifact_registry.d.ts +292 -0
- package/dist/generated/artifact_registry.d.ts.map +1 -0
- package/dist/generated/build.d.ts +184 -0
- package/dist/generated/build.d.ts.map +1 -0
- package/dist/generated/container.d.ts +101 -0
- package/dist/generated/container.d.ts.map +1 -0
- package/dist/generated/control.d.ts +236 -0
- package/dist/generated/control.d.ts.map +1 -0
- package/dist/generated/function.d.ts +107 -0
- package/dist/generated/function.d.ts.map +1 -0
- package/dist/generated/google/protobuf/duration.d.ts +94 -0
- package/dist/generated/google/protobuf/duration.d.ts.map +1 -0
- package/dist/generated/google/protobuf/timestamp.d.ts +124 -0
- package/dist/generated/google/protobuf/timestamp.d.ts.map +1 -0
- package/dist/generated/kv.d.ts +182 -0
- package/dist/generated/kv.d.ts.map +1 -0
- package/dist/generated/queue.d.ts +127 -0
- package/dist/generated/queue.d.ts.map +1 -0
- package/dist/generated/service_account.d.ts +112 -0
- package/dist/generated/service_account.d.ts.map +1 -0
- package/dist/generated/storage.d.ts +783 -0
- package/dist/generated/storage.d.ts.map +1 -0
- package/dist/generated/vault.d.ts +107 -0
- package/dist/generated/vault.d.ts.map +1 -0
- package/dist/generated/wait_until.d.ts +149 -0
- package/dist/generated/wait_until.d.ts.map +1 -0
- package/dist/global.d.ts +208 -0
- package/dist/global.d.ts.map +1 -0
- package/dist/grpc-utils.d.ts +25 -0
- package/dist/grpc-utils.d.ts.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38713 -0
- package/dist/types.d.ts +327 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/wait-until.d.ts +115 -0
- package/dist/wait-until.d.ts.map +1 -0
- package/package.json +63 -0
- package/scripts/generate-proto.sh +46 -0
- package/src/bindings/AGENTS.md +105 -0
- package/src/bindings/artifact-registry.ts +316 -0
- package/src/bindings/build.ts +195 -0
- package/src/bindings/function.ts +164 -0
- package/src/bindings/index.ts +12 -0
- package/src/bindings/kv.ts +240 -0
- package/src/bindings/queue.ts +191 -0
- package/src/bindings/service-account.ts +113 -0
- package/src/bindings/storage.ts +535 -0
- package/src/bindings/vault.ts +133 -0
- package/src/channel.ts +102 -0
- package/src/commands/client.ts +446 -0
- package/src/commands/errors.ts +126 -0
- package/src/commands/index.ts +41 -0
- package/src/commands/types.ts +52 -0
- package/src/commands.ts +76 -0
- package/src/context.ts +368 -0
- package/src/errors.ts +259 -0
- package/src/events.ts +511 -0
- package/src/generated/artifact_registry.ts +1952 -0
- package/src/generated/build.ts +1263 -0
- package/src/generated/container.ts +485 -0
- package/src/generated/control.ts +1922 -0
- package/src/generated/function.ts +741 -0
- package/src/generated/google/protobuf/duration.ts +196 -0
- package/src/generated/google/protobuf/timestamp.ts +226 -0
- package/src/generated/kv.ts +1137 -0
- package/src/generated/queue.ts +729 -0
- package/src/generated/service_account.ts +766 -0
- package/src/generated/storage.ts +3653 -0
- package/src/generated/vault.ts +519 -0
- package/src/generated/wait_until.ts +781 -0
- package/src/global.ts +287 -0
- package/src/grpc-utils.ts +159 -0
- package/src/index.ts +154 -0
- package/src/types.ts +386 -0
- package/src/wait-until.ts +273 -0
- package/tsconfig.json +8 -0
- package/tsdown.config.ts +17 -0
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage binding implementation.
|
|
3
|
+
*
|
|
4
|
+
* Provides object storage operations with streaming support.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type Channel, createClient } from "nice-grpc"
|
|
8
|
+
import {
|
|
9
|
+
type StorageServiceClient as GeneratedClient,
|
|
10
|
+
type StorageObjectMeta as ProtoObjectMeta,
|
|
11
|
+
StorageHttpMethod,
|
|
12
|
+
StoragePutModeEnum,
|
|
13
|
+
type StoragePutMultipartChunkRequest,
|
|
14
|
+
StorageServiceDefinition,
|
|
15
|
+
} from "../generated/storage.js"
|
|
16
|
+
import { wrapGrpcCall } from "../grpc-utils.js"
|
|
17
|
+
import type {
|
|
18
|
+
SignedUrlOptions,
|
|
19
|
+
SignedUrlResult,
|
|
20
|
+
StorageGetOptions,
|
|
21
|
+
StorageGetResult,
|
|
22
|
+
StorageListResult,
|
|
23
|
+
StorageObjectMeta,
|
|
24
|
+
StoragePutOptions,
|
|
25
|
+
} from "../types.js"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Storage binding for object storage operations.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* import { storage } from "@alienplatform/sdk"
|
|
33
|
+
*
|
|
34
|
+
* const bucket = storage("my-bucket")
|
|
35
|
+
*
|
|
36
|
+
* // Upload a file
|
|
37
|
+
* await bucket.put("images/photo.jpg", imageData, { contentType: "image/jpeg" })
|
|
38
|
+
*
|
|
39
|
+
* // Download a file
|
|
40
|
+
* const result = await bucket.get("images/photo.jpg")
|
|
41
|
+
* console.log("Size:", result.meta.size)
|
|
42
|
+
*
|
|
43
|
+
* // List files
|
|
44
|
+
* for await (const file of bucket.list("images/")) {
|
|
45
|
+
* console.log(file.location)
|
|
46
|
+
* }
|
|
47
|
+
*
|
|
48
|
+
* // Generate a signed URL
|
|
49
|
+
* const { url } = await bucket.signedUrl("images/photo.jpg", { operation: "get" })
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export class Storage {
|
|
53
|
+
private readonly client: GeneratedClient
|
|
54
|
+
private readonly bindingName: string
|
|
55
|
+
|
|
56
|
+
constructor(channel: Channel, bindingName: string) {
|
|
57
|
+
this.client = createClient(StorageServiceDefinition, channel)
|
|
58
|
+
this.bindingName = bindingName
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get an object from storage.
|
|
63
|
+
*
|
|
64
|
+
* @param path - Path to the object
|
|
65
|
+
* @param options - Get options (range, conditionals)
|
|
66
|
+
* @returns Object data and metadata
|
|
67
|
+
*/
|
|
68
|
+
async get(path: string, options?: StorageGetOptions): Promise<StorageGetResult> {
|
|
69
|
+
return await wrapGrpcCall(
|
|
70
|
+
"StorageService",
|
|
71
|
+
"Get",
|
|
72
|
+
async () => {
|
|
73
|
+
const stream = this.client.get({
|
|
74
|
+
bindingName: this.bindingName,
|
|
75
|
+
path,
|
|
76
|
+
options: options
|
|
77
|
+
? {
|
|
78
|
+
ifMatch: options.ifMatch,
|
|
79
|
+
ifNoneMatch: options.ifNoneMatch,
|
|
80
|
+
ifModifiedSince: options.ifModifiedSince,
|
|
81
|
+
ifUnmodifiedSince: options.ifUnmodifiedSince,
|
|
82
|
+
range:
|
|
83
|
+
options.rangeStart !== undefined || options.rangeEnd !== undefined
|
|
84
|
+
? {
|
|
85
|
+
bounded: {
|
|
86
|
+
start: options.rangeStart ?? 0,
|
|
87
|
+
end: options.rangeEnd ?? 0,
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
: undefined,
|
|
91
|
+
head: false,
|
|
92
|
+
}
|
|
93
|
+
: undefined,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
let meta: StorageObjectMeta | undefined
|
|
97
|
+
const chunks: Uint8Array[] = []
|
|
98
|
+
|
|
99
|
+
for await (const part of stream) {
|
|
100
|
+
if (part.metadata) {
|
|
101
|
+
meta = this.fromProtoMeta(part.metadata)
|
|
102
|
+
}
|
|
103
|
+
if (part.chunkData && part.chunkData.length > 0) {
|
|
104
|
+
chunks.push(part.chunkData)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!meta) {
|
|
109
|
+
throw new Error("No metadata received from storage")
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Combine chunks
|
|
113
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0)
|
|
114
|
+
const data = new Uint8Array(totalLength)
|
|
115
|
+
let offset = 0
|
|
116
|
+
for (const chunk of chunks) {
|
|
117
|
+
data.set(chunk, offset)
|
|
118
|
+
offset += chunk.length
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return { meta, data }
|
|
122
|
+
},
|
|
123
|
+
{ bindingName: this.bindingName },
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get an object as a UTF-8 string.
|
|
129
|
+
*
|
|
130
|
+
* @param path - Path to the object
|
|
131
|
+
* @param options - Get options
|
|
132
|
+
* @returns Object content as string
|
|
133
|
+
*/
|
|
134
|
+
async getText(path: string, options?: StorageGetOptions): Promise<string> {
|
|
135
|
+
const result = await this.get(path, options)
|
|
136
|
+
return new TextDecoder().decode(result.data)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Get an object and parse as JSON.
|
|
141
|
+
*
|
|
142
|
+
* @param path - Path to the object
|
|
143
|
+
* @param options - Get options
|
|
144
|
+
* @returns Parsed JSON content
|
|
145
|
+
*/
|
|
146
|
+
async getJson<T = unknown>(path: string, options?: StorageGetOptions): Promise<T> {
|
|
147
|
+
const text = await this.getText(path, options)
|
|
148
|
+
return JSON.parse(text) as T
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Put an object to storage.
|
|
153
|
+
*
|
|
154
|
+
* @param path - Path to store the object
|
|
155
|
+
* @param data - Object data
|
|
156
|
+
* @param options - Put options (content type, metadata)
|
|
157
|
+
*/
|
|
158
|
+
async put(
|
|
159
|
+
path: string,
|
|
160
|
+
data: Uint8Array | string | object,
|
|
161
|
+
options?: StoragePutOptions,
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
let bytes: Uint8Array
|
|
164
|
+
let contentType = options?.contentType
|
|
165
|
+
|
|
166
|
+
if (typeof data === "string") {
|
|
167
|
+
bytes = new TextEncoder().encode(data)
|
|
168
|
+
contentType ??= "text/plain; charset=utf-8"
|
|
169
|
+
} else if (data instanceof Uint8Array) {
|
|
170
|
+
bytes = data
|
|
171
|
+
contentType ??= "application/octet-stream"
|
|
172
|
+
} else {
|
|
173
|
+
bytes = new TextEncoder().encode(JSON.stringify(data))
|
|
174
|
+
contentType ??= "application/json"
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Build attributes for content-type and metadata
|
|
178
|
+
const attributePairs: Array<{ key: string; value: string }> = []
|
|
179
|
+
if (contentType) {
|
|
180
|
+
attributePairs.push({ key: "content-type", value: contentType })
|
|
181
|
+
}
|
|
182
|
+
if (options?.metadata) {
|
|
183
|
+
for (const [key, value] of Object.entries(options.metadata)) {
|
|
184
|
+
attributePairs.push({ key: `metadata:${key}`, value })
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await wrapGrpcCall(
|
|
189
|
+
"StorageService",
|
|
190
|
+
"Put",
|
|
191
|
+
async () => {
|
|
192
|
+
await this.client.put({
|
|
193
|
+
bindingName: this.bindingName,
|
|
194
|
+
path,
|
|
195
|
+
data: bytes,
|
|
196
|
+
options: {
|
|
197
|
+
mode: options?.ifNotExists
|
|
198
|
+
? StoragePutModeEnum.PUT_MODE_CREATE
|
|
199
|
+
: StoragePutModeEnum.PUT_MODE_OVERWRITE,
|
|
200
|
+
attributes: attributePairs.length > 0 ? { pairs: attributePairs } : undefined,
|
|
201
|
+
},
|
|
202
|
+
})
|
|
203
|
+
},
|
|
204
|
+
{ bindingName: this.bindingName },
|
|
205
|
+
)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Put an object using streaming (for large files).
|
|
210
|
+
*
|
|
211
|
+
* @param path - Path to store the object
|
|
212
|
+
* @param chunks - Async iterable of data chunks
|
|
213
|
+
* @param options - Put options
|
|
214
|
+
*/
|
|
215
|
+
async putMultipart(
|
|
216
|
+
path: string,
|
|
217
|
+
chunks: AsyncIterable<Uint8Array>,
|
|
218
|
+
options?: StoragePutOptions,
|
|
219
|
+
): Promise<void> {
|
|
220
|
+
const bindingName = this.bindingName
|
|
221
|
+
|
|
222
|
+
// Build attributes for content-type and metadata
|
|
223
|
+
const attributePairs: Array<{ key: string; value: string }> = []
|
|
224
|
+
if (options?.contentType) {
|
|
225
|
+
attributePairs.push({ key: "content-type", value: options.contentType })
|
|
226
|
+
}
|
|
227
|
+
if (options?.metadata) {
|
|
228
|
+
for (const [key, value] of Object.entries(options.metadata)) {
|
|
229
|
+
attributePairs.push({ key: `metadata:${key}`, value })
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await wrapGrpcCall(
|
|
234
|
+
"StorageService",
|
|
235
|
+
"PutMultipart",
|
|
236
|
+
async () => {
|
|
237
|
+
async function* generateChunks(): AsyncIterable<StoragePutMultipartChunkRequest> {
|
|
238
|
+
// First chunk: metadata
|
|
239
|
+
yield {
|
|
240
|
+
metadata: {
|
|
241
|
+
bindingName,
|
|
242
|
+
path,
|
|
243
|
+
options:
|
|
244
|
+
attributePairs.length > 0 ? { attributes: { pairs: attributePairs } } : undefined,
|
|
245
|
+
},
|
|
246
|
+
chunkData: undefined,
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Subsequent chunks: data
|
|
250
|
+
for await (const chunk of chunks) {
|
|
251
|
+
yield {
|
|
252
|
+
metadata: undefined,
|
|
253
|
+
chunkData: chunk,
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await this.client.putMultipart(generateChunks())
|
|
259
|
+
},
|
|
260
|
+
{ bindingName: this.bindingName },
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Delete an object from storage.
|
|
266
|
+
*
|
|
267
|
+
* @param path - Path to the object
|
|
268
|
+
*/
|
|
269
|
+
async delete(path: string): Promise<void> {
|
|
270
|
+
await wrapGrpcCall(
|
|
271
|
+
"StorageService",
|
|
272
|
+
"Delete",
|
|
273
|
+
async () => {
|
|
274
|
+
await this.client.delete({
|
|
275
|
+
bindingName: this.bindingName,
|
|
276
|
+
path,
|
|
277
|
+
})
|
|
278
|
+
},
|
|
279
|
+
{ bindingName: this.bindingName },
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* List objects in storage.
|
|
285
|
+
*
|
|
286
|
+
* @param prefix - Optional prefix filter
|
|
287
|
+
* @param options - List options
|
|
288
|
+
* @returns Async iterable of object metadata
|
|
289
|
+
*/
|
|
290
|
+
async *list(prefix?: string, options?: { offset?: string }): AsyncIterable<StorageObjectMeta> {
|
|
291
|
+
const stream = this.client.list({
|
|
292
|
+
bindingName: this.bindingName,
|
|
293
|
+
prefix,
|
|
294
|
+
offset: options?.offset,
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
for await (const meta of stream) {
|
|
298
|
+
yield this.fromProtoMeta(meta)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* List objects with delimiter (for directory-like listing).
|
|
304
|
+
*
|
|
305
|
+
* @param prefix - Optional prefix filter
|
|
306
|
+
* @returns List result with objects and common prefixes
|
|
307
|
+
*/
|
|
308
|
+
async listWithDelimiter(prefix?: string): Promise<StorageListResult> {
|
|
309
|
+
return await wrapGrpcCall(
|
|
310
|
+
"StorageService",
|
|
311
|
+
"ListWithDelimiter",
|
|
312
|
+
async () => {
|
|
313
|
+
const response = await this.client.listWithDelimiter({
|
|
314
|
+
bindingName: this.bindingName,
|
|
315
|
+
prefix,
|
|
316
|
+
})
|
|
317
|
+
return {
|
|
318
|
+
commonPrefixes: response.commonPrefixes,
|
|
319
|
+
objects: response.objects.map(obj => this.fromProtoMeta(obj)),
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
{ bindingName: this.bindingName },
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Get object metadata without downloading content.
|
|
328
|
+
*
|
|
329
|
+
* @param path - Path to the object
|
|
330
|
+
* @returns Object metadata
|
|
331
|
+
*/
|
|
332
|
+
async head(path: string): Promise<StorageObjectMeta> {
|
|
333
|
+
return await wrapGrpcCall(
|
|
334
|
+
"StorageService",
|
|
335
|
+
"Head",
|
|
336
|
+
async () => {
|
|
337
|
+
const response = await this.client.head({
|
|
338
|
+
bindingName: this.bindingName,
|
|
339
|
+
path,
|
|
340
|
+
})
|
|
341
|
+
return this.fromProtoMeta(response)
|
|
342
|
+
},
|
|
343
|
+
{ bindingName: this.bindingName },
|
|
344
|
+
)
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Check if an object exists.
|
|
349
|
+
*
|
|
350
|
+
* @param path - Path to the object
|
|
351
|
+
* @returns True if the object exists
|
|
352
|
+
*/
|
|
353
|
+
async exists(path: string): Promise<boolean> {
|
|
354
|
+
try {
|
|
355
|
+
await this.head(path)
|
|
356
|
+
return true
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (error instanceof Error && "code" in error && (error as any).code === "NOT_FOUND") {
|
|
359
|
+
return false
|
|
360
|
+
}
|
|
361
|
+
throw error
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get the base directory path for this storage binding.
|
|
367
|
+
*
|
|
368
|
+
* @returns Base directory path
|
|
369
|
+
*/
|
|
370
|
+
async getBaseDir(): Promise<string> {
|
|
371
|
+
return await wrapGrpcCall(
|
|
372
|
+
"StorageService",
|
|
373
|
+
"GetBaseDir",
|
|
374
|
+
async () => {
|
|
375
|
+
const response = await this.client.getBaseDir({
|
|
376
|
+
bindingName: this.bindingName,
|
|
377
|
+
})
|
|
378
|
+
return response.path
|
|
379
|
+
},
|
|
380
|
+
{ bindingName: this.bindingName },
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Get the underlying URL for this storage binding.
|
|
386
|
+
*
|
|
387
|
+
* @returns Storage URL
|
|
388
|
+
*/
|
|
389
|
+
async getUrl(): Promise<string> {
|
|
390
|
+
return await wrapGrpcCall(
|
|
391
|
+
"StorageService",
|
|
392
|
+
"GetUrl",
|
|
393
|
+
async () => {
|
|
394
|
+
const response = await this.client.getUrl({
|
|
395
|
+
bindingName: this.bindingName,
|
|
396
|
+
})
|
|
397
|
+
return response.url
|
|
398
|
+
},
|
|
399
|
+
{ bindingName: this.bindingName },
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Copy an object within the same storage binding.
|
|
405
|
+
*
|
|
406
|
+
* @param from - Source path
|
|
407
|
+
* @param to - Destination path
|
|
408
|
+
*/
|
|
409
|
+
async copy(from: string, to: string): Promise<void> {
|
|
410
|
+
await wrapGrpcCall(
|
|
411
|
+
"StorageService",
|
|
412
|
+
"Copy",
|
|
413
|
+
async () => {
|
|
414
|
+
await this.client.copy({
|
|
415
|
+
bindingName: this.bindingName,
|
|
416
|
+
fromPath: from,
|
|
417
|
+
toPath: to,
|
|
418
|
+
})
|
|
419
|
+
},
|
|
420
|
+
{ bindingName: this.bindingName },
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Rename (move) an object within the same storage binding.
|
|
426
|
+
*
|
|
427
|
+
* @param from - Source path
|
|
428
|
+
* @param to - Destination path
|
|
429
|
+
*/
|
|
430
|
+
async rename(from: string, to: string): Promise<void> {
|
|
431
|
+
await wrapGrpcCall(
|
|
432
|
+
"StorageService",
|
|
433
|
+
"Rename",
|
|
434
|
+
async () => {
|
|
435
|
+
await this.client.rename({
|
|
436
|
+
bindingName: this.bindingName,
|
|
437
|
+
fromPath: from,
|
|
438
|
+
toPath: to,
|
|
439
|
+
})
|
|
440
|
+
},
|
|
441
|
+
{ bindingName: this.bindingName },
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Copy an object only if the destination doesn't exist.
|
|
447
|
+
*
|
|
448
|
+
* @param from - Source path
|
|
449
|
+
* @param to - Destination path
|
|
450
|
+
*/
|
|
451
|
+
async copyIfNotExists(from: string, to: string): Promise<void> {
|
|
452
|
+
await wrapGrpcCall(
|
|
453
|
+
"StorageService",
|
|
454
|
+
"CopyIfNotExists",
|
|
455
|
+
async () => {
|
|
456
|
+
await this.client.copyIfNotExists({
|
|
457
|
+
bindingName: this.bindingName,
|
|
458
|
+
fromPath: from,
|
|
459
|
+
toPath: to,
|
|
460
|
+
})
|
|
461
|
+
},
|
|
462
|
+
{ bindingName: this.bindingName },
|
|
463
|
+
)
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Rename (move) an object only if the destination doesn't exist.
|
|
468
|
+
*
|
|
469
|
+
* @param from - Source path
|
|
470
|
+
* @param to - Destination path
|
|
471
|
+
*/
|
|
472
|
+
async renameIfNotExists(from: string, to: string): Promise<void> {
|
|
473
|
+
await wrapGrpcCall(
|
|
474
|
+
"StorageService",
|
|
475
|
+
"RenameIfNotExists",
|
|
476
|
+
async () => {
|
|
477
|
+
await this.client.renameIfNotExists({
|
|
478
|
+
bindingName: this.bindingName,
|
|
479
|
+
fromPath: from,
|
|
480
|
+
toPath: to,
|
|
481
|
+
})
|
|
482
|
+
},
|
|
483
|
+
{ bindingName: this.bindingName },
|
|
484
|
+
)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Generate a signed URL for an object.
|
|
489
|
+
*
|
|
490
|
+
* @param path - Path to the object
|
|
491
|
+
* @param options - Signed URL options
|
|
492
|
+
* @returns Signed URL result
|
|
493
|
+
*/
|
|
494
|
+
async signedUrl(path: string, options: SignedUrlOptions): Promise<SignedUrlResult> {
|
|
495
|
+
const operationMap: Record<SignedUrlOptions["operation"], StorageHttpMethod> = {
|
|
496
|
+
get: StorageHttpMethod.HTTP_METHOD_GET,
|
|
497
|
+
put: StorageHttpMethod.HTTP_METHOD_PUT,
|
|
498
|
+
delete: StorageHttpMethod.HTTP_METHOD_DELETE,
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// Calculate expiration time
|
|
502
|
+
const expiresInSeconds = options.expiresInSeconds ?? 3600
|
|
503
|
+
const expirationTime = new Date(Date.now() + expiresInSeconds * 1000)
|
|
504
|
+
|
|
505
|
+
return await wrapGrpcCall(
|
|
506
|
+
"StorageService",
|
|
507
|
+
"SignedUrl",
|
|
508
|
+
async () => {
|
|
509
|
+
const response = await this.client.signedUrl({
|
|
510
|
+
bindingName: this.bindingName,
|
|
511
|
+
path,
|
|
512
|
+
httpMethod: operationMap[options.operation],
|
|
513
|
+
expirationTime,
|
|
514
|
+
})
|
|
515
|
+
return {
|
|
516
|
+
url: response.url,
|
|
517
|
+
expiresAt: expirationTime,
|
|
518
|
+
}
|
|
519
|
+
},
|
|
520
|
+
{ bindingName: this.bindingName },
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Private helpers
|
|
525
|
+
|
|
526
|
+
private fromProtoMeta(proto: ProtoObjectMeta): StorageObjectMeta {
|
|
527
|
+
return {
|
|
528
|
+
location: proto.location,
|
|
529
|
+
lastModified: proto.lastModified,
|
|
530
|
+
size: proto.size,
|
|
531
|
+
etag: proto.eTag,
|
|
532
|
+
version: proto.version,
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vault binding implementation.
|
|
3
|
+
*
|
|
4
|
+
* Provides secure secret management operations.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { type Channel, createClient } from "nice-grpc"
|
|
8
|
+
import {
|
|
9
|
+
type VaultServiceClient as GeneratedVaultServiceClient,
|
|
10
|
+
VaultServiceDefinition,
|
|
11
|
+
} from "../generated/vault.js"
|
|
12
|
+
import { wrapGrpcCall } from "../grpc-utils.js"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Vault binding for secret management operations.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { vault } from "@alienplatform/sdk"
|
|
20
|
+
*
|
|
21
|
+
* const secrets = vault("app-secrets")
|
|
22
|
+
*
|
|
23
|
+
* // Get a secret
|
|
24
|
+
* const apiKey = await secrets.get("API_KEY")
|
|
25
|
+
*
|
|
26
|
+
* // Set a secret
|
|
27
|
+
* await secrets.set("DATABASE_URL", "postgres://...")
|
|
28
|
+
*
|
|
29
|
+
* // Delete a secret
|
|
30
|
+
* await secrets.delete("OLD_KEY")
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class Vault {
|
|
34
|
+
private readonly client: GeneratedVaultServiceClient
|
|
35
|
+
private readonly bindingName: string
|
|
36
|
+
|
|
37
|
+
constructor(channel: Channel, bindingName: string) {
|
|
38
|
+
this.client = createClient(VaultServiceDefinition, channel)
|
|
39
|
+
this.bindingName = bindingName
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get a secret value.
|
|
44
|
+
*
|
|
45
|
+
* @param secretName - Name of the secret
|
|
46
|
+
* @returns Secret value
|
|
47
|
+
*/
|
|
48
|
+
async get(secretName: string): Promise<string> {
|
|
49
|
+
return await wrapGrpcCall(
|
|
50
|
+
"VaultService",
|
|
51
|
+
"GetSecret",
|
|
52
|
+
async () => {
|
|
53
|
+
const response = await this.client.getSecret({
|
|
54
|
+
bindingName: this.bindingName,
|
|
55
|
+
secretName,
|
|
56
|
+
})
|
|
57
|
+
return response.value
|
|
58
|
+
},
|
|
59
|
+
{ bindingName: this.bindingName, secretName },
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get a secret as JSON.
|
|
65
|
+
*
|
|
66
|
+
* @param secretName - Name of the secret
|
|
67
|
+
* @returns Parsed JSON value
|
|
68
|
+
*/
|
|
69
|
+
async getJson<T = unknown>(secretName: string): Promise<T> {
|
|
70
|
+
const value = await this.get(secretName)
|
|
71
|
+
return JSON.parse(value) as T
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Set a secret value.
|
|
76
|
+
*
|
|
77
|
+
* @param secretName - Name of the secret
|
|
78
|
+
* @param value - Secret value (string or object for JSON)
|
|
79
|
+
*/
|
|
80
|
+
async set(secretName: string, value: string | object): Promise<void> {
|
|
81
|
+
const stringValue = typeof value === "string" ? value : JSON.stringify(value)
|
|
82
|
+
|
|
83
|
+
await wrapGrpcCall(
|
|
84
|
+
"VaultService",
|
|
85
|
+
"SetSecret",
|
|
86
|
+
async () => {
|
|
87
|
+
await this.client.setSecret({
|
|
88
|
+
bindingName: this.bindingName,
|
|
89
|
+
secretName,
|
|
90
|
+
value: stringValue,
|
|
91
|
+
})
|
|
92
|
+
},
|
|
93
|
+
{ bindingName: this.bindingName, secretName },
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Delete a secret.
|
|
99
|
+
*
|
|
100
|
+
* @param secretName - Name of the secret to delete
|
|
101
|
+
*/
|
|
102
|
+
async delete(secretName: string): Promise<void> {
|
|
103
|
+
await wrapGrpcCall(
|
|
104
|
+
"VaultService",
|
|
105
|
+
"DeleteSecret",
|
|
106
|
+
async () => {
|
|
107
|
+
await this.client.deleteSecret({
|
|
108
|
+
bindingName: this.bindingName,
|
|
109
|
+
secretName,
|
|
110
|
+
})
|
|
111
|
+
},
|
|
112
|
+
{ bindingName: this.bindingName, secretName },
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if a secret exists.
|
|
118
|
+
*
|
|
119
|
+
* @param secretName - Name of the secret
|
|
120
|
+
* @returns True if the secret exists
|
|
121
|
+
*/
|
|
122
|
+
async exists(secretName: string): Promise<boolean> {
|
|
123
|
+
try {
|
|
124
|
+
await this.get(secretName)
|
|
125
|
+
return true
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (error instanceof Error && "code" in error && (error as any).code === "SECRET_NOT_FOUND") {
|
|
128
|
+
return false
|
|
129
|
+
}
|
|
130
|
+
throw error
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|