@decaf-ts/integrations 0.9.0 → 0.10.2

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 (109) hide show
  1. package/dist/integrations.cjs +1 -1
  2. package/docker/minio-compose.yml +1 -1
  3. package/lib/cjs/blob/azure/AzureBlobStoreService.cjs +33 -0
  4. package/lib/cjs/blob/azure/AzureBlobStoreService.cjs.map +1 -1
  5. package/lib/cjs/blob/core/BlobStoreService.cjs +176 -1
  6. package/lib/cjs/blob/core/BlobStoreService.cjs.map +1 -1
  7. package/lib/cjs/blob/gcp/GcsBlobStoreService.cjs +32 -0
  8. package/lib/cjs/blob/gcp/GcsBlobStoreService.cjs.map +1 -1
  9. package/lib/cjs/blob/ipfs/IpfsBlobStoreService.cjs +31 -0
  10. package/lib/cjs/blob/ipfs/IpfsBlobStoreService.cjs.map +1 -1
  11. package/lib/cjs/blob/local/LocalBlobStoreService.cjs +30 -0
  12. package/lib/cjs/blob/local/LocalBlobStoreService.cjs.map +1 -1
  13. package/lib/cjs/blob/memory/MemoryBlobStoreService.cjs +30 -0
  14. package/lib/cjs/blob/memory/MemoryBlobStoreService.cjs.map +1 -1
  15. package/lib/cjs/blob/s3/MinioBlobStoreService.cjs +2 -16
  16. package/lib/cjs/blob/s3/MinioBlobStoreService.cjs.map +1 -1
  17. package/lib/cjs/blob/s3/R2BlobStoreService.cjs +2 -16
  18. package/lib/cjs/blob/s3/R2BlobStoreService.cjs.map +1 -1
  19. package/lib/cjs/blob/s3/S3CompatibleBlobStoreService.cjs +31 -0
  20. package/lib/cjs/blob/s3/S3CompatibleBlobStoreService.cjs.map +1 -1
  21. package/lib/cjs/calendar/CalendarService.cjs +393 -0
  22. package/lib/cjs/calendar/CalendarService.cjs.map +1 -0
  23. package/lib/cjs/calendar/index.cjs +21 -0
  24. package/lib/cjs/calendar/index.cjs.map +1 -0
  25. package/lib/cjs/calendar/types.cjs +4 -0
  26. package/lib/cjs/calendar/types.cjs.map +1 -0
  27. package/lib/cjs/calendar/validators/cron.cjs +58 -0
  28. package/lib/cjs/calendar/validators/cron.cjs.map +1 -0
  29. package/lib/cjs/calendar/writers/CalendarFsWriter.cjs +39 -0
  30. package/lib/cjs/calendar/writers/CalendarFsWriter.cjs.map +1 -0
  31. package/lib/cjs/calendar/writers/CalenderDownloader.cjs +47 -0
  32. package/lib/cjs/calendar/writers/CalenderDownloader.cjs.map +1 -0
  33. package/lib/cjs/calendar/writers/ICalendarWriter.cjs +4 -0
  34. package/lib/cjs/calendar/writers/ICalendarWriter.cjs.map +1 -0
  35. package/lib/cjs/graph/log/GraphRunLogger.cjs +2 -3
  36. package/lib/cjs/graph/log/GraphRunLogger.cjs.map +1 -1
  37. package/lib/cjs/index.cjs +3 -3
  38. package/lib/esm/blob/azure/AzureBlobStoreService.js +33 -0
  39. package/lib/esm/blob/azure/AzureBlobStoreService.js.map +1 -1
  40. package/lib/esm/blob/core/BlobStoreService.js +176 -1
  41. package/lib/esm/blob/core/BlobStoreService.js.map +1 -1
  42. package/lib/esm/blob/gcp/GcsBlobStoreService.js +32 -0
  43. package/lib/esm/blob/gcp/GcsBlobStoreService.js.map +1 -1
  44. package/lib/esm/blob/ipfs/IpfsBlobStoreService.js +31 -0
  45. package/lib/esm/blob/ipfs/IpfsBlobStoreService.js.map +1 -1
  46. package/lib/esm/blob/local/LocalBlobStoreService.js +30 -0
  47. package/lib/esm/blob/local/LocalBlobStoreService.js.map +1 -1
  48. package/lib/esm/blob/memory/MemoryBlobStoreService.js +30 -0
  49. package/lib/esm/blob/memory/MemoryBlobStoreService.js.map +1 -1
  50. package/lib/esm/blob/s3/MinioBlobStoreService.js +3 -14
  51. package/lib/esm/blob/s3/MinioBlobStoreService.js.map +1 -1
  52. package/lib/esm/blob/s3/R2BlobStoreService.js +3 -14
  53. package/lib/esm/blob/s3/R2BlobStoreService.js.map +1 -1
  54. package/lib/esm/blob/s3/S3CompatibleBlobStoreService.js +31 -0
  55. package/lib/esm/blob/s3/S3CompatibleBlobStoreService.js.map +1 -1
  56. package/lib/esm/calendar/CalendarService.js +384 -0
  57. package/lib/esm/calendar/CalendarService.js.map +1 -0
  58. package/lib/esm/calendar/index.js +4 -0
  59. package/lib/esm/calendar/index.js.map +1 -0
  60. package/lib/esm/calendar/types.js +2 -0
  61. package/lib/esm/calendar/types.js.map +1 -0
  62. package/lib/esm/calendar/validators/cron.js +48 -0
  63. package/lib/esm/calendar/validators/cron.js.map +1 -0
  64. package/lib/esm/calendar/writers/CalendarFsWriter.js +31 -0
  65. package/lib/esm/calendar/writers/CalendarFsWriter.js.map +1 -0
  66. package/lib/esm/calendar/writers/CalenderDownloader.js +39 -0
  67. package/lib/esm/calendar/writers/CalenderDownloader.js.map +1 -0
  68. package/lib/esm/calendar/writers/ICalendarWriter.js +2 -0
  69. package/lib/esm/calendar/writers/ICalendarWriter.js.map +1 -0
  70. package/lib/esm/graph/log/GraphRunLogger.js +2 -3
  71. package/lib/esm/graph/log/GraphRunLogger.js.map +1 -1
  72. package/lib/esm/index.js +3 -3
  73. package/lib/types/blob/azure/AzureBlobStoreService.d.cts +37 -0
  74. package/lib/types/blob/azure/AzureBlobStoreService.d.mts +37 -0
  75. package/lib/types/blob/core/BlobStoreService.d.cts +232 -1
  76. package/lib/types/blob/core/BlobStoreService.d.mts +232 -1
  77. package/lib/types/blob/gcp/GcsBlobStoreService.d.cts +35 -0
  78. package/lib/types/blob/gcp/GcsBlobStoreService.d.mts +35 -0
  79. package/lib/types/blob/ipfs/IpfsBlobStoreService.d.cts +34 -0
  80. package/lib/types/blob/ipfs/IpfsBlobStoreService.d.mts +34 -0
  81. package/lib/types/blob/local/LocalBlobStoreService.d.cts +27 -0
  82. package/lib/types/blob/local/LocalBlobStoreService.d.mts +27 -0
  83. package/lib/types/blob/memory/MemoryBlobStoreService.d.cts +27 -0
  84. package/lib/types/blob/memory/MemoryBlobStoreService.d.mts +27 -0
  85. package/lib/types/blob/s3/MinioBlobStoreService.d.cts +5 -0
  86. package/lib/types/blob/s3/MinioBlobStoreService.d.mts +5 -0
  87. package/lib/types/blob/s3/R2BlobStoreService.d.cts +5 -0
  88. package/lib/types/blob/s3/R2BlobStoreService.d.mts +5 -0
  89. package/lib/types/blob/s3/S3CompatibleBlobStoreService.d.cts +28 -0
  90. package/lib/types/blob/s3/S3CompatibleBlobStoreService.d.mts +28 -0
  91. package/lib/types/calendar/CalendarService.d.cts +40 -0
  92. package/lib/types/calendar/CalendarService.d.mts +40 -0
  93. package/lib/types/calendar/index.d.cts +3 -0
  94. package/lib/types/calendar/index.d.mts +3 -0
  95. package/lib/types/calendar/types.d.cts +64 -0
  96. package/lib/types/calendar/types.d.mts +64 -0
  97. package/lib/types/calendar/validators/cron.d.cts +7 -0
  98. package/lib/types/calendar/validators/cron.d.mts +7 -0
  99. package/lib/types/calendar/writers/CalendarFsWriter.d.cts +6 -0
  100. package/lib/types/calendar/writers/CalendarFsWriter.d.mts +6 -0
  101. package/lib/types/calendar/writers/CalenderDownloader.d.cts +6 -0
  102. package/lib/types/calendar/writers/CalenderDownloader.d.mts +6 -0
  103. package/lib/types/calendar/writers/ICalendarWriter.d.cts +5 -0
  104. package/lib/types/calendar/writers/ICalendarWriter.d.mts +5 -0
  105. package/lib/types/graph/log/GraphRunLogger.d.cts +3 -1
  106. package/lib/types/graph/log/GraphRunLogger.d.mts +3 -1
  107. package/lib/types/index.d.cts +2 -2
  108. package/lib/types/index.d.mts +2 -2
  109. package/package.json +14 -2
