@valon-technologies/gestalt 0.0.1-alpha.1 → 0.0.1-alpha.9

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/src/s3.ts CHANGED
@@ -28,12 +28,18 @@ const ENV_S3_SOCKET = "GESTALT_S3_SOCKET";
28
28
  const WRITE_CHUNK_SIZE = 64 * 1024;
29
29
  const textEncoder = new TextEncoder();
30
30
 
31
+ /**
32
+ * Returns the environment variable name used to discover an S3 socket.
33
+ */
31
34
  export function s3SocketEnv(name?: string): string {
32
35
  const trimmed = name?.trim() ?? "";
33
36
  if (!trimmed) return ENV_S3_SOCKET;
34
37
  return `${ENV_S3_SOCKET}_${trimmed.replace(/[^A-Za-z0-9]/g, "_").toUpperCase()}`;
35
38
  }
36
39
 
40
+ /**
41
+ * Error returned when an object reference does not exist.
42
+ */
37
43
  export class S3NotFoundError extends Error {
38
44
  constructor(message?: string) {
39
45
  super(message ?? "s3: not found");
@@ -41,6 +47,9 @@ export class S3NotFoundError extends Error {
41
47
  }
42
48
  }
43
49
 
50
+ /**
51
+ * Error returned when conditional read or write preconditions fail.
52
+ */
44
53
  export class S3PreconditionFailedError extends Error {
45
54
  constructor(message?: string) {
46
55
  super(message ?? "s3: precondition failed");
@@ -48,6 +57,9 @@ export class S3PreconditionFailedError extends Error {
48
57
  }
49
58
  }
50
59
 
60
+ /**
61
+ * Error returned when the requested byte range is invalid.
62
+ */
51
63
  export class S3InvalidRangeError extends Error {
52
64
  constructor(message?: string) {
53
65
  super(message ?? "s3: invalid range");
@@ -55,12 +67,18 @@ export class S3InvalidRangeError extends Error {
55
67
  }
56
68
  }
57
69
 
70
+ /**
71
+ * Identifies a concrete object or object version.
72
+ */
58
73
  export interface ObjectRef {
59
74
  bucket: string;
60
75
  key: string;
61
76
  versionId?: string;
62
77
  }
63
78
 
79
+ /**
80
+ * Metadata returned for an S3 object.
81
+ */
64
82
  export interface ObjectMeta {
65
83
  ref: ObjectRef;
66
84
  etag: string;
@@ -71,11 +89,17 @@ export interface ObjectMeta {
71
89
  storageClass: string;
72
90
  }
73
91
 
92
+ /**
93
+ * Byte range for partial object reads.
94
+ */
74
95
  export interface ByteRange {
75
96
  start?: number | bigint;
76
97
  end?: number | bigint;
77
98
  }
78
99
 
100
+ /**
101
+ * Conditional and range options for reads.
102
+ */
79
103
  export interface ReadOptions {
80
104
  range?: ByteRange;
81
105
  ifMatch?: string;
@@ -84,6 +108,9 @@ export interface ReadOptions {
84
108
  ifUnmodifiedSince?: Date;
85
109
  }
86
110
 
111
+ /**
112
+ * Optional headers and conditions for writes.
113
+ */
87
114
  export interface WriteOptions {
88
115
  contentType?: string;
89
116
  cacheControl?: string;
@@ -95,6 +122,9 @@ export interface WriteOptions {
95
122
  ifNoneMatch?: string;
96
123
  }
97
124
 
125
+ /**
126
+ * Listing options for object pagination and prefix filtering.
127
+ */
98
128
  export interface ListOptions {
99
129
  bucket: string;
100
130
  prefix?: string;
@@ -104,6 +134,9 @@ export interface ListOptions {
104
134
  maxKeys?: number;
105
135
  }
106
136
 
137
+ /**
138
+ * Single page of results returned by {@link S3.listObjects}.
139
+ */
107
140
  export interface ListPage {
108
141
  objects: ObjectMeta[];
109
142
  commonPrefixes: string[];
@@ -111,11 +144,17 @@ export interface ListPage {
111
144
  hasMore: boolean;
112
145
  }
113
146
 
147
+ /**
148
+ * Conditional options for server-side copy operations.
149
+ */
114
150
  export interface CopyOptions {
115
151
  ifMatch?: string;
116
152
  ifNoneMatch?: string;
117
153
  }
118
154
 
155
+ /**
156
+ * Supported presign methods.
157
+ */
119
158
  export enum PresignMethod {
120
159
  Get = "GET",
121
160
  Put = "PUT",
@@ -123,6 +162,9 @@ export enum PresignMethod {
123
162
  Head = "HEAD",
124
163
  }
125
164
 
165
+ /**
166
+ * Options used when generating a presigned URL.
167
+ */
126
168
  export interface PresignOptions {
127
169
  method?: PresignMethod;
128
170
  expiresSeconds?: number | bigint;
@@ -131,6 +173,9 @@ export interface PresignOptions {
131
173
  headers?: Record<string, string>;
132
174
  }
133
175
 
176
+ /**
177
+ * Result returned by {@link S3.presignObject} or {@link S3Object.presign}.
178
+ */
134
179
  export interface PresignResult {
135
180
  url: string;
136
181
  method: PresignMethod;
@@ -138,6 +183,9 @@ export interface PresignResult {
138
183
  headers: Record<string, string>;
139
184
  }
140
185
 
186
+ /**
187
+ * Accepted write body sources for the S3 client.
188
+ */
141
189
  export type S3BodySource =
142
190
  | string
143
191
  | Uint8Array
@@ -149,16 +197,25 @@ export type S3BodySource =
149
197
  | null
150
198
  | undefined;
151
199
 
200
+ /**
201
+ * Streaming read result returned by the S3 client.
202
+ */
152
203
  export interface ReadResult {
153
204
  meta: ObjectMeta;
154
205
  stream: AsyncIterable<Uint8Array>;
155
206
  }
156
207
 
208
+ /**
209
+ * Result returned by an authored S3 provider implementation.
210
+ */
157
211
  export interface ProviderReadResult {
158
212
  meta: ObjectMeta;
159
213
  body?: S3BodySource;
160
214
  }
161
215
 
216
+ /**
217
+ * Runtime hooks required to implement a Gestalt S3 provider.
218
+ */
162
219
  export interface S3ProviderOptions extends RuntimeProviderOptions {
163
220
  headObject: (ref: ObjectRef) => MaybePromise<ObjectMeta>;
164
221
  readObject: (ref: ObjectRef, options?: ReadOptions) => MaybePromise<ProviderReadResult>;
@@ -180,6 +237,9 @@ export interface S3ProviderOptions extends RuntimeProviderOptions {
180
237
  ) => MaybePromise<PresignResult>;
181
238
  }
182
239
 
240
+ /**
241
+ * S3 provider implementation consumed by the Gestalt runtime.
242
+ */
183
243
  export class S3Provider extends RuntimeProvider {
184
244
  readonly kind = "s3" as const;
185
245
 
@@ -239,10 +299,16 @@ export class S3Provider extends RuntimeProvider {
239
299
  }
240
300
  }
241
301
 
302
+ /**
303
+ * Creates an S3 provider from standard object storage handlers.
304
+ */
242
305
  export function defineS3Provider(options: S3ProviderOptions): S3Provider {
243
306
  return new S3Provider(options);
244
307
  }
245
308
 
309
+ /**
310
+ * Runtime type guard for S3 providers loaded from user modules.
311
+ */
246
312
  export function isS3Provider(value: unknown): value is S3Provider {
247
313
  return (
248
314
  value instanceof S3Provider ||
@@ -260,6 +326,11 @@ export function isS3Provider(value: unknown): value is S3Provider {
260
326
  );
261
327
  }
262
328
 
329
+ /**
330
+ * Adapts an authored S3 provider to the shared protocol service implementation.
331
+ *
332
+ * @internal
333
+ */
263
334
  export function createS3Service(
264
335
  provider: S3Provider,
265
336
  ): Partial<ServiceImpl<typeof S3Service>> {
@@ -411,6 +482,17 @@ export function createS3Service(
411
482
  };
412
483
  }
413
484
 
485
+ /**
486
+ * Client for invoking a host-provided S3 service over the Gestalt transport.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * import { S3 } from "@valon-technologies/gestalt";
491
+ *
492
+ * const s3 = new S3();
493
+ * await s3.object("example-bucket", "hello.json").writeJSON({ ok: true });
494
+ * ```
495
+ */
414
496
  export class S3 {
415
497
  private readonly client: Client<typeof S3Service>;
416
498
 
@@ -467,9 +549,10 @@ export class S3 {
467
549
  body?: S3BodySource,
468
550
  options?: WriteOptions,
469
551
  ): Promise<ObjectMeta> {
470
- const snapshot = snapshotS3Body(body);
552
+ const byteBody = asS3ByteArray(body);
553
+ const preparedBody = byteBody ? cloneBytes(byteBody) : body;
471
554
  const response = await s3Rpc(() =>
472
- this.client.writeObject(writeRequests(ref, snapshot, options)),
555
+ this.client.writeObject(writeRequests(ref, preparedBody, options)),
473
556
  );
474
557
  return fromProtoObjectMeta(response.meta);
475
558
  }
@@ -543,16 +626,25 @@ export class S3 {
543
626
  }
544
627
  }
545
628
 
629
+ /**
630
+ * Convenience wrapper for working with a single S3 object reference.
631
+ */
546
632
  export class S3Object {
547
633
  constructor(
548
634
  private readonly client: S3,
549
635
  readonly ref: ObjectRef,
550
636
  ) {}
551
637
 
638
+ /**
639
+ * Loads object metadata without downloading the object body.
640
+ */
552
641
  async stat(): Promise<ObjectMeta> {
553
642
  return await this.client.headObject(this.ref);
554
643
  }
555
644
 
645
+ /**
646
+ * Returns `true` when the referenced object exists.
647
+ */
556
648
  async exists(): Promise<boolean> {
557
649
  try {
558
650
  await this.stat();
@@ -565,40 +657,67 @@ export class S3Object {
565
657
  }
566
658
  }
567
659
 
660
+ /**
661
+ * Reads an object and returns its metadata and byte stream.
662
+ */
568
663
  async read(options?: ReadOptions): Promise<ReadResult> {
569
664
  return await this.client.readObject(this.ref, options);
570
665
  }
571
666
 
667
+ /**
668
+ * Reads only the object byte stream.
669
+ */
572
670
  async stream(options?: ReadOptions): Promise<AsyncIterable<Uint8Array>> {
573
671
  const result = await this.read(options);
574
672
  return result.stream;
575
673
  }
576
674
 
675
+ /**
676
+ * Reads an object into memory as bytes.
677
+ */
577
678
  async bytes(options?: ReadOptions): Promise<Uint8Array> {
578
679
  const result = await this.read(options);
579
680
  return await collectBytes(result.stream);
580
681
  }
581
682
 
683
+ /**
684
+ * Reads an object into memory as a string.
685
+ */
582
686
  async text(options?: ReadOptions, encoding = "utf-8"): Promise<string> {
583
687
  return new TextDecoder(encoding).decode(await this.bytes(options));
584
688
  }
585
689
 
690
+ /**
691
+ * Reads and parses an object as JSON.
692
+ */
586
693
  async json<T = unknown>(options?: ReadOptions): Promise<T> {
587
694
  return JSON.parse(await this.text(options)) as T;
588
695
  }
589
696
 
697
+ /**
698
+ * Writes an object body to storage.
699
+ */
590
700
  async write(body?: S3BodySource, options?: WriteOptions): Promise<ObjectMeta> {
591
701
  return await this.client.writeObject(this.ref, body, options);
592
702
  }
593
703
 
704
+ /**
705
+ * Writes binary content to storage.
706
+ */
594
707
  async writeBytes(body: Uint8Array | ArrayBuffer | ArrayBufferView): Promise<ObjectMeta> {
595
708
  return await this.write(body);
596
709
  }
597
710
 
711
+ /**
712
+ * Writes string content to storage.
713
+ */
598
714
  async writeString(body: string, options?: WriteOptions): Promise<ObjectMeta> {
599
715
  return await this.write(body, options);
600
716
  }
601
717
 
718
+ /**
719
+ * Writes JSON content using `application/json` by default.
720
+ */
602
721
  async writeJSON(value: unknown, options: WriteOptions = {}): Promise<ObjectMeta> {
603
722
  return await this.write(JSON.stringify(value), {
604
723
  ...options,
@@ -606,10 +725,16 @@ export class S3Object {
606
725
  });
607
726
  }
608
727
 
728
+ /**
729
+ * Deletes the referenced object.
730
+ */
609
731
  async delete(): Promise<void> {
610
732
  await this.client.deleteObject(this.ref);
611
733
  }
612
734
 
735
+ /**
736
+ * Generates a presigned URL for the referenced object.
737
+ */
613
738
  async presign(options?: PresignOptions): Promise<PresignResult> {
614
739
  return await this.client.presignObject(this.ref, options);
615
740
  }
@@ -755,16 +880,9 @@ async function* toAsyncByteStream(body?: S3BodySource): AsyncIterable<Uint8Array
755
880
  yield* chunkBytes(textEncoder.encode(body));
756
881
  return;
757
882
  }
758
- if (body instanceof Uint8Array) {
759
- yield* chunkBytes(body);
760
- return;
761
- }
762
- if (body instanceof ArrayBuffer) {
763
- yield* chunkBytes(new Uint8Array(body));
764
- return;
765
- }
766
- if (ArrayBuffer.isView(body)) {
767
- yield* chunkBytes(new Uint8Array(body.buffer, body.byteOffset, body.byteLength));
883
+ const bytes = asS3ByteArray(body);
884
+ if (bytes) {
885
+ yield* chunkBytes(bytes);
768
886
  return;
769
887
  }
770
888
  if (body instanceof Blob) {
@@ -790,20 +908,17 @@ function* chunkBytes(bytes: Uint8Array): Iterable<Uint8Array> {
790
908
  }
791
909
  }
792
910
 
793
- function snapshotS3Body(body?: S3BodySource): S3BodySource | undefined {
794
- if (body == null || typeof body === "string") {
795
- return body;
796
- }
911
+ function asS3ByteArray(body?: S3BodySource): Uint8Array | undefined {
797
912
  if (body instanceof Uint8Array) {
798
- return cloneBytes(body);
913
+ return body;
799
914
  }
800
915
  if (body instanceof ArrayBuffer) {
801
- return body.slice(0);
916
+ return new Uint8Array(body);
802
917
  }
803
918
  if (ArrayBuffer.isView(body)) {
804
- return new Uint8Array(body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength));
919
+ return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
805
920
  }
806
- return body;
921
+ return undefined;
807
922
  }
808
923
 
809
924
  async function* readableStreamToAsyncIterable(
package/src/schema.ts CHANGED
@@ -1,5 +1,11 @@
1
+ /**
2
+ * Catalog schema kinds supported by the TypeScript SDK.
3
+ */
1
4
  export type CatalogType = "string" | "integer" | "number" | "boolean" | "object" | "array";
2
5
 
6
+ /**
7
+ * Runtime schema that validates operation input and output values.
8
+ */
3
9
  export interface Schema<T> {
4
10
  readonly catalogType: CatalogType;
5
11
  readonly description: string;
@@ -10,10 +16,16 @@ export interface Schema<T> {
10
16
  parse(value: unknown, path?: string): T;
11
17
  }
12
18
 
19
+ /**
20
+ * Extracts the TypeScript type represented by a {@link Schema}.
21
+ */
13
22
  export type InferSchema<TSchema extends Schema<unknown>> = TSchema extends Schema<infer T>
14
23
  ? T
15
24
  : never;
16
25
 
26
+ /**
27
+ * Shared options supported by all schema builders.
28
+ */
17
29
  export interface SchemaOptions<T> {
18
30
  description?: string;
19
31
  required?: boolean;
@@ -53,6 +65,9 @@ function withMeta<T>(
53
65
  };
54
66
  }
55
67
 
68
+ /**
69
+ * Creates a string schema.
70
+ */
56
71
  export function string(options?: SchemaOptions<string>): Schema<string> {
57
72
  return withMeta(
58
73
  "string",
@@ -66,6 +81,9 @@ export function string(options?: SchemaOptions<string>): Schema<string> {
66
81
  );
67
82
  }
68
83
 
84
+ /**
85
+ * Creates an integer schema.
86
+ */
69
87
  export function integer(options?: SchemaOptions<number>): Schema<number> {
70
88
  return withMeta(
71
89
  "integer",
@@ -85,6 +103,9 @@ export function integer(options?: SchemaOptions<number>): Schema<number> {
85
103
  );
86
104
  }
87
105
 
106
+ /**
107
+ * Creates a floating-point or integer number schema.
108
+ */
88
109
  export function number(options?: SchemaOptions<number>): Schema<number> {
89
110
  return withMeta(
90
111
  "number",
@@ -107,6 +128,9 @@ export function number(options?: SchemaOptions<number>): Schema<number> {
107
128
  );
108
129
  }
109
130
 
131
+ /**
132
+ * Creates a boolean schema that accepts `true` / `false` and `1` / `0`.
133
+ */
110
134
  export function boolean(options?: SchemaOptions<boolean>): Schema<boolean> {
111
135
  return withMeta(
112
136
  "boolean",
@@ -129,6 +153,9 @@ export function boolean(options?: SchemaOptions<boolean>): Schema<boolean> {
129
153
  );
130
154
  }
131
155
 
156
+ /**
157
+ * Creates an array schema for repeated values.
158
+ */
132
159
  export function array<T>(item: Schema<T>, options?: SchemaOptions<T[]>): Schema<T[]> {
133
160
  const base = withMeta<T[]>(
134
161
  "array",
@@ -147,6 +174,19 @@ export function array<T>(item: Schema<T>, options?: SchemaOptions<T[]>): Schema<
147
174
  };
148
175
  }
149
176
 
177
+ /**
178
+ * Creates an object schema from a field map.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * import { s } from "@valon-technologies/gestalt";
183
+ *
184
+ * const input = s.object({
185
+ * name: s.string(),
186
+ * excited: s.optional(s.boolean()),
187
+ * });
188
+ * ```
189
+ */
150
190
  export function object<T extends Record<string, unknown>>(
151
191
  fields: { [K in keyof T]: Schema<T[K]> },
152
192
  options?: SchemaOptions<T>,
@@ -176,6 +216,9 @@ export function object<T extends Record<string, unknown>>(
176
216
  };
177
217
  }
178
218
 
219
+ /**
220
+ * Marks another schema as optional while preserving its metadata.
221
+ */
179
222
  export function optional<T>(schema: Schema<T>): Schema<T | undefined> {
180
223
  const wrapped: Schema<T | undefined> = {
181
224
  catalogType: schema.catalogType,
@@ -201,6 +244,9 @@ export function optional<T>(schema: Schema<T>): Schema<T | undefined> {
201
244
  };
202
245
  }
203
246
 
247
+ /**
248
+ * Namespace-style schema builder helpers.
249
+ */
204
250
  export const s = {
205
251
  string,
206
252
  integer,
package/src/secrets.ts CHANGED
@@ -1,10 +1,16 @@
1
1
  import { RuntimeProvider, type RuntimeProviderOptions } from "./provider.ts";
2
2
  import type { MaybePromise } from "./api.ts";
3
3
 
4
+ /**
5
+ * Runtime hooks required to implement a Gestalt secrets provider.
6
+ */
4
7
  export interface SecretsProviderOptions extends RuntimeProviderOptions {
5
8
  getSecret: (name: string) => MaybePromise<string>;
6
9
  }
7
10
 
11
+ /**
12
+ * Secrets provider implementation consumed by the Gestalt runtime.
13
+ */
8
14
  export class SecretsProvider extends RuntimeProvider {
9
15
  readonly kind = "secrets" as const;
10
16
 
@@ -20,10 +26,16 @@ export class SecretsProvider extends RuntimeProvider {
20
26
  }
21
27
  }
22
28
 
29
+ /**
30
+ * Creates a secrets provider from a simple `getSecret` implementation.
31
+ */
23
32
  export function defineSecretsProvider(options: SecretsProviderOptions): SecretsProvider {
24
33
  return new SecretsProvider(options);
25
34
  }
26
35
 
36
+ /**
37
+ * Runtime type guard for secrets providers loaded from user modules.
38
+ */
27
39
  export function isSecretsProvider(value: unknown): value is SecretsProvider {
28
40
  return (
29
41
  value instanceof SecretsProvider ||