@alienplatform/bindings 1.0.0 → 2.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.
- package/LICENSE.md +105 -0
- package/README.md +59 -0
- package/dist/errors.d.ts +56 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/factories.d.ts +26 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/index.d.ts +19 -1768
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -44772
- package/dist/loader.d.ts +171 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +3712 -0
- package/dist/native.d.ts +41 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +46 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +39 -21
- package/AGENTS.md +0 -70
- package/dist/index.js.map +0 -1
- package/scripts/generate-proto.sh +0 -46
- package/src/bindings/AGENTS.md +0 -105
- package/src/bindings/artifact-registry.ts +0 -314
- package/src/bindings/build.ts +0 -195
- package/src/bindings/function.ts +0 -164
- package/src/bindings/index.ts +0 -12
- package/src/bindings/kv.ts +0 -240
- package/src/bindings/queue.ts +0 -191
- package/src/bindings/service-account.ts +0 -113
- package/src/bindings/storage.ts +0 -535
- package/src/bindings/vault.ts +0 -133
- package/src/channel.ts +0 -94
- package/src/commands.ts +0 -76
- package/src/context.ts +0 -368
- package/src/errors.ts +0 -259
- package/src/events.ts +0 -463
- package/src/generated/artifact_registry.ts +0 -1934
- package/src/generated/build.ts +0 -1263
- package/src/generated/container.ts +0 -485
- package/src/generated/control.ts +0 -1574
- package/src/generated/function.ts +0 -741
- package/src/generated/google/protobuf/duration.ts +0 -196
- package/src/generated/google/protobuf/timestamp.ts +0 -225
- package/src/generated/kv.ts +0 -1137
- package/src/generated/queue.ts +0 -729
- package/src/generated/service_account.ts +0 -766
- package/src/generated/storage.ts +0 -3653
- package/src/generated/vault.ts +0 -519
- package/src/generated/wait_until.ts +0 -781
- package/src/global.ts +0 -316
- package/src/grpc-utils.ts +0 -159
- package/src/index.ts +0 -156
- package/src/types.ts +0 -384
- package/src/wait-until.ts +0 -257
- package/tsconfig.json +0 -8
- package/tsdown.config.ts +0 -14
package/src/errors.ts
DELETED
|
@@ -1,259 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error definitions for the Alien bindings SDK.
|
|
3
|
-
* These errors are thrown when binding operations fail.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { defineError } from "@alienplatform/core"
|
|
7
|
-
import * as z from "zod/v4"
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Error thrown when the gRPC connection to the alien-runtime fails.
|
|
11
|
-
*/
|
|
12
|
-
export const GrpcConnectionError = defineError({
|
|
13
|
-
code: "GRPC_CONNECTION_ERROR",
|
|
14
|
-
context: z.object({
|
|
15
|
-
endpoint: z.string(),
|
|
16
|
-
reason: z.string(),
|
|
17
|
-
}),
|
|
18
|
-
message: ({ endpoint, reason }) =>
|
|
19
|
-
`Failed to connect to alien-runtime at '${endpoint}': ${reason}`,
|
|
20
|
-
retryable: true,
|
|
21
|
-
internal: false,
|
|
22
|
-
httpStatusCode: 503,
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Error thrown when a gRPC call fails.
|
|
27
|
-
*/
|
|
28
|
-
export const GrpcCallError = defineError({
|
|
29
|
-
code: "GRPC_CALL_ERROR",
|
|
30
|
-
context: z.object({
|
|
31
|
-
service: z.string(),
|
|
32
|
-
method: z.string(),
|
|
33
|
-
grpcCode: z.string(),
|
|
34
|
-
details: z.string(),
|
|
35
|
-
}),
|
|
36
|
-
message: ({ service, method, grpcCode, details }) =>
|
|
37
|
-
`gRPC call ${service}.${method} failed with code ${grpcCode}: ${details}`,
|
|
38
|
-
retryable: false,
|
|
39
|
-
internal: false,
|
|
40
|
-
httpStatusCode: 500,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Error thrown when a binding is not found.
|
|
45
|
-
*/
|
|
46
|
-
export const BindingNotFoundError = defineError({
|
|
47
|
-
code: "BINDING_NOT_FOUND",
|
|
48
|
-
context: z.object({
|
|
49
|
-
bindingName: z.string(),
|
|
50
|
-
bindingType: z.string(),
|
|
51
|
-
}),
|
|
52
|
-
message: ({ bindingName, bindingType }) => `${bindingType} binding '${bindingName}' not found`,
|
|
53
|
-
retryable: false,
|
|
54
|
-
internal: false,
|
|
55
|
-
httpStatusCode: 404,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Error thrown when the AlienContext is not initialized.
|
|
60
|
-
*/
|
|
61
|
-
export const ContextNotInitializedError = defineError({
|
|
62
|
-
code: "CONTEXT_NOT_INITIALIZED",
|
|
63
|
-
context: z.object({
|
|
64
|
-
operation: z.string(),
|
|
65
|
-
}),
|
|
66
|
-
message: ({ operation }) =>
|
|
67
|
-
`AlienContext not initialized. Call AlienContext.fromEnv() before using ${operation}`,
|
|
68
|
-
retryable: false,
|
|
69
|
-
internal: false,
|
|
70
|
-
httpStatusCode: 500,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Error thrown when a required environment variable is missing.
|
|
75
|
-
*/
|
|
76
|
-
export const MissingEnvVarError = defineError({
|
|
77
|
-
code: "MISSING_ENV_VAR",
|
|
78
|
-
context: z.object({
|
|
79
|
-
variable: z.string(),
|
|
80
|
-
description: z.string(),
|
|
81
|
-
}),
|
|
82
|
-
message: ({ variable, description }) =>
|
|
83
|
-
`Required environment variable '${variable}' is not set. ${description}`,
|
|
84
|
-
retryable: false,
|
|
85
|
-
internal: false,
|
|
86
|
-
httpStatusCode: 500,
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Error thrown when a storage object is not found.
|
|
91
|
-
*/
|
|
92
|
-
export const StorageObjectNotFoundError = defineError({
|
|
93
|
-
code: "STORAGE_OBJECT_NOT_FOUND",
|
|
94
|
-
context: z.object({
|
|
95
|
-
bindingName: z.string(),
|
|
96
|
-
path: z.string(),
|
|
97
|
-
}),
|
|
98
|
-
message: ({ bindingName, path }) =>
|
|
99
|
-
`Object '${path}' not found in storage binding '${bindingName}'`,
|
|
100
|
-
retryable: false,
|
|
101
|
-
internal: false,
|
|
102
|
-
httpStatusCode: 404,
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Error thrown when a storage precondition fails.
|
|
107
|
-
*/
|
|
108
|
-
export const StoragePreconditionError = defineError({
|
|
109
|
-
code: "STORAGE_PRECONDITION_FAILED",
|
|
110
|
-
context: z.object({
|
|
111
|
-
bindingName: z.string(),
|
|
112
|
-
path: z.string(),
|
|
113
|
-
condition: z.string(),
|
|
114
|
-
}),
|
|
115
|
-
message: ({ bindingName, path, condition }) =>
|
|
116
|
-
`Precondition failed for '${path}' in storage binding '${bindingName}': ${condition}`,
|
|
117
|
-
retryable: false,
|
|
118
|
-
internal: false,
|
|
119
|
-
httpStatusCode: 412,
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Error thrown when a storage object already exists (for create mode).
|
|
124
|
-
*/
|
|
125
|
-
export const StorageObjectExistsError = defineError({
|
|
126
|
-
code: "STORAGE_OBJECT_EXISTS",
|
|
127
|
-
context: z.object({
|
|
128
|
-
bindingName: z.string(),
|
|
129
|
-
path: z.string(),
|
|
130
|
-
}),
|
|
131
|
-
message: ({ bindingName, path }) =>
|
|
132
|
-
`Object '${path}' already exists in storage binding '${bindingName}'`,
|
|
133
|
-
retryable: false,
|
|
134
|
-
internal: false,
|
|
135
|
-
httpStatusCode: 409,
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Error thrown when a KV key is not found.
|
|
140
|
-
*/
|
|
141
|
-
export const KvKeyNotFoundError = defineError({
|
|
142
|
-
code: "KV_KEY_NOT_FOUND",
|
|
143
|
-
context: z.object({
|
|
144
|
-
bindingName: z.string(),
|
|
145
|
-
key: z.string(),
|
|
146
|
-
}),
|
|
147
|
-
message: ({ bindingName, key }) => `Key '${key}' not found in KV binding '${bindingName}'`,
|
|
148
|
-
retryable: false,
|
|
149
|
-
internal: false,
|
|
150
|
-
httpStatusCode: 404,
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Error thrown when a KV key is invalid.
|
|
155
|
-
*/
|
|
156
|
-
export const KvInvalidKeyError = defineError({
|
|
157
|
-
code: "KV_INVALID_KEY",
|
|
158
|
-
context: z.object({
|
|
159
|
-
key: z.string(),
|
|
160
|
-
reason: z.string(),
|
|
161
|
-
}),
|
|
162
|
-
message: ({ key, reason }) => `Invalid KV key '${key}': ${reason}`,
|
|
163
|
-
retryable: false,
|
|
164
|
-
internal: false,
|
|
165
|
-
httpStatusCode: 400,
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Error thrown when a KV value is invalid.
|
|
170
|
-
*/
|
|
171
|
-
export const KvInvalidValueError = defineError({
|
|
172
|
-
code: "KV_INVALID_VALUE",
|
|
173
|
-
context: z.object({
|
|
174
|
-
reason: z.string(),
|
|
175
|
-
}),
|
|
176
|
-
message: ({ reason }) => `Invalid KV value: ${reason}`,
|
|
177
|
-
retryable: false,
|
|
178
|
-
internal: false,
|
|
179
|
-
httpStatusCode: 400,
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Error thrown when a secret is not found.
|
|
184
|
-
*/
|
|
185
|
-
export const SecretNotFoundError = defineError({
|
|
186
|
-
code: "SECRET_NOT_FOUND",
|
|
187
|
-
context: z.object({
|
|
188
|
-
bindingName: z.string(),
|
|
189
|
-
secretName: z.string(),
|
|
190
|
-
}),
|
|
191
|
-
message: ({ bindingName, secretName }) =>
|
|
192
|
-
`Secret '${secretName}' not found in vault binding '${bindingName}'`,
|
|
193
|
-
retryable: false,
|
|
194
|
-
internal: false,
|
|
195
|
-
httpStatusCode: 404,
|
|
196
|
-
})
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Error thrown when a queue operation fails.
|
|
200
|
-
*/
|
|
201
|
-
export const QueueOperationError = defineError({
|
|
202
|
-
code: "QUEUE_OPERATION_ERROR",
|
|
203
|
-
context: z.object({
|
|
204
|
-
bindingName: z.string(),
|
|
205
|
-
queue: z.string(),
|
|
206
|
-
operation: z.string(),
|
|
207
|
-
reason: z.string(),
|
|
208
|
-
}),
|
|
209
|
-
message: ({ bindingName, queue, operation, reason }) =>
|
|
210
|
-
`Queue ${operation} failed for queue '${queue}' in binding '${bindingName}': ${reason}`,
|
|
211
|
-
retryable: true,
|
|
212
|
-
internal: false,
|
|
213
|
-
httpStatusCode: 500,
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Error thrown when an event handler is already registered.
|
|
218
|
-
*/
|
|
219
|
-
export const EventHandlerAlreadyRegisteredError = defineError({
|
|
220
|
-
code: "EVENT_HANDLER_ALREADY_REGISTERED",
|
|
221
|
-
context: z.object({
|
|
222
|
-
handlerType: z.string(),
|
|
223
|
-
resourceName: z.string(),
|
|
224
|
-
}),
|
|
225
|
-
message: ({ handlerType, resourceName }) =>
|
|
226
|
-
`Event handler for ${handlerType} '${resourceName}' is already registered`,
|
|
227
|
-
retryable: false,
|
|
228
|
-
internal: false,
|
|
229
|
-
httpStatusCode: 409,
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Error thrown when a command is already registered.
|
|
234
|
-
*/
|
|
235
|
-
export const CommandAlreadyRegisteredError = defineError({
|
|
236
|
-
code: "COMMAND_ALREADY_REGISTERED",
|
|
237
|
-
context: z.object({
|
|
238
|
-
commandName: z.string(),
|
|
239
|
-
}),
|
|
240
|
-
message: ({ commandName }) => `Command '${commandName}' is already registered`,
|
|
241
|
-
retryable: false,
|
|
242
|
-
internal: false,
|
|
243
|
-
httpStatusCode: 409,
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Error thrown when the binding configuration is invalid.
|
|
248
|
-
*/
|
|
249
|
-
export const InvalidBindingConfigError = defineError({
|
|
250
|
-
code: "INVALID_BINDING_CONFIG",
|
|
251
|
-
context: z.object({
|
|
252
|
-
message: z.string(),
|
|
253
|
-
suggestion: z.string().optional(),
|
|
254
|
-
}),
|
|
255
|
-
message: ({ message, suggestion }) => (suggestion ? `${message}. ${suggestion}` : message),
|
|
256
|
-
retryable: false,
|
|
257
|
-
internal: false,
|
|
258
|
-
httpStatusCode: 400,
|
|
259
|
-
})
|
package/src/events.ts
DELETED
|
@@ -1,463 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Event handling for Alien bindings.
|
|
3
|
-
*
|
|
4
|
-
* Provides handlers for storage events, cron/scheduled events, and queue messages.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Import canonical event types from core
|
|
8
|
-
import type {
|
|
9
|
-
QueueMessage,
|
|
10
|
-
ScheduledEvent,
|
|
11
|
-
StorageEvent,
|
|
12
|
-
StorageEventType,
|
|
13
|
-
} from "@alienplatform/core"
|
|
14
|
-
import { type Channel, createClient } from "nice-grpc"
|
|
15
|
-
import { getCommands, runCommand } from "./commands.js"
|
|
16
|
-
import {
|
|
17
|
-
ControlServiceDefinition,
|
|
18
|
-
type ControlServiceClient as GeneratedClient,
|
|
19
|
-
type ArcCommand as ProtoArcCommand,
|
|
20
|
-
type CronEvent as ProtoCronEvent,
|
|
21
|
-
type QueueMessage as ProtoQueueMessage,
|
|
22
|
-
type StorageEvent as ProtoStorageEvent,
|
|
23
|
-
type Task,
|
|
24
|
-
} from "./generated/control.js"
|
|
25
|
-
import { wrapGrpcCall } from "./grpc-utils.js"
|
|
26
|
-
|
|
27
|
-
// Re-export core types for convenience
|
|
28
|
-
export type { StorageEvent, StorageEventType, ScheduledEvent, QueueMessage }
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Cron/scheduled event with schedule info.
|
|
32
|
-
* Extends ScheduledEvent with the schedule name.
|
|
33
|
-
*/
|
|
34
|
-
export interface CronEvent extends ScheduledEvent {
|
|
35
|
-
/** Schedule name */
|
|
36
|
-
scheduleName: string
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Queue message event with queue context.
|
|
41
|
-
*/
|
|
42
|
-
export interface QueueMessageEvent<T = unknown> {
|
|
43
|
-
/** Message ID */
|
|
44
|
-
id: string
|
|
45
|
-
/** Source queue name */
|
|
46
|
-
source: string
|
|
47
|
-
/** Receipt handle for acknowledgment */
|
|
48
|
-
receiptHandle: string
|
|
49
|
-
/** Message payload */
|
|
50
|
-
payload: T
|
|
51
|
-
/** Delivery attempt count */
|
|
52
|
-
attemptCount: number
|
|
53
|
-
/** Message timestamp */
|
|
54
|
-
timestamp: Date
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Event handler registration.
|
|
59
|
-
*/
|
|
60
|
-
export interface EventRegistration {
|
|
61
|
-
/** Handler ID */
|
|
62
|
-
id: string
|
|
63
|
-
/** Event type */
|
|
64
|
-
type: "storage" | "cron" | "queue"
|
|
65
|
-
/** Source filter (bucket name, queue name, etc.) */
|
|
66
|
-
source: string
|
|
67
|
-
/** Optional prefix filter (for storage events) */
|
|
68
|
-
prefix?: string
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Internal storage for event handlers
|
|
72
|
-
const eventHandlers: Map<
|
|
73
|
-
string,
|
|
74
|
-
{
|
|
75
|
-
registration: EventRegistration
|
|
76
|
-
handler: (event: unknown) => Promise<void>
|
|
77
|
-
}
|
|
78
|
-
> = new Map()
|
|
79
|
-
|
|
80
|
-
let handlerIdCounter = 0
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Register a storage event handler.
|
|
84
|
-
*
|
|
85
|
-
* @param bucket - Bucket name to listen for events
|
|
86
|
-
* @param handler - Handler function for storage events
|
|
87
|
-
* @param options - Handler options
|
|
88
|
-
* @returns Unsubscribe function
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* import { onStorageEvent } from "@alienplatform/bindings"
|
|
93
|
-
*
|
|
94
|
-
* onStorageEvent("uploads", async (event) => {
|
|
95
|
-
* console.log("File uploaded:", event.objectKey)
|
|
96
|
-
* await processFile(event.bucketName, event.objectKey)
|
|
97
|
-
* })
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
export function onStorageEvent(
|
|
101
|
-
bucket: string,
|
|
102
|
-
handler: (event: StorageEvent) => Promise<void>,
|
|
103
|
-
options?: { prefix?: string },
|
|
104
|
-
): () => void {
|
|
105
|
-
const id = `storage-${++handlerIdCounter}`
|
|
106
|
-
|
|
107
|
-
eventHandlers.set(id, {
|
|
108
|
-
registration: {
|
|
109
|
-
id,
|
|
110
|
-
type: "storage",
|
|
111
|
-
source: bucket,
|
|
112
|
-
prefix: options?.prefix,
|
|
113
|
-
},
|
|
114
|
-
handler: handler as (event: unknown) => Promise<void>,
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
return () => {
|
|
118
|
-
eventHandlers.delete(id)
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Register a cron/scheduled event handler.
|
|
124
|
-
*
|
|
125
|
-
* @param scheduleName - Schedule name to listen for
|
|
126
|
-
* @param handler - Handler function for scheduled events
|
|
127
|
-
* @returns Unsubscribe function
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```typescript
|
|
131
|
-
* import { onCronEvent } from "@alienplatform/bindings"
|
|
132
|
-
*
|
|
133
|
-
* onCronEvent("hourly-task", async (event) => {
|
|
134
|
-
* console.log("Scheduled run at:", event.timestamp)
|
|
135
|
-
* await runHourlyTask()
|
|
136
|
-
* })
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export function onCronEvent(
|
|
140
|
-
scheduleName: string,
|
|
141
|
-
handler: (event: CronEvent) => Promise<void>,
|
|
142
|
-
): () => void {
|
|
143
|
-
const id = `cron-${++handlerIdCounter}`
|
|
144
|
-
|
|
145
|
-
eventHandlers.set(id, {
|
|
146
|
-
registration: {
|
|
147
|
-
id,
|
|
148
|
-
type: "cron",
|
|
149
|
-
source: scheduleName,
|
|
150
|
-
},
|
|
151
|
-
handler: handler as (event: unknown) => Promise<void>,
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
return () => {
|
|
155
|
-
eventHandlers.delete(id)
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Register a queue message handler.
|
|
161
|
-
*
|
|
162
|
-
* @param queueName - Queue name to listen for messages
|
|
163
|
-
* @param handler - Handler function for queue messages
|
|
164
|
-
* @returns Unsubscribe function
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* ```typescript
|
|
168
|
-
* import { onQueueMessage } from "@alienplatform/bindings"
|
|
169
|
-
*
|
|
170
|
-
* onQueueMessage("tasks", async (message) => {
|
|
171
|
-
* console.log("Processing task:", message.payload)
|
|
172
|
-
* await processTask(message.payload)
|
|
173
|
-
* })
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
export function onQueueMessage<T = unknown>(
|
|
177
|
-
queueName: string,
|
|
178
|
-
handler: (message: QueueMessageEvent<T>) => Promise<void>,
|
|
179
|
-
): () => void {
|
|
180
|
-
const id = `queue-${++handlerIdCounter}`
|
|
181
|
-
|
|
182
|
-
eventHandlers.set(id, {
|
|
183
|
-
registration: {
|
|
184
|
-
id,
|
|
185
|
-
type: "queue",
|
|
186
|
-
source: queueName,
|
|
187
|
-
},
|
|
188
|
-
handler: handler as (event: unknown) => Promise<void>,
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
return () => {
|
|
192
|
-
eventHandlers.delete(id)
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Get all registered event handlers.
|
|
198
|
-
*
|
|
199
|
-
* @internal
|
|
200
|
-
*/
|
|
201
|
-
export function getEventHandlers(): Map<
|
|
202
|
-
string,
|
|
203
|
-
{
|
|
204
|
-
registration: EventRegistration
|
|
205
|
-
handler: (event: unknown) => Promise<void>
|
|
206
|
-
}
|
|
207
|
-
> {
|
|
208
|
-
return eventHandlers
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* Event loop runner for processing tasks from the control plane.
|
|
213
|
-
*
|
|
214
|
-
* @internal
|
|
215
|
-
*/
|
|
216
|
-
export class EventLoop {
|
|
217
|
-
private readonly client: GeneratedClient
|
|
218
|
-
private readonly applicationId: string
|
|
219
|
-
private running = false
|
|
220
|
-
|
|
221
|
-
constructor(channel: Channel, applicationId: string) {
|
|
222
|
-
this.client = createClient(ControlServiceDefinition, channel)
|
|
223
|
-
this.applicationId = applicationId
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Register all event handlers with the control plane.
|
|
228
|
-
*/
|
|
229
|
-
async registerHandlers(): Promise<void> {
|
|
230
|
-
// Register event handlers
|
|
231
|
-
for (const { registration } of eventHandlers.values()) {
|
|
232
|
-
await wrapGrpcCall(
|
|
233
|
-
"ControlService",
|
|
234
|
-
"RegisterEventHandler",
|
|
235
|
-
async () => {
|
|
236
|
-
await this.client.registerEventHandler({
|
|
237
|
-
handlerType: registration.type,
|
|
238
|
-
resourceName: registration.source,
|
|
239
|
-
})
|
|
240
|
-
},
|
|
241
|
-
{},
|
|
242
|
-
)
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Register command handlers
|
|
246
|
-
for (const command of getCommands().values()) {
|
|
247
|
-
await wrapGrpcCall(
|
|
248
|
-
"ControlService",
|
|
249
|
-
"RegisterEventHandler",
|
|
250
|
-
async () => {
|
|
251
|
-
await this.client.registerEventHandler({
|
|
252
|
-
handlerType: "command",
|
|
253
|
-
resourceName: command.name,
|
|
254
|
-
})
|
|
255
|
-
},
|
|
256
|
-
{},
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Start the event loop.
|
|
263
|
-
*/
|
|
264
|
-
async start(): Promise<void> {
|
|
265
|
-
this.running = true
|
|
266
|
-
|
|
267
|
-
while (this.running) {
|
|
268
|
-
try {
|
|
269
|
-
await this.processTasks()
|
|
270
|
-
} catch (error) {
|
|
271
|
-
// Log error and continue
|
|
272
|
-
console.error("Error processing tasks:", error)
|
|
273
|
-
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Stop the event loop.
|
|
280
|
-
*/
|
|
281
|
-
stop(): void {
|
|
282
|
-
this.running = false
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
private async processTasks(): Promise<void> {
|
|
286
|
-
const stream = this.client.waitForTasks({
|
|
287
|
-
applicationId: this.applicationId,
|
|
288
|
-
})
|
|
289
|
-
|
|
290
|
-
for await (const task of stream) {
|
|
291
|
-
await this.handleTask(task)
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
private async handleTask(task: Task): Promise<void> {
|
|
296
|
-
try {
|
|
297
|
-
// Handle ARC commands
|
|
298
|
-
if (task.arcCommand) {
|
|
299
|
-
const result = await this.handleArcCommand(task.arcCommand)
|
|
300
|
-
await this.sendTaskResult(task.taskId, { success: true, data: result })
|
|
301
|
-
return
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Handle events (storage, cron, queue)
|
|
305
|
-
let matchedEntry: { handler: (event: unknown) => Promise<void> } | undefined
|
|
306
|
-
|
|
307
|
-
for (const entry of eventHandlers.values()) {
|
|
308
|
-
if (task.storageEvent && entry.registration.type === "storage") {
|
|
309
|
-
if (entry.registration.source === task.storageEvent.bucket) {
|
|
310
|
-
matchedEntry = entry
|
|
311
|
-
break
|
|
312
|
-
}
|
|
313
|
-
} else if (task.cronEvent && entry.registration.type === "cron") {
|
|
314
|
-
if (entry.registration.source === task.cronEvent.scheduleName) {
|
|
315
|
-
matchedEntry = entry
|
|
316
|
-
break
|
|
317
|
-
}
|
|
318
|
-
} else if (task.queueMessage && entry.registration.type === "queue") {
|
|
319
|
-
if (entry.registration.source === task.queueMessage.source) {
|
|
320
|
-
matchedEntry = entry
|
|
321
|
-
break
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (!matchedEntry) {
|
|
327
|
-
console.warn(`No handler found for task: ${task.taskId}`)
|
|
328
|
-
return
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (task.storageEvent) {
|
|
332
|
-
const storageEvent = this.fromProtoStorageEvent(task.storageEvent)
|
|
333
|
-
await matchedEntry.handler(storageEvent)
|
|
334
|
-
} else if (task.cronEvent) {
|
|
335
|
-
const cronEvent = this.fromProtoCronEvent(task.cronEvent)
|
|
336
|
-
await matchedEntry.handler(cronEvent)
|
|
337
|
-
} else if (task.queueMessage) {
|
|
338
|
-
const queueEvent = this.fromProtoQueueMessage(task.queueMessage)
|
|
339
|
-
await matchedEntry.handler(queueEvent)
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Report success
|
|
343
|
-
await this.sendTaskResult(task.taskId, { success: true })
|
|
344
|
-
} catch (error) {
|
|
345
|
-
// Report error
|
|
346
|
-
await this.sendTaskResult(task.taskId, {
|
|
347
|
-
success: false,
|
|
348
|
-
error: error instanceof Error ? error.message : String(error),
|
|
349
|
-
})
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
private async handleArcCommand(command: ProtoArcCommand): Promise<unknown> {
|
|
354
|
-
// Parse command parameters
|
|
355
|
-
let params: unknown = {}
|
|
356
|
-
if (command.params && command.params.length > 0) {
|
|
357
|
-
try {
|
|
358
|
-
const text = new TextDecoder().decode(command.params)
|
|
359
|
-
params = JSON.parse(text)
|
|
360
|
-
} catch {
|
|
361
|
-
// If not valid JSON, use empty object
|
|
362
|
-
params = {}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// Run the command
|
|
367
|
-
return await runCommand(command.commandName, params)
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
private async sendTaskResult(
|
|
371
|
-
taskId: string,
|
|
372
|
-
result: { success: boolean; error?: string; data?: unknown },
|
|
373
|
-
): Promise<void> {
|
|
374
|
-
await wrapGrpcCall(
|
|
375
|
-
"ControlService",
|
|
376
|
-
"SendTaskResult",
|
|
377
|
-
async () => {
|
|
378
|
-
if (result.success) {
|
|
379
|
-
const responseData = result.data
|
|
380
|
-
? new TextEncoder().encode(JSON.stringify(result.data))
|
|
381
|
-
: new Uint8Array()
|
|
382
|
-
await this.client.sendTaskResult({
|
|
383
|
-
taskId,
|
|
384
|
-
success: { responseData },
|
|
385
|
-
})
|
|
386
|
-
} else {
|
|
387
|
-
await this.client.sendTaskResult({
|
|
388
|
-
taskId,
|
|
389
|
-
error: { code: "ERROR", message: result.error ?? "Unknown error" },
|
|
390
|
-
})
|
|
391
|
-
}
|
|
392
|
-
},
|
|
393
|
-
{},
|
|
394
|
-
)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Transform proto StorageEvent to core StorageEvent type.
|
|
399
|
-
*/
|
|
400
|
-
private fromProtoStorageEvent(proto: ProtoStorageEvent): StorageEvent {
|
|
401
|
-
const eventTypeMap: Record<string, StorageEventType> = {
|
|
402
|
-
created: "created",
|
|
403
|
-
deleted: "deleted",
|
|
404
|
-
copied: "copied",
|
|
405
|
-
metadata_updated: "metadataUpdated",
|
|
406
|
-
restored: "restored",
|
|
407
|
-
tier_changed: "tierChanged",
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
return {
|
|
411
|
-
eventType: eventTypeMap[proto.eventType] ?? ("unknown" as StorageEventType),
|
|
412
|
-
bucketName: proto.bucket,
|
|
413
|
-
objectKey: proto.key,
|
|
414
|
-
timestamp: proto.timestamp?.toISOString() ?? new Date().toISOString(),
|
|
415
|
-
size: proto.size ? Number(proto.size) : undefined,
|
|
416
|
-
contentType: proto.contentType || undefined,
|
|
417
|
-
// These fields are not in the proto but are in the core type
|
|
418
|
-
etag: undefined,
|
|
419
|
-
metadata: undefined,
|
|
420
|
-
copySource: undefined,
|
|
421
|
-
previousTier: undefined,
|
|
422
|
-
currentTier: undefined,
|
|
423
|
-
region: undefined,
|
|
424
|
-
versionId: undefined,
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
/**
|
|
429
|
-
* Transform proto CronEvent to CronEvent type.
|
|
430
|
-
*/
|
|
431
|
-
private fromProtoCronEvent(proto: ProtoCronEvent): CronEvent {
|
|
432
|
-
return {
|
|
433
|
-
scheduleName: proto.scheduleName,
|
|
434
|
-
timestamp: proto.scheduledTime?.toISOString() ?? new Date().toISOString(),
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Transform proto QueueMessage to QueueMessageEvent type.
|
|
440
|
-
*/
|
|
441
|
-
private fromProtoQueueMessage<T>(proto: ProtoQueueMessage): QueueMessageEvent<T> {
|
|
442
|
-
// Proto payload is Uint8Array (JSON bytes), parse it
|
|
443
|
-
let payload: unknown = null
|
|
444
|
-
if (proto.payload && proto.payload.length > 0) {
|
|
445
|
-
try {
|
|
446
|
-
const text = new TextDecoder().decode(proto.payload)
|
|
447
|
-
payload = JSON.parse(text)
|
|
448
|
-
} catch {
|
|
449
|
-
// If not valid JSON, use raw bytes
|
|
450
|
-
payload = proto.payload
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
return {
|
|
455
|
-
id: proto.id,
|
|
456
|
-
source: proto.source,
|
|
457
|
-
receiptHandle: proto.receiptHandle,
|
|
458
|
-
payload: payload as T,
|
|
459
|
-
attemptCount: proto.attemptCount,
|
|
460
|
-
timestamp: proto.timestamp ?? new Date(),
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|