@@ -7,9 +7,46 @@ import { type ContainerClient } from "@azure/storage-blob";
7
7
  import { type ContextualArgs, type MaybeContextualArg } from "@decaf-ts/core";
8
8
  import { BlobStoreService } from "../core/BlobStoreService.d.cts";
9
9
  import type { AzureBlobStoreServiceConfig, BlobGetOptions, BlobGetResult, BlobKey, BlobListOptions, BlobListResult, BlobMetadata, BlobPutOptions, BlobPutResult, BlobUrlOptions, BlobUrlResult, BlobValue } from "../core/BlobTypes.d.cts";
10
+ /**
11
+ * @description Azure Blob storage service registered as `blob-azure`
12
+ * @summary Stores blobs in an Azure Blob Storage container via
13
+ * `@azure/storage-blob`, authenticating either with a connection string or
14
+ * `DefaultAzureCredential`, and signing URLs with SAS tokens.
15
+ * @class AzureBlobStoreService
16
+ * @memberOf module:integrations/blob/azure/service
17
+ */
10
18
  export declare class AzureBlobStoreService extends BlobStoreService<ContainerClient, AzureBlobStoreServiceConfig> {
19
+ /**
20
+ * @description The `BlobServiceClient` used to resolve the container client
21
+ * @summary Built in `initialize()` from the connection string or endpoint;
22
+ * kept separately so `url()` can inspect the credential for SAS signing.
23
+ * @type {BlobServiceClient}
24
+ * @private
25
+ */
11
26
  private blobServiceClient;
27
+ /**
28
+ * @description Builds the Azure blob config from the environment
29
+ * @summary Reads the `blobs.azure` environment slice; considered
30
+ * unconfigured when no `container` is set, which keeps the auto-boot
31
+ * fallback (see `getConfigFromArgs`) resolving to `undefined`.
32
+ * @return {AzureBlobStoreServiceConfig | undefined} The environment-derived config, or `undefined` when unconfigured
33
+ */
12
34
  protected configFromEnvironment(): AzureBlobStoreServiceConfig | undefined;
35
+ /**
36
+ * @description Initializes the Azure Blob container client
37
+ * @summary Resolves the config via `getConfigFromArgs` (explicit config,
38
+ * then the `blobs.azure` environment slice). When no config resolves during
39
+ * a context-bearing auto-boot of an unconfigured provider, returns
40
+ * `skipInitialization()` so `Service.boot` does not throw; an explicit
41
+ * `initialize()` with no resolvable config still throws a
42
+ * `ValidationError`. Requires a `container`, builds the
43
+ * `BlobServiceClient` from the connection string when present or from the
44
+ * endpoint (defaulting to `https://<accountName>.blob.core.windows.net`)
45
+ * with `DefaultAzureCredential` otherwise, and stores the container client.
46
+ * @param {...ContextualArgs<any>} args - An optional `AzureBlobStoreServiceConfig`, optionally followed by a decaf `Context`
47
+ * @return {Promise<{config: AzureBlobStoreServiceConfig, client: ContainerClient}>} The resolved config and the container client
48
+ * @throws {InternalError} When a resolved config has no `container`
49
+ */
13
50
  initialize(...args: ContextualArgs<any>): Promise<{
14
51
  config: AzureBlobStoreServiceConfig;
15
52
  client: ContainerClient;
@@ -7,9 +7,46 @@ import { type ContainerClient } from "@azure/storage-blob";
7
7
  import { type ContextualArgs, type MaybeContextualArg } from "@decaf-ts/core";
8
8
  import { BlobStoreService } from "../core/BlobStoreService.d.mts";
9
9
  import type { AzureBlobStoreServiceConfig, BlobGetOptions, BlobGetResult, BlobKey, BlobListOptions, BlobListResult, BlobMetadata, BlobPutOptions, BlobPutResult, BlobUrlOptions, BlobUrlResult, BlobValue } from "../core/BlobTypes.d.mts";
10
+ /**
11
+ * @description Azure Blob storage service registered as `blob-azure`
12
+ * @summary Stores blobs in an Azure Blob Storage container via
13
+ * `@azure/storage-blob`, authenticating either with a connection string or
14
+ * `DefaultAzureCredential`, and signing URLs with SAS tokens.
15
+ * @class AzureBlobStoreService
16
+ * @memberOf module:integrations/blob/azure/service
17
+ */
10
18
  export declare class AzureBlobStoreService extends BlobStoreService<ContainerClient, AzureBlobStoreServiceConfig> {
19
+ /**
20
+ * @description The `BlobServiceClient` used to resolve the container client
21
+ * @summary Built in `initialize()` from the connection string or endpoint;
22
+ * kept separately so `url()` can inspect the credential for SAS signing.
23
+ * @type {BlobServiceClient}
24
+ * @private
25
+ */
11
26
  private blobServiceClient;
27
+ /**
28
+ * @description Builds the Azure blob config from the environment
29
+ * @summary Reads the `blobs.azure` environment slice; considered
30
+ * unconfigured when no `container` is set, which keeps the auto-boot
31
+ * fallback (see `getConfigFromArgs`) resolving to `undefined`.
32
+ * @return {AzureBlobStoreServiceConfig | undefined} The environment-derived config, or `undefined` when unconfigured
33
+ */
12
34
  protected configFromEnvironment(): AzureBlobStoreServiceConfig | undefined;
35
+ /**
36
+ * @description Initializes the Azure Blob container client
37
+ * @summary Resolves the config via `getConfigFromArgs` (explicit config,
38
+ * then the `blobs.azure` environment slice). When no config resolves during
39
+ * a context-bearing auto-boot of an unconfigured provider, returns
40
+ * `skipInitialization()` so `Service.boot` does not throw; an explicit
41
+ * `initialize()` with no resolvable config still throws a
42
+ * `ValidationError`. Requires a `container`, builds the
43
+ * `BlobServiceClient` from the connection string when present or from the
44
+ * endpoint (defaulting to `https://<accountName>.blob.core.windows.net`)
45
+ * with `DefaultAzureCredential` otherwise, and stores the container client.
46
+ * @param {...ContextualArgs<any>} args - An optional `AzureBlobStoreServiceConfig`, optionally followed by a decaf `Context`
47
+ * @return {Promise<{config: AzureBlobStoreServiceConfig, client: ContainerClient}>} The resolved config and the container client
48
+ * @throws {InternalError} When a resolved config has no `container`
49
+ */
13
50
  initialize(...args: ContextualArgs<any>): Promise<{
14
51
  config: AzureBlobStoreServiceConfig;
15
52
  client: ContainerClient;
@@ -5,30 +5,261 @@
5
5
  */
6
6
  import { ClientBasedService, type ContextualArgs, type MaybeContextualArg } from "@decaf-ts/core";
7
7
  import type { BlobGetOptions, BlobGetResult, BlobKey, BlobListOptions, BlobListResult, BlobMetadata, BlobProvider, BlobPutOptions, BlobPutResult, BlobStoreServiceConfig, BlobUrlOptions, BlobUrlResult, BlobValue } from "./BlobTypes.d.cts";
8
+ /**
9
+ * @description Base abstraction for blob store client services
10
+ * @summary Provides the shared key handling, URI composition, and
11
+ * auto-boot-aware config resolution used by every provider backend
12
+ * (memory, local filesystem, S3-compatible, Azure Blob, GCS, IPFS).
13
+ *
14
+ * Extends {@link ClientBasedService} so concrete providers are registered as
15
+ * injectable services and auto-booted by `Service.boot`. During auto-boot the
16
+ * framework passes a decaf `Context` instead of a blob config, so
17
+ * {@link BlobStoreService.getConfigFromArgs} must distinguish the two: a real
18
+ * config always carries a non-empty string `provider`, while a `Context` does
19
+ * not. When neither an explicit config nor the provider's environment slice
20
+ * resolves a config, a context-bearing auto-boot is skipped via
21
+ * {@link BlobStoreService.skipInitialization} instead of throwing, so optional
22
+ * providers (e.g. `blob-minio`/`blob-r2` registered alongside `blob-s3`) do not
23
+ * block the rest of the service graph. An explicit `initialize()` call with no
24
+ * resolvable config still fails loudly with a {@link ValidationError}.
25
+ *
26
+ * @template TClient - The provider-specific client type created by `initialize`
27
+ * @template TConfig - The provider-specific config extending {@link BlobStoreServiceConfig}
28
+ * @class BlobStoreService
29
+ * @memberOf module:integrations/blob/core/service
30
+ */
8
31
  export declare abstract class BlobStoreService<TClient = unknown, TConfig extends BlobStoreServiceConfig = BlobStoreServiceConfig> extends ClientBasedService<TClient, TConfig> {
32
+ /**
33
+ * @description Initializes the base service
34
+ * @summary Delegates to {@link ClientBasedService}; config and client are
35
+ * only set by a successful `initialize()` (or skipped on auto-boot).
36
+ */
9
37
  constructor();
38
+ /**
39
+ * @description The blob provider identifier for this service
40
+ * @summary Reads `provider` from the resolved config.
41
+ * @return {BlobProvider} The provider identifier (e.g. `"s3"`, `"memory"`)
42
+ * @throws {InternalError} If accessed before a successful `initialize()`
43
+ */
10
44
  get provider(): BlobProvider;
45
+ /**
46
+ * @description The logical source identifier for this service
47
+ * @summary Reads `sourceId` from the resolved config; used to compose blob URIs.
48
+ * @return {string} The configured source identifier
49
+ * @throws {InternalError} If accessed before a successful `initialize()`
50
+ */
11
51
  get sourceId(): string;
52
+ /**
53
+ * @description Initializes the provider client from config or environment
54
+ * @summary Concrete providers resolve their config via
55
+ * {@link BlobStoreService.getConfigFromArgs}, build the provider client, and
56
+ * store both on `_config`/`_client`. When no config can be resolved during a
57
+ * context-bearing auto-boot, providers return
58
+ * {@link BlobStoreService.skipInitialization} instead of throwing.
59
+ * @param {...ContextualArg<any>} args - An optional blob config, optionally followed by a decaf `Context` (auto-boot always appends a `Context`)
60
+ * @return {Promise<{config: TConfig, client: TClient}>} The resolved config and constructed client
61
+ */
12
62
  abstract initialize(...args: ContextualArgs<any>): Promise<{
13
63
  config: TConfig;
14
64
  client: TClient;
15
65
  }>;
66
+ /**
67
+ * @description Writes a blob under the given key
68
+ * @summary Serializes the value, optionally enforces `ifNotExists` and
69
+ * `expectedSha256`, and stores it with the provider backend.
70
+ * @param {BlobKey} key - Logical blob key (prefixed by the configured `prefix`)
71
+ * @param {BlobValue} value - Payload as `Uint8Array`, `Buffer`, or async stream
72
+ * @param {BlobPutOptions} [options] - Put options (conditional write, content type, metadata, checksum)
73
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
74
+ * @return {Promise<BlobPutResult>} The stored blob descriptor (key, URI, metadata)
75
+ */
16
76
  abstract put(key: BlobKey, value: BlobValue, options?: BlobPutOptions, ...args: MaybeContextualArg<any>): Promise<BlobPutResult>;
77
+ /**
78
+ * @description Reads a blob by key
79
+ * @summary Returns the payload as an async iterable along with its metadata.
80
+ * @param {BlobKey} key - Logical blob key
81
+ * @param {BlobGetOptions} [options] - Get options (byte range, version)
82
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
83
+ * @return {Promise<BlobGetResult>} The blob payload stream and metadata
84
+ * @throws {NotFoundError} If the blob does not exist
85
+ */
17
86
  abstract get(key: BlobKey, options?: BlobGetOptions, ...args: MaybeContextualArg<any>): Promise<BlobGetResult>;
87
+ /**
88
+ * @description Checks whether a blob exists
89
+ * @summary Must not throw when the blob is missing; resolves `false` instead.
90
+ * @param {BlobKey} key - Logical blob key
91
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
92
+ * @return {Promise<boolean>} `true` when the blob exists, `false` otherwise
93
+ */
18
94
  abstract has(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<boolean>;
95
+ /**
96
+ * @description Reads blob metadata without downloading the payload
97
+ * @summary Returns content type, length, etag, and provider-specific metadata.
98
+ * @param {BlobKey} key - Logical blob key
99
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
100
+ * @return {Promise<BlobMetadata>} The blob metadata
101
+ * @throws {NotFoundError} If the blob does not exist
102
+ */
19
103
  abstract stat(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<BlobMetadata>;
104
+ /**
105
+ * @description Deletes a blob
106
+ * @summary Should be idempotent; deleting a missing blob must not throw.
107
+ * @param {BlobKey} key - Logical blob key
108
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
109
+ * @return {Promise<void>} Resolves when the blob is deleted
110
+ */
20
111
  abstract delete(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<void>;
112
+ /**
113
+ * @description Copies a blob to another key
114
+ * @summary Performs a server-side copy where the backend supports it.
115
+ * @param {BlobKey} fromKey - Source logical blob key
116
+ * @param {BlobKey} toKey - Target logical blob key
117
+ * @param {BlobPutOptions} [options] - Copy options (content type, metadata)
118
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
119
+ * @return {Promise<BlobPutResult>} The copied blob descriptor
120
+ * @throws {NotFoundError} If the source blob does not exist
121
+ */
21
122
  abstract copy(fromKey: BlobKey, toKey: BlobKey, options?: BlobPutOptions, ...args: MaybeContextualArg<any>): Promise<BlobPutResult>;
123
+ /**
124
+ * @description Lists blobs under an optional prefix
125
+ * @summary Returns a page of keys with metadata and an optional pagination cursor.
126
+ * @param {BlobListOptions} [options] - List options (prefix, limit, cursor)
127
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
128
+ * @return {Promise<BlobListResult>} The page of items and the next cursor, if any
129
+ */
22
130
  abstract list(options?: BlobListOptions, ...args: MaybeContextualArg<any>): Promise<BlobListResult>;
131
+ /**
132
+ * @description Builds a URL for direct access to a blob
133
+ * @summary Backends with signed-URL support return a presigned URL; others
134
+ * return the canonical provider URI.
135
+ * @param {BlobKey} key - Logical blob key
136
+ * @param {BlobUrlOptions} [options] - URL options (operation, expiry, content type)
137
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
138
+ * @return {Promise<BlobUrlResult>} The URL, HTTP method, and expiry
139
+ */
23
140
  abstract url(key: BlobKey, options?: BlobUrlOptions, ...args: MaybeContextualArg<any>): Promise<BlobUrlResult>;
141
+ /**
142
+ * @description Composes the physical storage key for a logical blob key
143
+ * @summary Prepends the configured `prefix` to the given key.
144
+ * @param {BlobKey} key - Logical blob key
145
+ * @return {string} The physical key including the configured prefix
146
+ */
24
147
  protected physicalKey(key: BlobKey): string;
148
+ /**
149
+ * @description Normalizes a logical blob key
150
+ * @summary Delegates to `cleanKey` (trims slashes and collapses separators).
151
+ * @param {BlobKey} key - Logical blob key
152
+ * @return {string} The normalized key
153
+ */
25
154
  protected cleanKey(key: BlobKey): string;
26
- protected getConfigFromArgs<TExpected extends TConfig>(...args: MaybeContextualArg<any>): TExpected;
155
+ /**
156
+ * @description Resolves the provider config from the initialize arguments or the provider's environment slice
157
+ * @summary `Service.boot` passes a decaf `Context` (not a blob config) to
158
+ * every registered `ClientBasedService`, so an object-shaped argument must
159
+ * not blindly be treated as config. A real blob config always carries a
160
+ * non-empty string `provider` (see {@link BlobStoreService.isConfigLike}).
161
+ * Resolution order: explicit config argument, then the provider's
162
+ * `configFromEnvironment()` slice. When neither resolves, `undefined` is
163
+ * returned only for a context-bearing auto-boot so an unconfigured provider
164
+ * (e.g. an optional `blob-minio`/`blob-r2` registered alongside `blob-s3`)
165
+ * does not block the whole service graph from booting; an explicit
166
+ * `initialize()` with neither config nor environment still throws.
167
+ *
168
+ * @template TExpected - The provider-specific config type expected by the caller
169
+ * @param {...MaybeContextualArg<any>} args - Initialize arguments: an optional blob config and/or a decaf `Context`
170
+ * @return {TExpected | undefined} The resolved config, or `undefined` when a context-bearing auto-boot has no resolvable config
171
+ * @throws {ValidationError} When called explicitly (no `Context` in `args`) and no config argument or environment config is available
172
+ *
173
+ * @mermaid
174
+ * sequenceDiagram
175
+ * participant Caller as Service.boot / initialize()
176
+ * participant Service as BlobStoreService
177
+ * participant Env as configFromEnvironment()
178
+ *
179
+ * Caller->>Service: ...args (config?, Context?)
180
+ * activate Service
181
+ * Service->>Service: isConfigLike(args[0])
182
+ * alt config-like argument present
183
+ * Service-->>Caller: config
184
+ * else no config-like argument
185
+ * Service->>Env: read provider env slice
186
+ * activate Env
187
+ * Env-->>Service: env config | undefined
188
+ * deactivate Env
189
+ * alt env config resolved
190
+ * Service-->>Caller: config
191
+ * else no env config
192
+ * Service->>Service: hasContext(args)
193
+ * alt Context present (auto-boot)
194
+ * Service-->>Caller: undefined (skip initialization)
195
+ * else explicit initialize()
196
+ * Service--xCaller: ValidationError
197
+ * end
198
+ * end
199
+ * end
200
+ * deactivate Service
201
+ */
202
+ protected getConfigFromArgs<TExpected extends TConfig>(...args: MaybeContextualArg<any>): TExpected | undefined;
203
+ /**
204
+ * @description Determines whether a value is a blob config object
205
+ * @summary True only for an object that carries a non-empty string
206
+ * `provider` and is not a decaf `Context`. Avoids mistaking a contextual
207
+ * argument (e.g. the `Context` that `Service.boot` passes) for the config.
208
+ * @param {unknown} value - The value to test
209
+ * @return {boolean} `true` when the value is config-like
210
+ */
211
+ protected isConfigLike(value: unknown): boolean;
212
+ /**
213
+ * @description Detects a decaf `Context` by duck typing
214
+ * @summary True for an object exposing the `Context` method surface (`get`,
215
+ * `accumulate`, `override`, `toOverrides` as functions). Deliberately avoids
216
+ * `instanceof` so linked builds of `@decaf-ts/core` with a duplicate
217
+ * `Context` constructor still resolve.
218
+ * @param {unknown} value - The value to test
219
+ * @return {boolean} `true` when the value duck-types as a decaf `Context`
220
+ */
221
+ protected isContextLike(value: unknown): boolean;
222
+ /**
223
+ * @description Determines whether the initialize arguments carry a decaf `Context`
224
+ * @summary A `Context` in the arguments marks a background auto-boot of an
225
+ * unconfigured provider (resolved to a skip by
226
+ * {@link BlobStoreService.getConfigFromArgs}), as opposed to an explicit
227
+ * `initialize()` that must fail loudly when no config is available.
228
+ * @param {MaybeContextualArg<any>[]} args - The initialize arguments to inspect
229
+ * @return {boolean} `true` when any argument duck-types as a decaf `Context`
230
+ */
231
+ protected hasContext(args: MaybeContextualArg<any>): boolean;
232
+ /**
233
+ * @description Skips initialization for an unconfigured provider during auto-boot
234
+ * @summary Clears `_config`/`_client` and returns a `{ config, client }`
235
+ * result compatible with the `ClientBasedService.initialize` signature so
236
+ * `Service.boot` completes without throwing. Leaves the service
237
+ * deterministic: the `config`/`client` getters still throw if accessed until
238
+ * the service is explicitly initialized with a resolvable config, and the
239
+ * rest of the service graph is not blocked by this provider's absence.
240
+ * Concrete providers invoke this from `initialize()` when
241
+ * `getConfigFromArgs` resolves no config and cast the result to the
242
+ * declared return type.
243
+ * @return {{config: undefined, client: undefined}} An unset config/client pair for the auto-boot path
244
+ */
245
+ protected skipInitialization(): {
246
+ config: undefined;
247
+ client: undefined;
248
+ };
27
249
  /**
28
250
  * @description Builds a config from the provider's environment as a fallback.
29
251
  * @summary Providers that support environment-sourced configuration should
30
252
  * override this to read from their own `blobs.<provider>` environment slice.
31
253
  */
32
254
  protected abstract configFromEnvironment(): TConfig | undefined;
255
+ /**
256
+ * @description Composes the provider URI for a blob
257
+ * @summary Builds `<scheme>://<sourceId>/<physicalKey>` and appends the
258
+ * extra query fragment (e.g. a version identifier) when provided.
259
+ * @param {BlobKey} key - Logical blob key
260
+ * @param {string} scheme - Provider URI scheme (e.g. `"s3"`, `"memory"`, `"file"`)
261
+ * @param {string} [extra] - Optional query string appended after `?`
262
+ * @return {string} The composed blob URI
263
+ */
33
264
  protected uri(key: BlobKey, scheme: string, extra?: string): string;
34
265
  }
@@ -5,30 +5,261 @@
5
5
  */
6
6
  import { ClientBasedService, type ContextualArgs, type MaybeContextualArg } from "@decaf-ts/core";
7
7
  import type { BlobGetOptions, BlobGetResult, BlobKey, BlobListOptions, BlobListResult, BlobMetadata, BlobProvider, BlobPutOptions, BlobPutResult, BlobStoreServiceConfig, BlobUrlOptions, BlobUrlResult, BlobValue } from "./BlobTypes.d.mts";
8
+ /**
9
+ * @description Base abstraction for blob store client services
10
+ * @summary Provides the shared key handling, URI composition, and
11
+ * auto-boot-aware config resolution used by every provider backend
12
+ * (memory, local filesystem, S3-compatible, Azure Blob, GCS, IPFS).
13
+ *
14
+ * Extends {@link ClientBasedService} so concrete providers are registered as
15
+ * injectable services and auto-booted by `Service.boot`. During auto-boot the
16
+ * framework passes a decaf `Context` instead of a blob config, so
17
+ * {@link BlobStoreService.getConfigFromArgs} must distinguish the two: a real
18
+ * config always carries a non-empty string `provider`, while a `Context` does
19
+ * not. When neither an explicit config nor the provider's environment slice
20
+ * resolves a config, a context-bearing auto-boot is skipped via
21
+ * {@link BlobStoreService.skipInitialization} instead of throwing, so optional
22
+ * providers (e.g. `blob-minio`/`blob-r2` registered alongside `blob-s3`) do not
23
+ * block the rest of the service graph. An explicit `initialize()` call with no
24
+ * resolvable config still fails loudly with a {@link ValidationError}.
25
+ *
26
+ * @template TClient - The provider-specific client type created by `initialize`
27
+ * @template TConfig - The provider-specific config extending {@link BlobStoreServiceConfig}
28
+ * @class BlobStoreService
29
+ * @memberOf module:integrations/blob/core/service
30
+ */
8
31
  export declare abstract class BlobStoreService<TClient = unknown, TConfig extends BlobStoreServiceConfig = BlobStoreServiceConfig> extends ClientBasedService<TClient, TConfig> {
32
+ /**
33
+ * @description Initializes the base service
34
+ * @summary Delegates to {@link ClientBasedService}; config and client are
35
+ * only set by a successful `initialize()` (or skipped on auto-boot).
36
+ */
9
37
  constructor();
38
+ /**
39
+ * @description The blob provider identifier for this service
40
+ * @summary Reads `provider` from the resolved config.
41
+ * @return {BlobProvider} The provider identifier (e.g. `"s3"`, `"memory"`)
42
+ * @throws {InternalError} If accessed before a successful `initialize()`
43
+ */
10
44
  get provider(): BlobProvider;
45
+ /**
46
+ * @description The logical source identifier for this service
47
+ * @summary Reads `sourceId` from the resolved config; used to compose blob URIs.
48
+ * @return {string} The configured source identifier
49
+ * @throws {InternalError} If accessed before a successful `initialize()`
50
+ */
11
51
  get sourceId(): string;
52
+ /**
53
+ * @description Initializes the provider client from config or environment
54
+ * @summary Concrete providers resolve their config via
55
+ * {@link BlobStoreService.getConfigFromArgs}, build the provider client, and
56
+ * store both on `_config`/`_client`. When no config can be resolved during a
57
+ * context-bearing auto-boot, providers return
58
+ * {@link BlobStoreService.skipInitialization} instead of throwing.
59
+ * @param {...ContextualArg<any>} args - An optional blob config, optionally followed by a decaf `Context` (auto-boot always appends a `Context`)
60
+ * @return {Promise<{config: TConfig, client: TClient}>} The resolved config and constructed client
61
+ */
12
62
  abstract initialize(...args: ContextualArgs<any>): Promise<{
13
63
  config: TConfig;
14
64
  client: TClient;
15
65
  }>;
66
+ /**
67
+ * @description Writes a blob under the given key
68
+ * @summary Serializes the value, optionally enforces `ifNotExists` and
69
+ * `expectedSha256`, and stores it with the provider backend.
70
+ * @param {BlobKey} key - Logical blob key (prefixed by the configured `prefix`)
71
+ * @param {BlobValue} value - Payload as `Uint8Array`, `Buffer`, or async stream
72
+ * @param {BlobPutOptions} [options] - Put options (conditional write, content type, metadata, checksum)
73
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
74
+ * @return {Promise<BlobPutResult>} The stored blob descriptor (key, URI, metadata)
75
+ */
16
76
  abstract put(key: BlobKey, value: BlobValue, options?: BlobPutOptions, ...args: MaybeContextualArg<any>): Promise<BlobPutResult>;
77
+ /**
78
+ * @description Reads a blob by key
79
+ * @summary Returns the payload as an async iterable along with its metadata.
80
+ * @param {BlobKey} key - Logical blob key
81
+ * @param {BlobGetOptions} [options] - Get options (byte range, version)
82
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
83
+ * @return {Promise<BlobGetResult>} The blob payload stream and metadata
84
+ * @throws {NotFoundError} If the blob does not exist
85
+ */
17
86
  abstract get(key: BlobKey, options?: BlobGetOptions, ...args: MaybeContextualArg<any>): Promise<BlobGetResult>;
87
+ /**
88
+ * @description Checks whether a blob exists
89
+ * @summary Must not throw when the blob is missing; resolves `false` instead.
90
+ * @param {BlobKey} key - Logical blob key
91
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
92
+ * @return {Promise<boolean>} `true` when the blob exists, `false` otherwise
93
+ */
18
94
  abstract has(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<boolean>;
95
+ /**
96
+ * @description Reads blob metadata without downloading the payload
97
+ * @summary Returns content type, length, etag, and provider-specific metadata.
98
+ * @param {BlobKey} key - Logical blob key
99
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
100
+ * @return {Promise<BlobMetadata>} The blob metadata
101
+ * @throws {NotFoundError} If the blob does not exist
102
+ */
19
103
  abstract stat(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<BlobMetadata>;
104
+ /**
105
+ * @description Deletes a blob
106
+ * @summary Should be idempotent; deleting a missing blob must not throw.
107
+ * @param {BlobKey} key - Logical blob key
108
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
109
+ * @return {Promise<void>} Resolves when the blob is deleted
110
+ */
20
111
  abstract delete(key: BlobKey, ...args: MaybeContextualArg<any>): Promise<void>;
112
+ /**
113
+ * @description Copies a blob to another key
114
+ * @summary Performs a server-side copy where the backend supports it.
115
+ * @param {BlobKey} fromKey - Source logical blob key
116
+ * @param {BlobKey} toKey - Target logical blob key
117
+ * @param {BlobPutOptions} [options] - Copy options (content type, metadata)
118
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
119
+ * @return {Promise<BlobPutResult>} The copied blob descriptor
120
+ * @throws {NotFoundError} If the source blob does not exist
121
+ */
21
122
  abstract copy(fromKey: BlobKey, toKey: BlobKey, options?: BlobPutOptions, ...args: MaybeContextualArg<any>): Promise<BlobPutResult>;
123
+ /**
124
+ * @description Lists blobs under an optional prefix
125
+ * @summary Returns a page of keys with metadata and an optional pagination cursor.
126
+ * @param {BlobListOptions} [options] - List options (prefix, limit, cursor)
127
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
128
+ * @return {Promise<BlobListResult>} The page of items and the next cursor, if any
129
+ */
22
130
  abstract list(options?: BlobListOptions, ...args: MaybeContextualArg<any>): Promise<BlobListResult>;
131
+ /**
132
+ * @description Builds a URL for direct access to a blob
133
+ * @summary Backends with signed-URL support return a presigned URL; others
134
+ * return the canonical provider URI.
135
+ * @param {BlobKey} key - Logical blob key
136
+ * @param {BlobUrlOptions} [options] - URL options (operation, expiry, content type)
137
+ * @param {...MaybeContextualArg<any>} args - Optional decaf `Context` for logging
138
+ * @return {Promise<BlobUrlResult>} The URL, HTTP method, and expiry
139
+ */
23
140
  abstract url(key: BlobKey, options?: BlobUrlOptions, ...args: MaybeContextualArg<any>): Promise<BlobUrlResult>;
141
+ /**
142
+ * @description Composes the physical storage key for a logical blob key
143
+ * @summary Prepends the configured `prefix` to the given key.
144
+ * @param {BlobKey} key - Logical blob key
145
+ * @return {string} The physical key including the configured prefix
146
+ */
24
147
  protected physicalKey(key: BlobKey): string;
148
+ /**
149
+ * @description Normalizes a logical blob key
150
+ * @summary Delegates to `cleanKey` (trims slashes and collapses separators).
151
+ * @param {BlobKey} key - Logical blob key
152
+ * @return {string} The normalized key
153
+ */
25
154
  protected cleanKey(key: BlobKey): string;
26
- protected getConfigFromArgs<TExpected extends TConfig>(...args: MaybeContextualArg<any>): TExpected;
155
+ /**
156
+ * @description Resolves the provider config from the initialize arguments or the provider's environment slice
157
+ * @summary `Service.boot` passes a decaf `Context` (not a blob config) to
158
+ * every registered `ClientBasedService`, so an object-shaped argument must
159
+ * not blindly be treated as config. A real blob config always carries a
160
+ * non-empty string `provider` (see {@link BlobStoreService.isConfigLike}).
161
+ * Resolution order: explicit config argument, then the provider's
162
+ * `configFromEnvironment()` slice. When neither resolves, `undefined` is
163
+ * returned only for a context-bearing auto-boot so an unconfigured provider
164
+ * (e.g. an optional `blob-minio`/`blob-r2` registered alongside `blob-s3`)
165
+ * does not block the whole service graph from booting; an explicit
166
+ * `initialize()` with neither config nor environment still throws.
167
+ *
168
+ * @template TExpected - The provider-specific config type expected by the caller
169
+ * @param {...MaybeContextualArg<any>} args - Initialize arguments: an optional blob config and/or a decaf `Context`
170
+ * @return {TExpected | undefined} The resolved config, or `undefined` when a context-bearing auto-boot has no resolvable config
171
+ * @throws {ValidationError} When called explicitly (no `Context` in `args`) and no config argument or environment config is available
172
+ *
173
+ * @mermaid
174
+ * sequenceDiagram
175
+ * participant Caller as Service.boot / initialize()
176
+ * participant Service as BlobStoreService
177
+ * participant Env as configFromEnvironment()
178
+ *
179
+ * Caller->>Service: ...args (config?, Context?)
180
+ * activate Service
181
+ * Service->>Service: isConfigLike(args[0])
182
+ * alt config-like argument present
183
+ * Service-->>Caller: config
184
+ * else no config-like argument
185
+ * Service->>Env: read provider env slice
186
+ * activate Env
187
+ * Env-->>Service: env config | undefined
188
+ * deactivate Env
189
+ * alt env config resolved
190
+ * Service-->>Caller: config
191
+ * else no env config
192
+ * Service->>Service: hasContext(args)
193
+ * alt Context present (auto-boot)
194
+ * Service-->>Caller: undefined (skip initialization)
195
+ * else explicit initialize()
196
+ * Service--xCaller: ValidationError
197
+ * end
198
+ * end
199
+ * end
200
+ * deactivate Service
201
+ */
202
+ protected getConfigFromArgs<TExpected extends TConfig>(...args: MaybeContextualArg<any>): TExpected | undefined;
203
+ /**
204
+ * @description Determines whether a value is a blob config object
205
+ * @summary True only for an object that carries a non-empty string
206
+ * `provider` and is not a decaf `Context`. Avoids mistaking a contextual
207
+ * argument (e.g. the `Context` that `Service.boot` passes) for the config.
208
+ * @param {unknown} value - The value to test
209
+ * @return {boolean} `true` when the value is config-like
210
+ */
211
+ protected isConfigLike(value: unknown): boolean;
212
+ /**
213
+ * @description Detects a decaf `Context` by duck typing
214
+ * @summary True for an object exposing the `Context` method surface (`get`,
215
+ * `accumulate`, `override`, `toOverrides` as functions). Deliberately avoids
216
+ * `instanceof` so linked builds of `@decaf-ts/core` with a duplicate
217
+ * `Context` constructor still resolve.
218
+ * @param {unknown} value - The value to test
219
+ * @return {boolean} `true` when the value duck-types as a decaf `Context`
220
+ */
221
+ protected isContextLike(value: unknown): boolean;
222
+ /**
223
+ * @description Determines whether the initialize arguments carry a decaf `Context`
224
+ * @summary A `Context` in the arguments marks a background auto-boot of an
225
+ * unconfigured provider (resolved to a skip by
226
+ * {@link BlobStoreService.getConfigFromArgs}), as opposed to an explicit
227
+ * `initialize()` that must fail loudly when no config is available.
228
+ * @param {MaybeContextualArg<any>[]} args - The initialize arguments to inspect
229
+ * @return {boolean} `true` when any argument duck-types as a decaf `Context`
230
+ */
231
+ protected hasContext(args: MaybeContextualArg<any>): boolean;
232
+ /**
233
+ * @description Skips initialization for an unconfigured provider during auto-boot
234
+ * @summary Clears `_config`/`_client` and returns a `{ config, client }`
235
+ * result compatible with the `ClientBasedService.initialize` signature so
236
+ * `Service.boot` completes without throwing. Leaves the service
237
+ * deterministic: the `config`/`client` getters still throw if accessed until
238
+ * the service is explicitly initialized with a resolvable config, and the
239
+ * rest of the service graph is not blocked by this provider's absence.
240
+ * Concrete providers invoke this from `initialize()` when
241
+ * `getConfigFromArgs` resolves no config and cast the result to the
242
+ * declared return type.
243
+ * @return {{config: undefined, client: undefined}} An unset config/client pair for the auto-boot path
244
+ */
245
+ protected skipInitialization(): {
246
+ config: undefined;
247
+ client: undefined;
248
+ };
27
249
  /**
28
250
  * @description Builds a config from the provider's environment as a fallback.
29
251
  * @summary Providers that support environment-sourced configuration should
30
252
  * override this to read from their own `blobs.<provider>` environment slice.
31
253
  */
32
254
  protected abstract configFromEnvironment(): TConfig | undefined;
255
+ /**
256
+ * @description Composes the provider URI for a blob
257
+ * @summary Builds `<scheme>://<sourceId>/<physicalKey>` and appends the
258
+ * extra query fragment (e.g. a version identifier) when provided.
259
+ * @param {BlobKey} key - Logical blob key
260
+ * @param {string} scheme - Provider URI scheme (e.g. `"s3"`, `"memory"`, `"file"`)
261
+ * @param {string} [extra] - Optional query string appended after `?`
262
+ * @return {string} The composed blob URI
263
+ */
33
264
  protected uri(key: BlobKey, scheme: string, extra?: string): string;
34
265
  }