@gcoredev/proxy-wasm-sdk-as 1.0.2 → 1.2.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.
@@ -1,4 +1,12 @@
1
- import { getBaseContext, getContext, getRootContext, ensureContext, ensureRootContext, deleteContext, PeerTypeValues } from "./runtime";
1
+ import {
2
+ getBaseContext,
3
+ getContext,
4
+ getRootContext,
5
+ ensureContext,
6
+ ensureRootContext,
7
+ deleteContext,
8
+ PeerTypeValues,
9
+ } from "./runtime";
2
10
 
3
11
  ///// CALLS IN
4
12
  type FilterStatus = i32;
@@ -10,17 +18,26 @@ type GrpcStatus = i32;
10
18
  type WasmOnDoneResult = u32;
11
19
 
12
20
  // Calls in.
13
- export function proxy_abi_version_0_2_1(): void { }
21
+ export function proxy_abi_version_0_2_1(): void {}
14
22
 
15
- export function proxy_on_vm_start(root_context_id: u32, configuration_size: u32): u32 {
23
+ export function proxy_on_vm_start(
24
+ root_context_id: u32,
25
+ configuration_size: u32
26
+ ): u32 {
16
27
  let root_context = getRootContext(root_context_id);
17
28
  return root_context.onStart(configuration_size) ? 1 : 0;
18
29
  }
19
- export function proxy_validate_configuration(root_context_id: u32, configuration_size: u32): u32 {
30
+ export function proxy_validate_configuration(
31
+ root_context_id: u32,
32
+ configuration_size: u32
33
+ ): u32 {
20
34
  let root_context = getRootContext(root_context_id);
21
35
  return root_context.validateConfiguration(configuration_size) ? 1 : 0;
22
36
  }
23
- export function proxy_on_configure(root_context_id: u32, configuration_size: u32): u32 {
37
+ export function proxy_on_configure(
38
+ root_context_id: u32,
39
+ configuration_size: u32
40
+ ): u32 {
24
41
  let root_context = getRootContext(root_context_id);
25
42
  return root_context.onConfigure(configuration_size) ? 1 : 0;
26
43
  }
@@ -28,7 +45,11 @@ export function proxy_on_tick(root_context_id: u32): void {
28
45
  let root_context = getRootContext(root_context_id);
29
46
  root_context.onTick();
30
47
  }
31
- export function proxy_on_foreign_function(root_context_id: u32, function_id: u32, data_size: u32): void {
48
+ export function proxy_on_foreign_function(
49
+ root_context_id: u32,
50
+ function_id: u32,
51
+ data_size: u32
52
+ ): void {
32
53
  // TODO: implement me
33
54
  }
34
55
 
@@ -37,7 +58,10 @@ export function proxy_on_queue_ready(root_context_id: u32, token: u32): void {
37
58
  root_context.onQueueReady(token);
38
59
  }
39
60
  // Stream calls.
40
- export function proxy_on_context_create(context_id: u32, root_context_id: u32): void {
61
+ export function proxy_on_context_create(
62
+ context_id: u32,
63
+ root_context_id: u32
64
+ ): void {
41
65
  if (root_context_id != 0) {
42
66
  ensureContext(context_id, root_context_id);
43
67
  } else {
@@ -45,77 +69,159 @@ export function proxy_on_context_create(context_id: u32, root_context_id: u32):
45
69
  }
46
70
  }
47
71
 
48
- export function proxy_on_request_headers(context_id: u32, headers: u32, end_of_stream: u32): FilterHeadersStatus {
72
+ export function proxy_on_request_headers(
73
+ context_id: u32,
74
+ headers: u32,
75
+ end_of_stream: u32
76
+ ): FilterHeadersStatus {
49
77
  let ctx = getContext(context_id);
50
- return ctx.onRequestHeaders(headers, end_of_stream != 0) as FilterHeadersStatus;
51
- }
52
- export function proxy_on_request_body(context_id: u32, body_buffer_length: u32, end_of_stream: u32): FilterDataStatus {
78
+ return ctx.onRequestHeaders(
79
+ headers,
80
+ end_of_stream != 0
81
+ ) as FilterHeadersStatus;
82
+ }
83
+ export function proxy_on_request_body(
84
+ context_id: u32,
85
+ body_buffer_length: u32,
86
+ end_of_stream: u32
87
+ ): FilterDataStatus {
53
88
  let ctx = getContext(context_id);
54
- return ctx.onRequestBody(body_buffer_length, end_of_stream != 0) as FilterDataStatus;
55
- }
56
- export function proxy_on_request_trailers(context_id: u32, trailers: u32): FilterTrailersStatus {
89
+ return ctx.onRequestBody(
90
+ body_buffer_length,
91
+ end_of_stream != 0
92
+ ) as FilterDataStatus;
93
+ }
94
+ export function proxy_on_request_trailers(
95
+ context_id: u32,
96
+ trailers: u32
97
+ ): FilterTrailersStatus {
57
98
  let ctx = getContext(context_id);
58
99
  return ctx.onRequestTrailers(trailers) as FilterTrailersStatus;
59
100
  }
60
- export function proxy_on_request_metadata(context_id: u32, nelements: u32): FilterMetadataStatus {
101
+ export function proxy_on_request_metadata(
102
+ context_id: u32,
103
+ nelements: u32
104
+ ): FilterMetadataStatus {
61
105
  let ctx = getContext(context_id);
62
106
  return ctx.onRequestMetadata(nelements) as FilterMetadataStatus;
63
107
  }
64
- export function proxy_on_response_headers(context_id: u32, headers: u32, end_of_stream: u32): FilterHeadersStatus {
108
+ export function proxy_on_response_headers(
109
+ context_id: u32,
110
+ headers: u32,
111
+ end_of_stream: u32
112
+ ): FilterHeadersStatus {
65
113
  let ctx = getContext(context_id);
66
- return ctx.onResponseHeaders(headers, end_of_stream != 0) as FilterHeadersStatus;
67
- }
68
- export function proxy_on_response_body(context_id: u32, body_buffer_length: u32, end_of_stream: u32): FilterDataStatus {
114
+ return ctx.onResponseHeaders(
115
+ headers,
116
+ end_of_stream != 0
117
+ ) as FilterHeadersStatus;
118
+ }
119
+ export function proxy_on_response_body(
120
+ context_id: u32,
121
+ body_buffer_length: u32,
122
+ end_of_stream: u32
123
+ ): FilterDataStatus {
69
124
  let ctx = getContext(context_id);
70
- return ctx.onResponseBody(body_buffer_length, end_of_stream != 0) as FilterDataStatus;
71
- }
72
- export function proxy_on_response_trailers(context_id: u32, trailers: u32): FilterTrailersStatus {
125
+ return ctx.onResponseBody(
126
+ body_buffer_length,
127
+ end_of_stream != 0
128
+ ) as FilterDataStatus;
129
+ }
130
+ export function proxy_on_response_trailers(
131
+ context_id: u32,
132
+ trailers: u32
133
+ ): FilterTrailersStatus {
73
134
  let ctx = getContext(context_id);
74
135
  return ctx.onResponseTrailers(trailers) as FilterTrailersStatus;
75
136
  }
76
- export function proxy_on_response_metadata(context_id: u32, nelements: u32): FilterMetadataStatus {
137
+ export function proxy_on_response_metadata(
138
+ context_id: u32,
139
+ nelements: u32
140
+ ): FilterMetadataStatus {
77
141
  let ctx = getContext(context_id);
78
142
  return ctx.onResponseMetadata(nelements) as FilterMetadataStatus;
79
143
  }
80
144
 
81
145
  // HTTP/gRPC.
82
- export function proxy_on_http_call_response(context_id: u32, token: u32, headers: u32, body_size: u32, trailers: u32): void {
146
+ export function proxy_on_http_call_response(
147
+ context_id: u32,
148
+ token: u32,
149
+ headers: u32,
150
+ body_size: u32,
151
+ trailers: u32
152
+ ): void {
83
153
  let ctx = getRootContext(context_id);
84
154
  ctx.onHttpCallResponse(token, headers, body_size, trailers);
85
155
  }
86
- export function proxy_on_grpc_receive_initial_metadata(context_id: u32, token: u32, headers: u32): void {
87
- getRootContext(context_id).on_grpc_receive_initial_metadata(token, headers)
88
- }
89
- export function proxy_on_grpc_trailing_metadata(context_id: u32, token: u32, trailers: u32): void {
90
- getRootContext(context_id).on_grpc_trailing_metadata(token, trailers)
91
- }
92
- export function proxy_on_grpc_receive(context_id: u32, token: u32, response_size: u32): void {
93
- getRootContext(context_id).on_grpc_receive(token, response_size)
94
- }
95
- export function proxy_on_grpc_close(context_id: u32, token: u32, status_code: u32): void {
96
- getRootContext(context_id).on_grpc_close(token, status_code)
156
+ export function proxy_on_grpc_receive_initial_metadata(
157
+ context_id: u32,
158
+ token: u32,
159
+ headers: u32
160
+ ): void {
161
+ getRootContext(context_id).on_grpc_receive_initial_metadata(token, headers);
162
+ }
163
+ export function proxy_on_grpc_trailing_metadata(
164
+ context_id: u32,
165
+ token: u32,
166
+ trailers: u32
167
+ ): void {
168
+ getRootContext(context_id).on_grpc_trailing_metadata(token, trailers);
169
+ }
170
+ export function proxy_on_grpc_receive(
171
+ context_id: u32,
172
+ token: u32,
173
+ response_size: u32
174
+ ): void {
175
+ getRootContext(context_id).on_grpc_receive(token, response_size);
176
+ }
177
+ export function proxy_on_grpc_close(
178
+ context_id: u32,
179
+ token: u32,
180
+ status_code: u32
181
+ ): void {
182
+ getRootContext(context_id).on_grpc_close(token, status_code);
97
183
  }
98
184
 
99
185
  // NETWORK_FILTER support (TCP and, perhaps one day, UDP).
100
- export function proxy_on_downstream_data(context_id: u32, body_buffer_length: u32, end_of_stream: u32): FilterStatus {
101
- let ctx = getContext(context_id);
102
- return ctx.onDownstreamData(body_buffer_length, end_of_stream != 0) as FilterStatus;
103
- }
104
- export function proxy_on_upstream_data(context_id: u32, body_buffer_length: u32, end_of_stream: u32): FilterStatus {
105
- let ctx = getContext(context_id);
106
- return ctx.onUpstreamData(body_buffer_length, end_of_stream != 0) as FilterStatus;
107
- }
108
- export function proxy_on_upstream_connection_close(context_id: u32, peer_type: u32): void {
109
- let ctx = getContext(context_id);
110
- ctx.onUpstreamConnectionClose(peer_type as PeerTypeValues);
186
+ export function proxy_on_downstream_data(
187
+ context_id: u32,
188
+ body_buffer_length: u32,
189
+ end_of_stream: u32
190
+ ): FilterStatus {
191
+ let ctx = getContext(context_id);
192
+ return ctx.onDownstreamData(
193
+ body_buffer_length,
194
+ end_of_stream != 0
195
+ ) as FilterStatus;
196
+ }
197
+ export function proxy_on_upstream_data(
198
+ context_id: u32,
199
+ body_buffer_length: u32,
200
+ end_of_stream: u32
201
+ ): FilterStatus {
202
+ let ctx = getContext(context_id);
203
+ return ctx.onUpstreamData(
204
+ body_buffer_length,
205
+ end_of_stream != 0
206
+ ) as FilterStatus;
207
+ }
208
+ export function proxy_on_upstream_connection_close(
209
+ context_id: u32,
210
+ peer_type: u32
211
+ ): void {
212
+ let ctx = getContext(context_id);
213
+ ctx.onUpstreamConnectionClose(peer_type as PeerTypeValues);
111
214
  }
112
- export function proxy_on_downstream_connection_close(context_id: u32, peer_type: u32): void {
113
- let ctx = getContext(context_id);
114
- ctx.onDownstreamConnectionClose(peer_type as PeerTypeValues);
215
+ export function proxy_on_downstream_connection_close(
216
+ context_id: u32,
217
+ peer_type: u32
218
+ ): void {
219
+ let ctx = getContext(context_id);
220
+ ctx.onDownstreamConnectionClose(peer_type as PeerTypeValues);
115
221
  }
116
222
  export function proxy_on_new_connection(context_id: u32): FilterStatus {
117
- let ctx = getContext(context_id);
118
- return ctx.onNewConnection() as FilterStatus;
223
+ let ctx = getContext(context_id);
224
+ return ctx.onNewConnection() as FilterStatus;
119
225
  }
120
226
 
121
227
  // The stream/vm has completed.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Function to get the value for the provided environment variable name.
3
+ * @param {string} name - The name of the environment variable.
4
+ * @returns {string} The value of the environment variable.
5
+ */
6
+ function getEnvVar(name: string): string {
7
+ const hasKey = process.env.has(name);
8
+ if (hasKey) {
9
+ return process.env.get(name);
10
+ }
11
+ return "";
12
+ }
13
+
14
+ export { getEnvVar };
@@ -0,0 +1,4 @@
1
+ export * from "./env";
2
+ export * from "./kvStore";
3
+ export * from "./secrets";
4
+ export * from "./utils";
@@ -0,0 +1,228 @@
1
+ import * as imports from "../imports";
2
+
3
+ import {
4
+ globalArrayBufferReference,
5
+ Reference,
6
+ WasmResultValues,
7
+ } from "../runtime";
8
+ import { ItemParser, StringParser, parseBufferToList } from "./utils";
9
+
10
+ /**
11
+ * KvStore provides access to the FastEdge Key-Value store
12
+ */
13
+ export class KvStore {
14
+ private handle: u32;
15
+
16
+ private constructor(handle: u32) {
17
+ this.handle = handle;
18
+ }
19
+
20
+ /**
21
+ * Opens a connection to the specified KV store
22
+ * @param storeName - The name of the KV store to open
23
+ * @returns A new KvStore instance or null if the store cannot be opened
24
+ */
25
+ static open(storeName: string): KvStore | null {
26
+ const buffer = String.UTF8.encode(storeName);
27
+ const returnHandlerRef = new Reference<u32>();
28
+
29
+ const status = imports.proxy_kv_store_open(
30
+ changetype<usize>(buffer),
31
+ buffer.byteLength,
32
+ returnHandlerRef.ptr() as u32
33
+ );
34
+
35
+ if (status == WasmResultValues.Ok) {
36
+ return new KvStore(returnHandlerRef.data);
37
+ }
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * Retrieves the value associated with the given key from the KV store.
43
+ * @param {string} key The key to retrieve the value for.
44
+ * @returns {ArrayBuffer | null} The value associated with the key, or null if not found.
45
+ */
46
+ get(key: string): ArrayBuffer | null {
47
+ const buffer = String.UTF8.encode(key);
48
+ const status = imports.proxy_kv_store_get(
49
+ this.getHandle(),
50
+ changetype<usize>(buffer),
51
+ buffer.byteLength,
52
+ globalArrayBufferReference.bufferPtr(),
53
+ globalArrayBufferReference.sizePtr()
54
+ );
55
+ if (status == WasmResultValues.Ok) {
56
+ return globalArrayBufferReference.toArrayBuffer();
57
+ }
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Retrieves all key prefix matches from the KV store.
63
+ * @param {string} pattern The prefix pattern to match keys against. e.g. 'foo*' ( Must include wildcard )
64
+ * @returns {Array<string>} The keys matching the pattern, or empty array if none found.
65
+ */
66
+ scan(pattern: string): Array<string> {
67
+ const match = String.UTF8.encode(pattern);
68
+ const status = imports.proxy_kv_store_scan(
69
+ this.getHandle(),
70
+ changetype<usize>(match),
71
+ match.byteLength,
72
+ globalArrayBufferReference.bufferPtr(),
73
+ globalArrayBufferReference.sizePtr()
74
+ );
75
+ if (status == WasmResultValues.Ok) {
76
+ return parseBufferToList<string>(
77
+ globalArrayBufferReference.toArrayBuffer(),
78
+ new StringParser()
79
+ );
80
+ }
81
+ return new Array<string>();
82
+ }
83
+
84
+ /**
85
+ * Retrieves all the values from ZSet with scores between the given range.
86
+ * @param {string} key The key for the Sorted Set.
87
+ * @param {f64} min The minimum score for the range.
88
+ * @param {f64} max The maximum score for the range.
89
+ * @returns {Array<ValueScoreTuple>} Array of [value, score] tuples within range for the key, or an empty array if none found.
90
+ */
91
+ zrangeByScore(key: string, min: f64, max: f64): Array<ValueScoreTuple> {
92
+ const keyBuffer = String.UTF8.encode(key);
93
+ const status = imports.proxy_kv_store_zrange_by_score(
94
+ this.getHandle(),
95
+ changetype<usize>(keyBuffer),
96
+ keyBuffer.byteLength,
97
+ min,
98
+ max,
99
+ globalArrayBufferReference.bufferPtr(),
100
+ globalArrayBufferReference.sizePtr()
101
+ );
102
+ if (status == WasmResultValues.Ok) {
103
+ return parseBufferToList<ValueScoreTuple>(
104
+ globalArrayBufferReference.toArrayBuffer(),
105
+ new ValueScoreTupleParser()
106
+ );
107
+ }
108
+ return new Array<ValueScoreTuple>();
109
+ }
110
+
111
+ /**
112
+ * Retrieves all value prefix matches from the KV ZSet.
113
+ * @param {string} key The key for the Sorted Set.
114
+ * @param {string} pattern The prefix pattern to match values against. e.g. 'foo*' ( Must include wildcard )
115
+ * @returns {Array<ValueScoreTuple>} Array of [value, score] tuples which match the prefix pattern, or an empty array if none found.
116
+ */
117
+ zscan(key: string, pattern: string): Array<ValueScoreTuple> {
118
+ const match = String.UTF8.encode(pattern);
119
+ const keyBuffer = String.UTF8.encode(key);
120
+ const status = imports.proxy_kv_store_zscan(
121
+ this.getHandle(),
122
+ changetype<usize>(keyBuffer),
123
+ keyBuffer.byteLength,
124
+ changetype<usize>(match),
125
+ match.byteLength,
126
+ globalArrayBufferReference.bufferPtr(),
127
+ globalArrayBufferReference.sizePtr()
128
+ );
129
+ if (status == WasmResultValues.Ok) {
130
+ return parseBufferToList<ValueScoreTuple>(
131
+ globalArrayBufferReference.toArrayBuffer(),
132
+ new ValueScoreTupleParser()
133
+ );
134
+ }
135
+ return new Array<ValueScoreTuple>();
136
+ }
137
+
138
+ /**
139
+ * Checks if a given item exists within the KV stores Bloom Filter.
140
+ * @param {string} key The key for the Bloom Filter.
141
+ * @param {string} item The item to check for existence.
142
+ * @returns {boolean} True if the item exists, false otherwise.
143
+ */
144
+ bfExists(key: string, item: string): boolean {
145
+ const keyBuffer = String.UTF8.encode(key);
146
+ const itemBuffer = String.UTF8.encode(item);
147
+ const returnHandlerRef = new Reference<u32>();
148
+
149
+ const status = imports.proxy_kv_store_bf_exists(
150
+ this.getHandle(),
151
+ changetype<usize>(keyBuffer),
152
+ keyBuffer.byteLength,
153
+ changetype<usize>(itemBuffer),
154
+ itemBuffer.byteLength,
155
+ returnHandlerRef.ptr() as u32
156
+ );
157
+
158
+ if (status == WasmResultValues.Ok) {
159
+ return returnHandlerRef.data != 0;
160
+ }
161
+ return false;
162
+ }
163
+
164
+ /**
165
+ * Gets the handle of this KV store instance
166
+ * @returns The handle
167
+ */
168
+ getHandle(): u32 {
169
+ return this.handle;
170
+ }
171
+ }
172
+
173
+ export class ValueScoreTuple {
174
+ value: ArrayBuffer;
175
+ score: number;
176
+
177
+ constructor(value: ArrayBuffer, score: number) {
178
+ this.value = value;
179
+ this.score = score;
180
+ }
181
+ }
182
+
183
+ // ValueScoreTuple parser implementation
184
+ class ValueScoreTupleParser extends ItemParser<ValueScoreTuple> {
185
+ parseItem(
186
+ buffer: ArrayBuffer,
187
+ dataIndex: u32,
188
+ itemSize: u32
189
+ ): ValueScoreTuple | null {
190
+ // Each item contains value bytes + 8-byte f64 score
191
+ const scoreSize: u32 = 8; // f64 = 8 bytes
192
+ if (itemSize < scoreSize) {
193
+ // Invalid item, skip
194
+ return null;
195
+ }
196
+
197
+ const valueSize = itemSize - scoreSize;
198
+
199
+ // Extract value data
200
+ const valueData = buffer.slice(
201
+ <i32>dataIndex,
202
+ <i32>(dataIndex + valueSize)
203
+ );
204
+
205
+ // Extract score data (last 8 bytes of the item)
206
+ const scoreData = buffer.slice(
207
+ <i32>(dataIndex + valueSize),
208
+ <i32>(dataIndex + itemSize)
209
+ );
210
+
211
+ // Convert score bytes to f64 (little-endian)
212
+ const scoreBytes = new Uint8Array(scoreSize);
213
+ const sourceScoreBytes = Uint8Array.wrap(scoreData);
214
+ for (let j: u32 = 0; j < scoreSize; j++) {
215
+ scoreBytes[j] = sourceScoreBytes[j];
216
+ }
217
+
218
+ // Convert little-endian bytes to f64
219
+ const scoreView = new DataView(scoreBytes.buffer);
220
+ const score = scoreView.getFloat64(0, true); // true = little-endian
221
+
222
+ return new ValueScoreTuple(valueData, score);
223
+ }
224
+
225
+ createEmptyArray(): Array<ValueScoreTuple> {
226
+ return new Array<ValueScoreTuple>();
227
+ }
228
+ }
@@ -0,0 +1,51 @@
1
+ import * as imports from "../imports";
2
+
3
+ import { globalArrayBufferReference, WasmResultValues } from "../runtime";
4
+
5
+ /**
6
+ * Function to get the value for the provided secret variable name.
7
+ * @param {string} name - The name of the secret variable.
8
+ * @returns {string} The value of the secret variable.
9
+ */
10
+ function getSecretVar(name: string): string {
11
+ const buffer = String.UTF8.encode(name);
12
+ const status = imports.proxy_get_secret(
13
+ changetype<usize>(buffer),
14
+ buffer.byteLength,
15
+ globalArrayBufferReference.bufferPtr(),
16
+ globalArrayBufferReference.sizePtr()
17
+ );
18
+ if (status == WasmResultValues.Ok) {
19
+ const arrBuff = globalArrayBufferReference.toArrayBuffer();
20
+ if (arrBuff.byteLength > 0) {
21
+ return String.UTF8.decode(arrBuff);
22
+ }
23
+ }
24
+ return "";
25
+ }
26
+
27
+ /**
28
+ * Function to get the value for the provided secret variable name from a specific slot.
29
+ * @param {string} name - The name of the secret variable.
30
+ * @param {u32} effectiveAt - The slot index of the secret. (effectiveAt >= secret_slots.slot)
31
+ * @returns {string} The value of the secret variable.
32
+ */
33
+ function getSecretVarEffectiveAt(name: string, effectiveAt: u32): string {
34
+ const buffer = String.UTF8.encode(name);
35
+ const status = imports.proxy_get_effective_at_secret(
36
+ changetype<usize>(buffer),
37
+ buffer.byteLength,
38
+ effectiveAt,
39
+ globalArrayBufferReference.bufferPtr(),
40
+ globalArrayBufferReference.sizePtr()
41
+ );
42
+ if (status == WasmResultValues.Ok) {
43
+ const arrBuff = globalArrayBufferReference.toArrayBuffer();
44
+ if (arrBuff.byteLength > 0) {
45
+ return String.UTF8.decode(arrBuff);
46
+ }
47
+ }
48
+ return "";
49
+ }
50
+
51
+ export { getSecretVar, getSecretVarEffectiveAt };
@@ -0,0 +1,2 @@
1
+ export { getCurrentTime, log, LogLevelValues, setLogLevel } from "./runtime";
2
+ export { ItemParser, StringParser, parseBufferToList } from "./listParser";
@@ -0,0 +1,70 @@
1
+ // Abstract base class for item parsers
2
+ abstract class ItemParser<T> {
3
+ abstract parseItem(
4
+ buffer: ArrayBuffer,
5
+ dataIndex: u32,
6
+ itemSize: u32
7
+ ): T | null;
8
+ abstract createEmptyArray(): Array<T>;
9
+ }
10
+
11
+ // String parser implementation
12
+ class StringParser extends ItemParser<string> {
13
+ parseItem(buffer: ArrayBuffer, dataIndex: u32, itemSize: u32): string | null {
14
+ // Extract string data
15
+ const stringData = buffer.slice(
16
+ <i32>dataIndex,
17
+ <i32>(dataIndex + itemSize)
18
+ );
19
+ return String.UTF8.decode(stringData);
20
+ }
21
+
22
+ createEmptyArray(): Array<string> {
23
+ return new Array<string>();
24
+ }
25
+ }
26
+
27
+ // Generic deserialization function
28
+ function parseBufferToList<T>(
29
+ buffer: ArrayBuffer,
30
+ parser: ItemParser<T>
31
+ ): Array<T> {
32
+ // Check if buffer is valid
33
+ if (!buffer || buffer.byteLength === 0) {
34
+ return parser.createEmptyArray();
35
+ }
36
+
37
+ // Read number of items
38
+ const numItems = Uint32Array.wrap(buffer, 0, 1)[0];
39
+
40
+ if (numItems === 0) {
41
+ return parser.createEmptyArray();
42
+ }
43
+
44
+ // Read sizes array
45
+ const sizes = Uint32Array.wrap(buffer, sizeof<u32>(), numItems);
46
+
47
+ // Start of actual data
48
+ let dataIndex: u32 = sizeof<u32>() * (1 + numItems);
49
+ const result = parser.createEmptyArray();
50
+
51
+ for (let i: u32 = 0; i < numItems; i++) {
52
+ const itemSize = sizes[i];
53
+
54
+ if (dataIndex + itemSize > <u32>buffer.byteLength) {
55
+ break;
56
+ }
57
+
58
+ const item = parser.parseItem(buffer, dataIndex, itemSize);
59
+ if (item !== null) {
60
+ result.push(item);
61
+ }
62
+
63
+ // Move to next item (including null terminator if present)
64
+ dataIndex += itemSize + 1; // +1 for null terminator
65
+ }
66
+
67
+ return result;
68
+ }
69
+
70
+ export { ItemParser, StringParser, parseBufferToList };
@@ -0,0 +1,31 @@
1
+ import { get_current_time_nanoseconds, LogLevelValues } from "../../runtime";
2
+
3
+ let logLevel: LogLevelValues = LogLevelValues.info;
4
+
5
+ /**
6
+ * Sets the logging level, proxy_get_log_level is not yet implemented in FastEdge.
7
+ * @param {LogLevelValues} level The logging level to set.
8
+ * @returns {void}
9
+ */
10
+ function setLogLevel(level: LogLevelValues): void {
11
+ logLevel = level;
12
+ }
13
+
14
+ /**
15
+ * Temporary fix for proxy_log not being implemented in FastEdge.
16
+ * The function relies on @assemblyscript/wasi-shim to print to standard output.
17
+ * @param {LogLevelValues} level The logging level to use.
18
+ * @param {string} logMessage The logging message to log.
19
+ * @returns {void}
20
+ */
21
+ function log(level: LogLevelValues, logMessage: string): void {
22
+ if (level >= logLevel) {
23
+ process.stdout.write(logMessage + "\n");
24
+ }
25
+ }
26
+
27
+ function getCurrentTime(): u64 {
28
+ return get_current_time_nanoseconds() / 1_000_000; // Convert nanoseconds to milliseconds
29
+ }
30
+
31
+ export { getCurrentTime, log, LogLevelValues, setLogLevel };
@@ -124,6 +124,10 @@ export declare function proxy_get_buffer_bytes(typ: BufferType, start: u32, leng
124
124
  // @ts-ignore: decorator
125
125
  @external("env", "proxy_get_buffer_status")
126
126
  export declare function proxy_get_buffer_status(typ: BufferType, length_ptr: ptr<usize>, flags_ptr: ptr<u32>): WasmResult;
127
+ // @ts-ignore: decorator
128
+ @external("env", "proxy_set_buffer_bytes")
129
+ export declare function proxy_set_buffer_bytes(typ: BufferType, start: u32, length: u32, ptr: ptr<ptr<char>>, size: ptr<usize>): WasmResult;
130
+
127
131
 
128
132
  // HTTP
129
133
  // @ts-ignore: decorator
@@ -197,3 +201,68 @@ export declare function proxy_get_effective_at_secret(
197
201
  return_value_size: usize
198
202
  ): u32;
199
203
 
204
+
205
+ // KV Store
206
+ // @ts-ignore: decorator
207
+ @external("env", "proxy_kv_store_open")
208
+ export declare function proxy_kv_store_open(
209
+ key_data: usize,
210
+ key_size: usize,
211
+ return_handle: u32
212
+ ): u32;
213
+
214
+ // @ts-ignore: decorator
215
+ @external("env", "proxy_kv_store_get")
216
+ export declare function proxy_kv_store_get(
217
+ handle: u32,
218
+ key_data: usize,
219
+ key_size: usize,
220
+ return_value_data: usize,
221
+ return_value_size: usize,
222
+ ): u32;
223
+
224
+ // @ts-ignore: decorator
225
+ @external("env", "proxy_kv_store_scan")
226
+ export declare function proxy_kv_store_scan(
227
+ handle: u32,
228
+ pattern_data: usize,
229
+ pattern_size: usize,
230
+ return_value_data: usize,
231
+ return_value_size: usize,
232
+ ): u32;
233
+
234
+ // @ts-ignore: decorator
235
+ @external("env", "proxy_kv_store_zrange_by_score")
236
+ export declare function proxy_kv_store_zrange_by_score(
237
+ handle: u32,
238
+ key_data: usize,
239
+ key_size: usize,
240
+ min: f64,
241
+ max: f64,
242
+ return_value_data: usize,
243
+ return_value_size: usize,
244
+ ): u32;
245
+
246
+ // @ts-ignore: decorator
247
+ @external("env", "proxy_kv_store_zscan")
248
+ export declare function proxy_kv_store_zscan(
249
+ handle: u32,
250
+ key_data: usize,
251
+ key_size: usize,
252
+ pattern_data: usize,
253
+ pattern_size: usize,
254
+ return_value_data: usize,
255
+ return_value_size: usize,
256
+ ): u32;
257
+
258
+ // @ts-ignore: decorator
259
+ @external("env", "proxy_kv_store_bf_exists")
260
+ export declare function proxy_kv_store_bf_exists(
261
+ handle: u32,
262
+ key_data: usize,
263
+ key_size: usize,
264
+ item_data: usize,
265
+ item_size: usize,
266
+ return_handle: u32,
267
+ ): u32;
268
+
package/assembly/index.ts CHANGED
@@ -1,15 +1,43 @@
1
1
  export {
2
- BaseContext, RootContext, Context, registerRootContext,
3
- BufferTypeValues, LogLevelValues, WasmResultValues, GrpcStatusValues,
4
- FilterStatusValues,
5
- FilterHeadersStatusValues,
6
- FilterMetadataStatusValues,
7
- FilterTrailersStatusValues,
8
- FilterDataStatusValues, stream_context,
9
- HeaderPair, Headers, makeHeaderPair,
10
- Gauge, Histogram, Counter, log,
11
- HttpCallback, send_local_response, send_http_response, continue_request, continue_response,
12
- proxy_set_effective_context, set_property, get_property, get_shared_data, GetSharedData, set_shared_data, set_tick_period_milliseconds,
13
- register_shared_queue, resolve_shared_queue, enqueue_shared_queue, dequeue_shared_queue,
14
- get_buffer_bytes, call_foreign_function, get_current_time_nanoseconds
2
+ BaseContext,
3
+ BufferTypeValues,
4
+ call_foreign_function,
5
+ Context,
6
+ continue_request,
7
+ continue_response,
8
+ Counter,
9
+ dequeue_shared_queue,
10
+ enqueue_shared_queue,
11
+ FilterDataStatusValues,
12
+ FilterHeadersStatusValues,
13
+ FilterMetadataStatusValues,
14
+ FilterStatusValues,
15
+ FilterTrailersStatusValues,
16
+ Gauge,
17
+ get_buffer_bytes,
18
+ get_current_time_nanoseconds,
19
+ get_property,
20
+ get_shared_data,
21
+ GetSharedData,
22
+ GrpcStatusValues,
23
+ HeaderPair,
24
+ Headers,
25
+ Histogram,
26
+ HttpCallback,
27
+ log,
28
+ LogLevelValues,
29
+ makeHeaderPair,
30
+ proxy_set_effective_context,
31
+ register_shared_queue,
32
+ registerRootContext,
33
+ resolve_shared_queue,
34
+ RootContext,
35
+ send_http_response,
36
+ send_local_response,
37
+ set_buffer_bytes,
38
+ set_property,
39
+ set_shared_data,
40
+ set_tick_period_milliseconds,
41
+ stream_context,
42
+ WasmResultValues,
15
43
  } from "./runtime";
@@ -3,6 +3,7 @@ import { free } from "./malloc";
3
3
 
4
4
  import { log as wasiLog } from "./fastedge";
5
5
 
6
+
6
7
  // abort function.
7
8
  // use with:
8
9
  // --use abort=index/abort_proc_exit
@@ -32,7 +33,6 @@ function CHECK_RESULT(c: imports.WasmResult): void {
32
33
  }
33
34
 
34
35
  /////////////// Access helpers
35
-
36
36
  export class Reference<T> {
37
37
  data: T;
38
38
 
@@ -55,8 +55,12 @@ class ArrayBufferReference {
55
55
  return changetype<usize>(this) + offsetof<ArrayBufferReference>("buffer");
56
56
  }
57
57
 
58
- // Before calling toArrayBuffer below, you must call out to the host to fill in the values.
59
- // toArrayBuffer below **must** be called once and only once.
58
+ /**
59
+ * Before calling toArrayBuffer below, you must call out to the host to fill in the values.
60
+ * toArrayBuffer below **must** be called once and only once.
61
+ * Host code used malloc to allocate this buffer.
62
+ * This method consumes the buffer and handles memory cleanup automatically.
63
+ */
60
64
  toArrayBuffer(): ArrayBuffer {
61
65
  if (this.size == 0) {
62
66
  return new ArrayBuffer(0);
@@ -71,6 +75,8 @@ class ArrayBufferReference {
71
75
  }
72
76
  }
73
77
 
78
+
79
+
74
80
  export const globalArrayBufferReference = new ArrayBufferReference();
75
81
  let globalU32Ref = new Reference<u32>();
76
82
  let globalLogLevelRef = new Reference<imports.LogLevel>();
@@ -256,8 +262,12 @@ export function get_current_time_nanoseconds(): u64 {
256
262
 
257
263
  export function get_property(path: string): ArrayBuffer {
258
264
  let buffer = String.UTF8.encode(path);
259
- CHECK_RESULT(imports.proxy_get_property(changetype<usize>(buffer), buffer.byteLength, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr()));
260
- return globalArrayBufferReference.toArrayBuffer();
265
+ const result = imports.proxy_get_property(changetype<usize>(buffer), buffer.byteLength, globalArrayBufferReference.bufferPtr(), globalArrayBufferReference.sizePtr());
266
+ CHECK_RESULT(result);
267
+ if (result == WasmResultValues.Ok) {
268
+ return globalArrayBufferReference.toArrayBuffer();
269
+ }
270
+ return new ArrayBuffer(0); // result == WasmResultValues.NotFound
261
271
  }
262
272
 
263
273
  export function set_property(path: string, data: ArrayBuffer): WasmResultValues {
@@ -627,6 +637,11 @@ export function get_buffer_bytes(typ: BufferTypeValues, start: u32, length: u32)
627
637
  return new ArrayBuffer(0);
628
638
  }
629
639
 
640
+ export function set_buffer_bytes(typ: BufferTypeValues, start: u32, length: u32, value: ArrayBuffer): WasmResultValues {
641
+ const result = imports.proxy_set_buffer_bytes(typ, start, length, changetype<usize>(value), value.byteLength);
642
+ return result
643
+ }
644
+
630
645
  // returning tuples is not supported.
631
646
  class BufferStatusResult {
632
647
  result: WasmResultValues;
@@ -1117,3 +1132,5 @@ export function registerRootContext(
1117
1132
  name: string): void {
1118
1133
  root_context_factory = context_factory;
1119
1134
  }
1135
+
1136
+
package/package.json CHANGED
@@ -1,4 +1,8 @@
1
1
  {
2
+ "name": "@gcoredev/proxy-wasm-sdk-as",
3
+ "description": "Use this SDK to write extensions for the proxy WASM ABI",
4
+ "version": "1.2.0",
5
+ "main": "assembly/index.ts",
2
6
  "scripts": {
3
7
  "asbuild:debug": "asc assembly/index.ts --target debug",
4
8
  "asbuild:release": "asc assembly/index.ts --target release",
@@ -15,10 +19,6 @@
15
19
  "minimist": ">=1.2.2",
16
20
  "typedoc": "^0.27.7"
17
21
  },
18
- "name": "@gcoredev/proxy-wasm-sdk-as",
19
- "description": "Use this SDK to write extensions for the proxy WASM ABI",
20
- "version": "1.0.2",
21
- "main": "assembly/index.ts",
22
22
  "directories": {
23
23
  "doc": "docs"
24
24
  },
@@ -1,66 +0,0 @@
1
- import * as imports from "./imports";
2
-
3
- import { globalArrayBufferReference, LogLevelValues } from "./runtime";
4
-
5
- let logLevel: LogLevelValues = LogLevelValues.info;
6
-
7
- export function setLogLevel(level: LogLevelValues): void {
8
- logLevel = level;
9
- }
10
-
11
- export function log(level: LogLevelValues, logMessage: string): void {
12
- // Temporary fix for proxy_log not being implemented in fastedge:
13
- // relies on @assemblyscript/wasi-shim to print to standard output
14
- if (level >= logLevel) {
15
- process.stdout.write(logMessage + "\n");
16
- }
17
- }
18
-
19
- export function getEnvVar(key: string): string {
20
- const hasKey = process.env.has(key);
21
- if (hasKey) {
22
- return process.env.get(key);
23
- }
24
- return "";
25
- }
26
-
27
- export function getSecretVar(key: string): string {
28
- const buffer = String.UTF8.encode(key);
29
- const status = imports.proxy_get_secret(
30
- changetype<usize>(buffer),
31
- buffer.byteLength,
32
- globalArrayBufferReference.bufferPtr(),
33
- globalArrayBufferReference.sizePtr()
34
- );
35
- if (status != 0) {
36
- // Something went wrong - returns 0 with an empty ArrayBuffer if not found
37
- return "";
38
- }
39
- const arrBuff = globalArrayBufferReference.toArrayBuffer();
40
- if (arrBuff.byteLength == 0) {
41
- return ""; // Not found
42
- }
43
- return String.UTF8.decode(arrBuff);
44
- }
45
-
46
- export function getSecretVarEffectiveAt(key: string, at: u32): string {
47
- const buffer = String.UTF8.encode(key);
48
- const status = imports.proxy_get_effective_at_secret(
49
- changetype<usize>(buffer),
50
- buffer.byteLength,
51
- at,
52
- globalArrayBufferReference.bufferPtr(),
53
- globalArrayBufferReference.sizePtr()
54
- );
55
- if (status != 0) {
56
- // Something went wrong - returns 0 with an empty ArrayBuffer if not found
57
- return "";
58
- }
59
- const arrBuff = globalArrayBufferReference.toArrayBuffer();
60
- if (arrBuff.byteLength == 0) {
61
- return ""; // Not found
62
- }
63
- return String.UTF8.decode(arrBuff);
64
- }
65
-
66
- export { LogLevelValues };