@alienplatform/bindings 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 (42) hide show
  1. package/AGENTS.md +70 -0
  2. package/dist/index.d.ts +1770 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +44772 -0
  5. package/dist/index.js.map +1 -0
  6. package/package.json +56 -0
  7. package/scripts/generate-proto.sh +46 -0
  8. package/src/bindings/AGENTS.md +105 -0
  9. package/src/bindings/artifact-registry.ts +314 -0
  10. package/src/bindings/build.ts +195 -0
  11. package/src/bindings/function.ts +164 -0
  12. package/src/bindings/index.ts +12 -0
  13. package/src/bindings/kv.ts +240 -0
  14. package/src/bindings/queue.ts +191 -0
  15. package/src/bindings/service-account.ts +113 -0
  16. package/src/bindings/storage.ts +535 -0
  17. package/src/bindings/vault.ts +133 -0
  18. package/src/channel.ts +94 -0
  19. package/src/commands.ts +76 -0
  20. package/src/context.ts +368 -0
  21. package/src/errors.ts +259 -0
  22. package/src/events.ts +463 -0
  23. package/src/generated/artifact_registry.ts +1934 -0
  24. package/src/generated/build.ts +1263 -0
  25. package/src/generated/container.ts +485 -0
  26. package/src/generated/control.ts +1574 -0
  27. package/src/generated/function.ts +741 -0
  28. package/src/generated/google/protobuf/duration.ts +196 -0
  29. package/src/generated/google/protobuf/timestamp.ts +225 -0
  30. package/src/generated/kv.ts +1137 -0
  31. package/src/generated/queue.ts +729 -0
  32. package/src/generated/service_account.ts +766 -0
  33. package/src/generated/storage.ts +3653 -0
  34. package/src/generated/vault.ts +519 -0
  35. package/src/generated/wait_until.ts +781 -0
  36. package/src/global.ts +316 -0
  37. package/src/grpc-utils.ts +159 -0
  38. package/src/index.ts +156 -0
  39. package/src/types.ts +384 -0
  40. package/src/wait-until.ts +257 -0
  41. package/tsconfig.json +8 -0
  42. package/tsdown.config.ts +14 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Build binding implementation.
3
+ *
4
+ * Provides build execution capabilities.
5
+ */
6
+
7
+ import type { BuildStatus } from "@alienplatform/core"
8
+ import { type Channel, createClient } from "nice-grpc"
9
+ import {
10
+ type BuildConfig as BuildConfigProto,
11
+ type BuildExecution as BuildExecutionProto,
12
+ BuildServiceDefinition,
13
+ BuildStatus as BuildStatusProto,
14
+ ComputeType,
15
+ type BuildServiceClient as GeneratedBuildServiceClient,
16
+ } from "../generated/build.js"
17
+ import { wrapGrpcCall } from "../grpc-utils.js"
18
+ import type { BuildExecution, BuildStartConfig } from "../types.js"
19
+
20
+ // Map proto status to core BuildStatus (matches Rust: Unspecified → Failed)
21
+ const statusMap: Record<number, BuildStatus> = {
22
+ [BuildStatusProto.BUILD_STATUS_UNSPECIFIED]: "FAILED",
23
+ [BuildStatusProto.BUILD_STATUS_QUEUED]: "QUEUED",
24
+ [BuildStatusProto.BUILD_STATUS_RUNNING]: "RUNNING",
25
+ [BuildStatusProto.BUILD_STATUS_SUCCEEDED]: "SUCCEEDED",
26
+ [BuildStatusProto.BUILD_STATUS_FAILED]: "FAILED",
27
+ [BuildStatusProto.BUILD_STATUS_CANCELLED]: "CANCELLED",
28
+ [BuildStatusProto.BUILD_STATUS_TIMED_OUT]: "TIMED_OUT",
29
+ }
30
+
31
+ // Map compute type to proto
32
+ const computeTypeMap: Record<string, ComputeType> = {
33
+ small: ComputeType.COMPUTE_TYPE_SMALL,
34
+ medium: ComputeType.COMPUTE_TYPE_MEDIUM,
35
+ large: ComputeType.COMPUTE_TYPE_LARGE,
36
+ "x-large": ComputeType.COMPUTE_TYPE_XLARGE,
37
+ }
38
+
39
+ /**
40
+ * Build binding for executing build operations.
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * import { build } from "@alienplatform/bindings"
45
+ *
46
+ * const builder = build("my-builder")
47
+ *
48
+ * // Start a build
49
+ * const execution = await builder.start({
50
+ * script: "npm run build",
51
+ * computeType: "medium",
52
+ * timeoutSeconds: 600,
53
+ * })
54
+ *
55
+ * // Wait for completion
56
+ * const result = await builder.waitForCompletion(execution.id)
57
+ * console.log("Build status:", result.status)
58
+ * ```
59
+ */
60
+ export class Build {
61
+ private readonly client: GeneratedBuildServiceClient
62
+ private readonly bindingName: string
63
+
64
+ constructor(channel: Channel, bindingName: string) {
65
+ this.client = createClient(BuildServiceDefinition, channel)
66
+ this.bindingName = bindingName
67
+ }
68
+
69
+ /**
70
+ * Start a new build.
71
+ *
72
+ * @param config - Build configuration
73
+ * @returns Build execution information
74
+ */
75
+ async start(config: BuildStartConfig): Promise<BuildExecution> {
76
+ const protoConfig = this.toProtoConfig(config)
77
+
78
+ return await wrapGrpcCall(
79
+ "BuildService",
80
+ "StartBuild",
81
+ async () => {
82
+ const response = await this.client.startBuild({
83
+ bindingName: this.bindingName,
84
+ config: protoConfig,
85
+ })
86
+ return this.fromProtoExecution(response.execution!)
87
+ },
88
+ { bindingName: this.bindingName },
89
+ )
90
+ }
91
+
92
+ /**
93
+ * Get the status of a build.
94
+ *
95
+ * @param buildId - Build execution ID
96
+ * @returns Build execution information
97
+ */
98
+ async getStatus(buildId: string): Promise<BuildExecution> {
99
+ return await wrapGrpcCall(
100
+ "BuildService",
101
+ "GetBuildStatus",
102
+ async () => {
103
+ const response = await this.client.getBuildStatus({
104
+ bindingName: this.bindingName,
105
+ buildId,
106
+ })
107
+ return this.fromProtoExecution(response.execution!)
108
+ },
109
+ { bindingName: this.bindingName },
110
+ )
111
+ }
112
+
113
+ /**
114
+ * Stop a running build.
115
+ *
116
+ * @param buildId - Build execution ID
117
+ */
118
+ async stop(buildId: string): Promise<void> {
119
+ await wrapGrpcCall(
120
+ "BuildService",
121
+ "StopBuild",
122
+ async () => {
123
+ await this.client.stopBuild({
124
+ bindingName: this.bindingName,
125
+ buildId,
126
+ })
127
+ },
128
+ { bindingName: this.bindingName },
129
+ )
130
+ }
131
+
132
+ /**
133
+ * Wait for a build to complete.
134
+ *
135
+ * @param buildId - Build execution ID
136
+ * @param options - Polling options
137
+ * @returns Final build execution information
138
+ */
139
+ async waitForCompletion(
140
+ buildId: string,
141
+ options?: { pollIntervalMs?: number; timeoutMs?: number },
142
+ ): Promise<BuildExecution> {
143
+ const pollInterval = options?.pollIntervalMs ?? 5000
144
+ const timeout = options?.timeoutMs ?? 30 * 60 * 1000 // 30 minutes default
145
+ const startTime = Date.now()
146
+
147
+ while (Date.now() - startTime < timeout) {
148
+ const execution = await this.getStatus(buildId)
149
+
150
+ if (
151
+ execution.status === "SUCCEEDED" ||
152
+ execution.status === "FAILED" ||
153
+ execution.status === "CANCELLED" ||
154
+ execution.status === "TIMED_OUT"
155
+ ) {
156
+ return execution
157
+ }
158
+
159
+ await new Promise(resolve => setTimeout(resolve, pollInterval))
160
+ }
161
+
162
+ throw new Error(`Build ${buildId} did not complete within timeout`)
163
+ }
164
+
165
+ // Private helpers
166
+
167
+ private toProtoConfig(config: BuildStartConfig): BuildConfigProto {
168
+ return {
169
+ script: config.script,
170
+ environment: config.environment ?? {},
171
+ computeType: config.computeType
172
+ ? (computeTypeMap[config.computeType] ?? ComputeType.COMPUTE_TYPE_UNSPECIFIED)
173
+ : ComputeType.COMPUTE_TYPE_UNSPECIFIED,
174
+ timeoutSeconds: config.timeoutSeconds,
175
+ monitoring: config.monitoring
176
+ ? {
177
+ endpoint: config.monitoring.endpoint,
178
+ headers: config.monitoring.headers ?? {},
179
+ logsUri: config.monitoring.logsUri ?? "/v1/logs",
180
+ tlsEnabled: config.monitoring.tlsEnabled ?? true,
181
+ tlsVerify: config.monitoring.tlsVerify ?? true,
182
+ }
183
+ : undefined,
184
+ }
185
+ }
186
+
187
+ private fromProtoExecution(proto: BuildExecutionProto): BuildExecution {
188
+ return {
189
+ id: proto.id,
190
+ status: statusMap[proto.status] ?? "QUEUED",
191
+ startTime: proto.startTime ? new Date(proto.startTime) : undefined,
192
+ endTime: proto.endTime ? new Date(proto.endTime) : undefined,
193
+ }
194
+ }
195
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Function binding implementation.
3
+ *
4
+ * Provides direct function-to-function invocation.
5
+ */
6
+
7
+ import { type Channel, createClient } from "nice-grpc"
8
+ import {
9
+ FunctionServiceDefinition,
10
+ type FunctionServiceClient as GeneratedClient,
11
+ } from "../generated/function.js"
12
+ import { wrapGrpcCall } from "../grpc-utils.js"
13
+ import type { FunctionInvokeRequest, FunctionInvokeResponse } from "../types.js"
14
+
15
+ /**
16
+ * Function binding for direct function invocation.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { func } from "@alienplatform/bindings"
21
+ *
22
+ * const processor = func("image-processor")
23
+ *
24
+ * // Invoke with JSON body
25
+ * const response = await processor.invokeJson("resize-image", {
26
+ * imageUrl: "https://...",
27
+ * width: 800,
28
+ * height: 600,
29
+ * })
30
+ *
31
+ * // Get function URL
32
+ * const url = await processor.getUrl()
33
+ * ```
34
+ */
35
+ export class FunctionBinding {
36
+ private readonly client: GeneratedClient
37
+ private readonly bindingName: string
38
+
39
+ constructor(channel: Channel, bindingName: string) {
40
+ this.client = createClient(FunctionServiceDefinition, channel)
41
+ this.bindingName = bindingName
42
+ }
43
+
44
+ /**
45
+ * Invoke a function with raw request data.
46
+ *
47
+ * @param request - Invocation request
48
+ * @returns Function response
49
+ */
50
+ async invoke(request: FunctionInvokeRequest): Promise<FunctionInvokeResponse> {
51
+ return await wrapGrpcCall(
52
+ "FunctionService",
53
+ "Invoke",
54
+ async () => {
55
+ const response = await this.client.invoke({
56
+ bindingName: this.bindingName,
57
+ targetFunction: request.targetFunction,
58
+ method: request.method,
59
+ path: request.path,
60
+ headers: request.headers ?? {},
61
+ body: request.body ?? new Uint8Array(),
62
+ timeoutSeconds: request.timeoutMs ? Math.floor(request.timeoutMs / 1000) : undefined,
63
+ })
64
+ return {
65
+ status: response.status,
66
+ headers: response.headers,
67
+ body: response.body,
68
+ }
69
+ },
70
+ { bindingName: this.bindingName },
71
+ )
72
+ }
73
+
74
+ /**
75
+ * Invoke a function with a JSON body.
76
+ *
77
+ * @param targetFunction - Target function identifier
78
+ * @param body - JSON body
79
+ * @param options - Optional request options
80
+ * @returns Parsed JSON response
81
+ */
82
+ async invokeJson<TRequest, TResponse = unknown>(
83
+ targetFunction: string,
84
+ body: TRequest,
85
+ options?: {
86
+ method?: string
87
+ path?: string
88
+ headers?: Record<string, string>
89
+ timeoutMs?: number
90
+ },
91
+ ): Promise<TResponse> {
92
+ const response = await this.invoke({
93
+ targetFunction,
94
+ method: options?.method ?? "POST",
95
+ path: options?.path ?? "/",
96
+ headers: {
97
+ "content-type": "application/json",
98
+ ...options?.headers,
99
+ },
100
+ body: new TextEncoder().encode(JSON.stringify(body)),
101
+ timeoutMs: options?.timeoutMs,
102
+ })
103
+
104
+ if (response.status >= 400) {
105
+ const errorText = new TextDecoder().decode(response.body)
106
+ throw new Error(`Function invocation failed with status ${response.status}: ${errorText}`)
107
+ }
108
+
109
+ const responseText = new TextDecoder().decode(response.body)
110
+ return JSON.parse(responseText) as TResponse
111
+ }
112
+
113
+ /**
114
+ * Invoke a function with GET request.
115
+ *
116
+ * @param targetFunction - Target function identifier
117
+ * @param path - Request path
118
+ * @param options - Optional request options
119
+ * @returns Parsed JSON response
120
+ */
121
+ async get<TResponse = unknown>(
122
+ targetFunction: string,
123
+ path = "/",
124
+ options?: {
125
+ headers?: Record<string, string>
126
+ timeoutMs?: number
127
+ },
128
+ ): Promise<TResponse> {
129
+ const response = await this.invoke({
130
+ targetFunction,
131
+ method: "GET",
132
+ path,
133
+ headers: options?.headers,
134
+ timeoutMs: options?.timeoutMs,
135
+ })
136
+
137
+ if (response.status >= 400) {
138
+ const errorText = new TextDecoder().decode(response.body)
139
+ throw new Error(`Function GET failed with status ${response.status}: ${errorText}`)
140
+ }
141
+
142
+ const responseText = new TextDecoder().decode(response.body)
143
+ return JSON.parse(responseText) as TResponse
144
+ }
145
+
146
+ /**
147
+ * Get the public URL of the function.
148
+ *
149
+ * @returns Function URL if available
150
+ */
151
+ async getUrl(): Promise<string | undefined> {
152
+ return await wrapGrpcCall(
153
+ "FunctionService",
154
+ "GetFunctionUrl",
155
+ async () => {
156
+ const response = await this.client.getFunctionUrl({
157
+ bindingName: this.bindingName,
158
+ })
159
+ return response.url
160
+ },
161
+ { bindingName: this.bindingName },
162
+ )
163
+ }
164
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Binding exports.
3
+ */
4
+
5
+ export { Storage } from "./storage.js"
6
+ export { Kv } from "./kv.js"
7
+ export { Queue } from "./queue.js"
8
+ export { Vault } from "./vault.js"
9
+ export { Build } from "./build.js"
10
+ export { ArtifactRegistry } from "./artifact-registry.js"
11
+ export { FunctionBinding } from "./function.js"
12
+ export { ServiceAccount } from "./service-account.js"
@@ -0,0 +1,240 @@
1
+ /**
2
+ * KV binding implementation.
3
+ *
4
+ * Provides key-value storage operations with TTL support.
5
+ */
6
+
7
+ import { type Channel, createClient } from "nice-grpc"
8
+ import {
9
+ type KvServiceClient as GeneratedKvServiceClient,
10
+ KvServiceDefinition,
11
+ } from "../generated/kv.js"
12
+ import { wrapGrpcCall } from "../grpc-utils.js"
13
+ import type { KvPutOptions, KvScanResult } from "../types.js"
14
+
15
+ /**
16
+ * KV binding for key-value storage operations.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { kv } from "@alienplatform/bindings"
21
+ *
22
+ * const cache = kv("my-cache")
23
+ *
24
+ * // Set a value
25
+ * await cache.set("user:123", { name: "John", email: "john@example.com" })
26
+ *
27
+ * // Get a value
28
+ * const user = await cache.getJson<User>("user:123")
29
+ *
30
+ * // Set with TTL (expires in 1 hour)
31
+ * await cache.set("session:abc", sessionData, { ttlMs: 60 * 60 * 1000 })
32
+ *
33
+ * // Delete
34
+ * await cache.delete("user:123")
35
+ *
36
+ * // Scan by prefix
37
+ * for await (const { key, value } of cache.scan("user:")) {
38
+ * console.log(key, value)
39
+ * }
40
+ * ```
41
+ */
42
+ export class Kv {
43
+ private readonly client: GeneratedKvServiceClient
44
+ private readonly bindingName: string
45
+
46
+ constructor(channel: Channel, bindingName: string) {
47
+ this.client = createClient(KvServiceDefinition, channel)
48
+ this.bindingName = bindingName
49
+ }
50
+
51
+ /**
52
+ * Get a value by key.
53
+ *
54
+ * @param key - Key to retrieve
55
+ * @returns Value as Uint8Array, or undefined if not found
56
+ */
57
+ async get(key: string): Promise<Uint8Array | undefined> {
58
+ return await wrapGrpcCall(
59
+ "KvService",
60
+ "Get",
61
+ async () => {
62
+ const response = await this.client.get({
63
+ bindingName: this.bindingName,
64
+ key,
65
+ })
66
+ return response.value
67
+ },
68
+ { bindingName: this.bindingName, key },
69
+ )
70
+ }
71
+
72
+ /**
73
+ * Get a value as a UTF-8 string.
74
+ *
75
+ * @param key - Key to retrieve
76
+ * @returns Value as string, or undefined if not found
77
+ */
78
+ async getText(key: string): Promise<string | undefined> {
79
+ const value = await this.get(key)
80
+ return value ? new TextDecoder().decode(value) : undefined
81
+ }
82
+
83
+ /**
84
+ * Get a value and parse as JSON.
85
+ *
86
+ * @param key - Key to retrieve
87
+ * @returns Parsed JSON value, or undefined if not found
88
+ */
89
+ async getJson<T = unknown>(key: string): Promise<T | undefined> {
90
+ const text = await this.getText(key)
91
+ return text ? (JSON.parse(text) as T) : undefined
92
+ }
93
+
94
+ /**
95
+ * Set a value.
96
+ *
97
+ * @param key - Key to store
98
+ * @param value - Value to store (string, Uint8Array, or object for JSON)
99
+ * @param options - Optional put options (TTL, ifNotExists)
100
+ * @returns True if stored, false if ifNotExists was true and key already exists
101
+ */
102
+ async set(
103
+ key: string,
104
+ value: string | Uint8Array | object,
105
+ options?: KvPutOptions,
106
+ ): Promise<boolean> {
107
+ let bytes: Uint8Array
108
+
109
+ if (typeof value === "string") {
110
+ bytes = new TextEncoder().encode(value)
111
+ } else if (value instanceof Uint8Array) {
112
+ bytes = value
113
+ } else {
114
+ bytes = new TextEncoder().encode(JSON.stringify(value))
115
+ }
116
+
117
+ return await wrapGrpcCall(
118
+ "KvService",
119
+ "Put",
120
+ async () => {
121
+ const response = await this.client.put({
122
+ bindingName: this.bindingName,
123
+ key,
124
+ value: bytes,
125
+ options: options
126
+ ? {
127
+ ttlSeconds: options.ttlMs ? Math.floor(options.ttlMs / 1000) : undefined,
128
+ ifNotExists: options.ifNotExists ?? false,
129
+ }
130
+ : undefined,
131
+ })
132
+ return response.success
133
+ },
134
+ { bindingName: this.bindingName, key },
135
+ )
136
+ }
137
+
138
+ /**
139
+ * Delete a key.
140
+ *
141
+ * @param key - Key to delete
142
+ */
143
+ async delete(key: string): Promise<void> {
144
+ await wrapGrpcCall(
145
+ "KvService",
146
+ "Delete",
147
+ async () => {
148
+ await this.client.delete({
149
+ bindingName: this.bindingName,
150
+ key,
151
+ })
152
+ },
153
+ { bindingName: this.bindingName, key },
154
+ )
155
+ }
156
+
157
+ /**
158
+ * Check if a key exists.
159
+ *
160
+ * @param key - Key to check
161
+ * @returns True if the key exists
162
+ */
163
+ async exists(key: string): Promise<boolean> {
164
+ return await wrapGrpcCall(
165
+ "KvService",
166
+ "Exists",
167
+ async () => {
168
+ const response = await this.client.exists({
169
+ bindingName: this.bindingName,
170
+ key,
171
+ })
172
+ return response.exists
173
+ },
174
+ { bindingName: this.bindingName, key },
175
+ )
176
+ }
177
+
178
+ /**
179
+ * Scan keys with a prefix.
180
+ *
181
+ * @param prefix - Prefix to scan for
182
+ * @param options - Optional scan options (limit)
183
+ * @returns Async iterable of key-value pairs
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // Scan all users
188
+ * for await (const { key, value } of cache.scan("user:")) {
189
+ * console.log(key, new TextDecoder().decode(value))
190
+ * }
191
+ * ```
192
+ */
193
+ async *scan(
194
+ prefix: string,
195
+ options?: { limit?: number },
196
+ ): AsyncIterable<{ key: string; value: Uint8Array }> {
197
+ let cursor: string | undefined
198
+
199
+ do {
200
+ const result = await this.scanPage(prefix, options?.limit, cursor)
201
+
202
+ for (const item of result.items) {
203
+ yield item
204
+ }
205
+
206
+ cursor = result.nextCursor
207
+ } while (cursor)
208
+ }
209
+
210
+ /**
211
+ * Scan a single page of results.
212
+ *
213
+ * @param prefix - Prefix to scan for
214
+ * @param limit - Maximum items to return
215
+ * @param cursor - Pagination cursor
216
+ * @returns Scan result with items and next cursor
217
+ */
218
+ async scanPage(prefix: string, limit?: number, cursor?: string): Promise<KvScanResult> {
219
+ return await wrapGrpcCall(
220
+ "KvService",
221
+ "ScanPrefix",
222
+ async () => {
223
+ const response = await this.client.scanPrefix({
224
+ bindingName: this.bindingName,
225
+ prefix,
226
+ limit,
227
+ cursor,
228
+ })
229
+ return {
230
+ items: response.items.map(item => ({
231
+ key: item.key,
232
+ value: item.value,
233
+ })),
234
+ nextCursor: response.nextCursor,
235
+ }
236
+ },
237
+ { bindingName: this.bindingName },
238
+ )
239
+ }
240
+ }