@contractspec/lib.contracts-integrations 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/README.md +3 -0
  2. package/dist/index.d.ts +11 -0
  3. package/dist/index.js +3676 -0
  4. package/dist/integrations/binding.d.ts +14 -0
  5. package/dist/integrations/binding.js +1 -0
  6. package/dist/integrations/connection.d.ts +47 -0
  7. package/dist/integrations/connection.js +1 -0
  8. package/dist/integrations/docs/integrations.docblock.d.ts +2 -0
  9. package/dist/integrations/docs/integrations.docblock.js +110 -0
  10. package/dist/integrations/health.d.ts +17 -0
  11. package/dist/integrations/health.js +73 -0
  12. package/dist/integrations/health.test.d.ts +1 -0
  13. package/dist/integrations/index.d.ts +11 -0
  14. package/dist/integrations/index.js +3264 -0
  15. package/dist/integrations/integrations.capability.d.ts +1 -0
  16. package/dist/integrations/integrations.capability.js +18 -0
  17. package/dist/integrations/integrations.feature.d.ts +5 -0
  18. package/dist/integrations/integrations.feature.js +33 -0
  19. package/dist/integrations/meeting-recorder/contracts/index.d.ts +7 -0
  20. package/dist/integrations/meeting-recorder/contracts/index.js +474 -0
  21. package/dist/integrations/meeting-recorder/contracts/meetings.d.ts +451 -0
  22. package/dist/integrations/meeting-recorder/contracts/meetings.js +219 -0
  23. package/dist/integrations/meeting-recorder/contracts/transcripts.d.ts +166 -0
  24. package/dist/integrations/meeting-recorder/contracts/transcripts.js +287 -0
  25. package/dist/integrations/meeting-recorder/contracts/webhooks.d.ts +85 -0
  26. package/dist/integrations/meeting-recorder/contracts/webhooks.js +172 -0
  27. package/dist/integrations/meeting-recorder/meeting-recorder.capability.d.ts +1 -0
  28. package/dist/integrations/meeting-recorder/meeting-recorder.capability.js +18 -0
  29. package/dist/integrations/meeting-recorder/meeting-recorder.feature.d.ts +5 -0
  30. package/dist/integrations/meeting-recorder/meeting-recorder.feature.js +33 -0
  31. package/dist/integrations/meeting-recorder/models.d.ts +402 -0
  32. package/dist/integrations/meeting-recorder/models.js +122 -0
  33. package/dist/integrations/meeting-recorder/telemetry.d.ts +13 -0
  34. package/dist/integrations/meeting-recorder/telemetry.js +54 -0
  35. package/dist/integrations/openbanking/contracts/accounts.d.ts +282 -0
  36. package/dist/integrations/openbanking/contracts/accounts.js +328 -0
  37. package/dist/integrations/openbanking/contracts/balances.d.ts +158 -0
  38. package/dist/integrations/openbanking/contracts/balances.js +292 -0
  39. package/dist/integrations/openbanking/contracts/index.d.ts +7 -0
  40. package/dist/integrations/openbanking/contracts/index.js +644 -0
  41. package/dist/integrations/openbanking/contracts/transactions.d.ts +206 -0
  42. package/dist/integrations/openbanking/contracts/transactions.js +298 -0
  43. package/dist/integrations/openbanking/guards.d.ts +8 -0
  44. package/dist/integrations/openbanking/guards.js +42 -0
  45. package/dist/integrations/openbanking/guards.test.d.ts +1 -0
  46. package/dist/integrations/openbanking/models.d.ts +223 -0
  47. package/dist/integrations/openbanking/models.js +110 -0
  48. package/dist/integrations/openbanking/openbanking.capability.d.ts +1 -0
  49. package/dist/integrations/openbanking/openbanking.capability.js +18 -0
  50. package/dist/integrations/openbanking/openbanking.feature.d.ts +5 -0
  51. package/dist/integrations/openbanking/openbanking.feature.js +35 -0
  52. package/dist/integrations/openbanking/telemetry.d.ts +12 -0
  53. package/dist/integrations/openbanking/telemetry.js +51 -0
  54. package/dist/integrations/operations.d.ts +430 -0
  55. package/dist/integrations/operations.js +297 -0
  56. package/dist/integrations/operations.test.d.ts +1 -0
  57. package/dist/integrations/providers/analytics-reader.d.ts +103 -0
  58. package/dist/integrations/providers/analytics-reader.js +1 -0
  59. package/dist/integrations/providers/analytics-writer.d.ts +6 -0
  60. package/dist/integrations/providers/analytics-writer.js +1 -0
  61. package/dist/integrations/providers/analytics.d.ts +47 -0
  62. package/dist/integrations/providers/analytics.js +1 -0
  63. package/dist/integrations/providers/calendar.d.ts +75 -0
  64. package/dist/integrations/providers/calendar.js +1 -0
  65. package/dist/integrations/providers/database.d.ts +12 -0
  66. package/dist/integrations/providers/database.js +1 -0
  67. package/dist/integrations/providers/elevenlabs.d.ts +3 -0
  68. package/dist/integrations/providers/elevenlabs.js +86 -0
  69. package/dist/integrations/providers/email.d.ts +83 -0
  70. package/dist/integrations/providers/email.js +1 -0
  71. package/dist/integrations/providers/embedding.d.ts +21 -0
  72. package/dist/integrations/providers/embedding.js +1 -0
  73. package/dist/integrations/providers/fal.d.ts +3 -0
  74. package/dist/integrations/providers/fal.js +112 -0
  75. package/dist/integrations/providers/fathom.d.ts +3 -0
  76. package/dist/integrations/providers/fathom.js +126 -0
  77. package/dist/integrations/providers/fireflies.d.ts +3 -0
  78. package/dist/integrations/providers/fireflies.js +106 -0
  79. package/dist/integrations/providers/gcs-storage.d.ts +3 -0
  80. package/dist/integrations/providers/gcs-storage.js +97 -0
  81. package/dist/integrations/providers/gmail.d.ts +3 -0
  82. package/dist/integrations/providers/gmail.js +109 -0
  83. package/dist/integrations/providers/google-calendar.d.ts +3 -0
  84. package/dist/integrations/providers/google-calendar.js +92 -0
  85. package/dist/integrations/providers/gradium.d.ts +3 -0
  86. package/dist/integrations/providers/gradium.js +110 -0
  87. package/dist/integrations/providers/granola.d.ts +3 -0
  88. package/dist/integrations/providers/granola.js +107 -0
  89. package/dist/integrations/providers/index.d.ts +38 -0
  90. package/dist/integrations/providers/index.js +2094 -0
  91. package/dist/integrations/providers/jira.d.ts +3 -0
  92. package/dist/integrations/providers/jira.js +108 -0
  93. package/dist/integrations/providers/linear.d.ts +3 -0
  94. package/dist/integrations/providers/linear.js +107 -0
  95. package/dist/integrations/providers/llm.d.ts +79 -0
  96. package/dist/integrations/providers/llm.js +1 -0
  97. package/dist/integrations/providers/meeting-recorder.d.ts +129 -0
  98. package/dist/integrations/providers/meeting-recorder.js +1 -0
  99. package/dist/integrations/providers/mistral.d.ts +3 -0
  100. package/dist/integrations/providers/mistral.js +94 -0
  101. package/dist/integrations/providers/notion.d.ts +3 -0
  102. package/dist/integrations/providers/notion.js +113 -0
  103. package/dist/integrations/providers/openbanking.d.ts +125 -0
  104. package/dist/integrations/providers/openbanking.js +1 -0
  105. package/dist/integrations/providers/payments.d.ts +106 -0
  106. package/dist/integrations/providers/payments.js +1 -0
  107. package/dist/integrations/providers/posthog-llm-telemetry.d.ts +51 -0
  108. package/dist/integrations/providers/posthog-llm-telemetry.js +176 -0
  109. package/dist/integrations/providers/posthog.d.ts +3 -0
  110. package/dist/integrations/providers/posthog.js +106 -0
  111. package/dist/integrations/providers/postmark.d.ts +3 -0
  112. package/dist/integrations/providers/postmark.js +98 -0
  113. package/dist/integrations/providers/powens.d.ts +3 -0
  114. package/dist/integrations/providers/powens.js +124 -0
  115. package/dist/integrations/providers/project-management.d.ts +32 -0
  116. package/dist/integrations/providers/project-management.js +1 -0
  117. package/dist/integrations/providers/providers.test.d.ts +1 -0
  118. package/dist/integrations/providers/qdrant.d.ts +3 -0
  119. package/dist/integrations/providers/qdrant.js +101 -0
  120. package/dist/integrations/providers/registry.d.ts +6 -0
  121. package/dist/integrations/providers/registry.js +1878 -0
  122. package/dist/integrations/providers/sms.d.ts +31 -0
  123. package/dist/integrations/providers/sms.js +1 -0
  124. package/dist/integrations/providers/storage.d.ts +57 -0
  125. package/dist/integrations/providers/storage.js +1 -0
  126. package/dist/integrations/providers/stripe.d.ts +3 -0
  127. package/dist/integrations/providers/stripe.js +105 -0
  128. package/dist/integrations/providers/supabase-postgres.d.ts +3 -0
  129. package/dist/integrations/providers/supabase-postgres.js +87 -0
  130. package/dist/integrations/providers/supabase-vector.d.ts +3 -0
  131. package/dist/integrations/providers/supabase-vector.js +107 -0
  132. package/dist/integrations/providers/tldv.d.ts +3 -0
  133. package/dist/integrations/providers/tldv.js +106 -0
  134. package/dist/integrations/providers/twilio-sms.d.ts +3 -0
  135. package/dist/integrations/providers/twilio-sms.js +91 -0
  136. package/dist/integrations/providers/vector-store.d.ts +39 -0
  137. package/dist/integrations/providers/vector-store.js +1 -0
  138. package/dist/integrations/providers/voice.d.ts +31 -0
  139. package/dist/integrations/providers/voice.js +1 -0
  140. package/dist/integrations/runtime.d.ts +95 -0
  141. package/dist/integrations/runtime.js +209 -0
  142. package/dist/integrations/runtime.test.d.ts +1 -0
  143. package/dist/integrations/secrets/aws-secret-manager.d.ts +28 -0
  144. package/dist/integrations/secrets/aws-secret-manager.js +346 -0
  145. package/dist/integrations/secrets/env-secret-provider.d.ts +28 -0
  146. package/dist/integrations/secrets/env-secret-provider.js +159 -0
  147. package/dist/integrations/secrets/gcp-secret-manager.d.ts +29 -0
  148. package/dist/integrations/secrets/gcp-secret-manager.js +347 -0
  149. package/dist/integrations/secrets/index.d.ts +6 -0
  150. package/dist/integrations/secrets/index.js +1129 -0
  151. package/dist/integrations/secrets/manager.d.ts +44 -0
  152. package/dist/integrations/secrets/manager.js +183 -0
  153. package/dist/integrations/secrets/provider.d.ts +49 -0
  154. package/dist/integrations/secrets/provider.js +74 -0
  155. package/dist/integrations/secrets/provider.test.d.ts +1 -0
  156. package/dist/integrations/secrets/scaleway-secret-manager.d.ts +35 -0
  157. package/dist/integrations/secrets/scaleway-secret-manager.js +375 -0
  158. package/dist/integrations/secrets-types.d.ts +14 -0
  159. package/dist/integrations/secrets-types.js +1 -0
  160. package/dist/integrations/spec.d.ts +72 -0
  161. package/dist/integrations/spec.js +22 -0
  162. package/dist/integrations/spec.test.d.ts +1 -0
  163. package/dist/node/index.js +3675 -0
  164. package/dist/node/integrations/binding.js +0 -0
  165. package/dist/node/integrations/connection.js +0 -0
  166. package/dist/node/integrations/docs/integrations.docblock.js +109 -0
  167. package/dist/node/integrations/health.js +72 -0
  168. package/dist/node/integrations/index.js +3263 -0
  169. package/dist/node/integrations/integrations.capability.js +17 -0
  170. package/dist/node/integrations/integrations.feature.js +32 -0
  171. package/dist/node/integrations/meeting-recorder/contracts/index.js +473 -0
  172. package/dist/node/integrations/meeting-recorder/contracts/meetings.js +218 -0
  173. package/dist/node/integrations/meeting-recorder/contracts/transcripts.js +286 -0
  174. package/dist/node/integrations/meeting-recorder/contracts/webhooks.js +171 -0
  175. package/dist/node/integrations/meeting-recorder/meeting-recorder.capability.js +17 -0
  176. package/dist/node/integrations/meeting-recorder/meeting-recorder.feature.js +32 -0
  177. package/dist/node/integrations/meeting-recorder/models.js +121 -0
  178. package/dist/node/integrations/meeting-recorder/telemetry.js +53 -0
  179. package/dist/node/integrations/openbanking/contracts/accounts.js +327 -0
  180. package/dist/node/integrations/openbanking/contracts/balances.js +291 -0
  181. package/dist/node/integrations/openbanking/contracts/index.js +643 -0
  182. package/dist/node/integrations/openbanking/contracts/transactions.js +297 -0
  183. package/dist/node/integrations/openbanking/guards.js +41 -0
  184. package/dist/node/integrations/openbanking/models.js +109 -0
  185. package/dist/node/integrations/openbanking/openbanking.capability.js +17 -0
  186. package/dist/node/integrations/openbanking/openbanking.feature.js +34 -0
  187. package/dist/node/integrations/openbanking/telemetry.js +50 -0
  188. package/dist/node/integrations/operations.js +296 -0
  189. package/dist/node/integrations/providers/analytics-reader.js +0 -0
  190. package/dist/node/integrations/providers/analytics-writer.js +0 -0
  191. package/dist/node/integrations/providers/analytics.js +0 -0
  192. package/dist/node/integrations/providers/calendar.js +0 -0
  193. package/dist/node/integrations/providers/database.js +0 -0
  194. package/dist/node/integrations/providers/elevenlabs.js +85 -0
  195. package/dist/node/integrations/providers/email.js +0 -0
  196. package/dist/node/integrations/providers/embedding.js +0 -0
  197. package/dist/node/integrations/providers/fal.js +111 -0
  198. package/dist/node/integrations/providers/fathom.js +125 -0
  199. package/dist/node/integrations/providers/fireflies.js +105 -0
  200. package/dist/node/integrations/providers/gcs-storage.js +96 -0
  201. package/dist/node/integrations/providers/gmail.js +108 -0
  202. package/dist/node/integrations/providers/google-calendar.js +91 -0
  203. package/dist/node/integrations/providers/gradium.js +109 -0
  204. package/dist/node/integrations/providers/granola.js +106 -0
  205. package/dist/node/integrations/providers/index.js +2093 -0
  206. package/dist/node/integrations/providers/jira.js +107 -0
  207. package/dist/node/integrations/providers/linear.js +106 -0
  208. package/dist/node/integrations/providers/llm.js +0 -0
  209. package/dist/node/integrations/providers/meeting-recorder.js +0 -0
  210. package/dist/node/integrations/providers/mistral.js +93 -0
  211. package/dist/node/integrations/providers/notion.js +112 -0
  212. package/dist/node/integrations/providers/openbanking.js +0 -0
  213. package/dist/node/integrations/providers/payments.js +0 -0
  214. package/dist/node/integrations/providers/posthog-llm-telemetry.js +175 -0
  215. package/dist/node/integrations/providers/posthog.js +105 -0
  216. package/dist/node/integrations/providers/postmark.js +97 -0
  217. package/dist/node/integrations/providers/powens.js +123 -0
  218. package/dist/node/integrations/providers/project-management.js +0 -0
  219. package/dist/node/integrations/providers/qdrant.js +100 -0
  220. package/dist/node/integrations/providers/registry.js +1877 -0
  221. package/dist/node/integrations/providers/sms.js +0 -0
  222. package/dist/node/integrations/providers/storage.js +0 -0
  223. package/dist/node/integrations/providers/stripe.js +104 -0
  224. package/dist/node/integrations/providers/supabase-postgres.js +86 -0
  225. package/dist/node/integrations/providers/supabase-vector.js +106 -0
  226. package/dist/node/integrations/providers/tldv.js +105 -0
  227. package/dist/node/integrations/providers/twilio-sms.js +90 -0
  228. package/dist/node/integrations/providers/vector-store.js +0 -0
  229. package/dist/node/integrations/providers/voice.js +0 -0
  230. package/dist/node/integrations/runtime.js +208 -0
  231. package/dist/node/integrations/secrets/aws-secret-manager.js +345 -0
  232. package/dist/node/integrations/secrets/env-secret-provider.js +158 -0
  233. package/dist/node/integrations/secrets/gcp-secret-manager.js +346 -0
  234. package/dist/node/integrations/secrets/index.js +1128 -0
  235. package/dist/node/integrations/secrets/manager.js +182 -0
  236. package/dist/node/integrations/secrets/provider.js +73 -0
  237. package/dist/node/integrations/secrets/scaleway-secret-manager.js +374 -0
  238. package/dist/node/integrations/secrets-types.js +0 -0
  239. package/dist/node/integrations/spec.js +21 -0
  240. package/package.json +1029 -0
@@ -0,0 +1,182 @@
1
+ // src/integrations/secrets/provider.ts
2
+ import { Buffer } from "node:buffer";
3
+
4
+ class SecretProviderError extends Error {
5
+ provider;
6
+ reference;
7
+ code;
8
+ cause;
9
+ constructor(params) {
10
+ super(params.message);
11
+ this.name = "SecretProviderError";
12
+ this.provider = params.provider;
13
+ this.reference = params.reference;
14
+ this.code = params.code ?? "UNKNOWN";
15
+ this.cause = params.cause;
16
+ }
17
+ }
18
+ function parseSecretUri(reference) {
19
+ if (!reference) {
20
+ throw new SecretProviderError({
21
+ message: "Secret reference cannot be empty",
22
+ provider: "unknown",
23
+ reference,
24
+ code: "INVALID"
25
+ });
26
+ }
27
+ const [scheme, rest] = reference.split("://");
28
+ if (!scheme || !rest) {
29
+ throw new SecretProviderError({
30
+ message: `Invalid secret reference: ${reference}`,
31
+ provider: "unknown",
32
+ reference,
33
+ code: "INVALID"
34
+ });
35
+ }
36
+ const queryIndex = rest.indexOf("?");
37
+ if (queryIndex === -1) {
38
+ return {
39
+ provider: scheme,
40
+ path: rest
41
+ };
42
+ }
43
+ const path = rest.slice(0, queryIndex);
44
+ const query = rest.slice(queryIndex + 1);
45
+ const extras = Object.fromEntries(query.split("&").filter(Boolean).map((pair) => {
46
+ const [keyRaw, valueRaw] = pair.split("=");
47
+ const key = keyRaw ?? "";
48
+ const value = valueRaw ?? "";
49
+ return [decodeURIComponent(key), decodeURIComponent(value)];
50
+ }));
51
+ return {
52
+ provider: scheme,
53
+ path,
54
+ extras
55
+ };
56
+ }
57
+ function normalizeSecretPayload(payload) {
58
+ if (payload.data instanceof Uint8Array) {
59
+ return payload.data;
60
+ }
61
+ if (payload.encoding === "base64") {
62
+ return Buffer.from(payload.data, "base64");
63
+ }
64
+ if (payload.encoding === "binary") {
65
+ return Buffer.from(payload.data, "binary");
66
+ }
67
+ return Buffer.from(payload.data, "utf-8");
68
+ }
69
+
70
+ // src/integrations/secrets/manager.ts
71
+ class SecretProviderManager {
72
+ id;
73
+ providers = [];
74
+ registrationCounter = 0;
75
+ constructor(options = {}) {
76
+ this.id = options.id ?? "secret-provider-manager";
77
+ const initialProviders = options.providers ?? [];
78
+ for (const entry of initialProviders) {
79
+ this.register(entry.provider, { priority: entry.priority });
80
+ }
81
+ }
82
+ register(provider, options = {}) {
83
+ this.providers.push({
84
+ provider,
85
+ priority: options.priority ?? 0,
86
+ order: this.registrationCounter++
87
+ });
88
+ this.providers.sort((a, b) => {
89
+ if (a.priority !== b.priority) {
90
+ return b.priority - a.priority;
91
+ }
92
+ return a.order - b.order;
93
+ });
94
+ return this;
95
+ }
96
+ canHandle(reference) {
97
+ return this.providers.some(({ provider }) => safeCanHandle(provider, reference));
98
+ }
99
+ async getSecret(reference, options) {
100
+ const errors = [];
101
+ for (const { provider } of this.providers) {
102
+ if (!safeCanHandle(provider, reference)) {
103
+ continue;
104
+ }
105
+ try {
106
+ return await provider.getSecret(reference, options);
107
+ } catch (error) {
108
+ if (error instanceof SecretProviderError) {
109
+ errors.push(error);
110
+ if (error.code !== "NOT_FOUND") {
111
+ break;
112
+ }
113
+ continue;
114
+ }
115
+ throw error;
116
+ }
117
+ }
118
+ throw this.composeError("getSecret", reference, errors, options?.version);
119
+ }
120
+ async setSecret(reference, payload) {
121
+ return this.delegateToFirst("setSecret", reference, (provider) => provider.setSecret(reference, payload));
122
+ }
123
+ async rotateSecret(reference, payload) {
124
+ return this.delegateToFirst("rotateSecret", reference, (provider) => provider.rotateSecret(reference, payload));
125
+ }
126
+ async deleteSecret(reference) {
127
+ await this.delegateToFirst("deleteSecret", reference, (provider) => provider.deleteSecret(reference));
128
+ }
129
+ async delegateToFirst(operation, reference, invoker) {
130
+ const errors = [];
131
+ for (const { provider } of this.providers) {
132
+ if (!safeCanHandle(provider, reference)) {
133
+ continue;
134
+ }
135
+ try {
136
+ return await invoker(provider);
137
+ } catch (error) {
138
+ if (error instanceof SecretProviderError) {
139
+ errors.push(error);
140
+ continue;
141
+ }
142
+ throw error;
143
+ }
144
+ }
145
+ throw this.composeError(operation, reference, errors);
146
+ }
147
+ composeError(operation, reference, errors, version) {
148
+ if (errors.length === 1) {
149
+ const [singleError] = errors;
150
+ if (singleError) {
151
+ return singleError;
152
+ }
153
+ }
154
+ const messageParts = [
155
+ `No registered secret provider could ${operation}`,
156
+ `reference "${reference}"`
157
+ ];
158
+ if (version) {
159
+ messageParts.push(`(version: ${version})`);
160
+ }
161
+ if (errors.length > 1) {
162
+ messageParts.push(`Attempts: ${errors.map((error) => `${error.provider}:${error.code}`).join(", ")}`);
163
+ }
164
+ return new SecretProviderError({
165
+ message: messageParts.join(" "),
166
+ provider: this.id,
167
+ reference,
168
+ code: errors.length > 0 ? errors[errors.length - 1]?.code ?? "UNKNOWN" : "UNKNOWN",
169
+ cause: errors
170
+ });
171
+ }
172
+ }
173
+ function safeCanHandle(provider, reference) {
174
+ try {
175
+ return provider.canHandle(reference);
176
+ } catch {
177
+ return false;
178
+ }
179
+ }
180
+ export {
181
+ SecretProviderManager
182
+ };
@@ -0,0 +1,73 @@
1
+ // src/integrations/secrets/provider.ts
2
+ import { Buffer } from "node:buffer";
3
+
4
+ class SecretProviderError extends Error {
5
+ provider;
6
+ reference;
7
+ code;
8
+ cause;
9
+ constructor(params) {
10
+ super(params.message);
11
+ this.name = "SecretProviderError";
12
+ this.provider = params.provider;
13
+ this.reference = params.reference;
14
+ this.code = params.code ?? "UNKNOWN";
15
+ this.cause = params.cause;
16
+ }
17
+ }
18
+ function parseSecretUri(reference) {
19
+ if (!reference) {
20
+ throw new SecretProviderError({
21
+ message: "Secret reference cannot be empty",
22
+ provider: "unknown",
23
+ reference,
24
+ code: "INVALID"
25
+ });
26
+ }
27
+ const [scheme, rest] = reference.split("://");
28
+ if (!scheme || !rest) {
29
+ throw new SecretProviderError({
30
+ message: `Invalid secret reference: ${reference}`,
31
+ provider: "unknown",
32
+ reference,
33
+ code: "INVALID"
34
+ });
35
+ }
36
+ const queryIndex = rest.indexOf("?");
37
+ if (queryIndex === -1) {
38
+ return {
39
+ provider: scheme,
40
+ path: rest
41
+ };
42
+ }
43
+ const path = rest.slice(0, queryIndex);
44
+ const query = rest.slice(queryIndex + 1);
45
+ const extras = Object.fromEntries(query.split("&").filter(Boolean).map((pair) => {
46
+ const [keyRaw, valueRaw] = pair.split("=");
47
+ const key = keyRaw ?? "";
48
+ const value = valueRaw ?? "";
49
+ return [decodeURIComponent(key), decodeURIComponent(value)];
50
+ }));
51
+ return {
52
+ provider: scheme,
53
+ path,
54
+ extras
55
+ };
56
+ }
57
+ function normalizeSecretPayload(payload) {
58
+ if (payload.data instanceof Uint8Array) {
59
+ return payload.data;
60
+ }
61
+ if (payload.encoding === "base64") {
62
+ return Buffer.from(payload.data, "base64");
63
+ }
64
+ if (payload.encoding === "binary") {
65
+ return Buffer.from(payload.data, "binary");
66
+ }
67
+ return Buffer.from(payload.data, "utf-8");
68
+ }
69
+ export {
70
+ parseSecretUri,
71
+ normalizeSecretPayload,
72
+ SecretProviderError
73
+ };
@@ -0,0 +1,374 @@
1
+ // src/integrations/secrets/provider.ts
2
+ import { Buffer } from "node:buffer";
3
+
4
+ class SecretProviderError extends Error {
5
+ provider;
6
+ reference;
7
+ code;
8
+ cause;
9
+ constructor(params) {
10
+ super(params.message);
11
+ this.name = "SecretProviderError";
12
+ this.provider = params.provider;
13
+ this.reference = params.reference;
14
+ this.code = params.code ?? "UNKNOWN";
15
+ this.cause = params.cause;
16
+ }
17
+ }
18
+ function parseSecretUri(reference) {
19
+ if (!reference) {
20
+ throw new SecretProviderError({
21
+ message: "Secret reference cannot be empty",
22
+ provider: "unknown",
23
+ reference,
24
+ code: "INVALID"
25
+ });
26
+ }
27
+ const [scheme, rest] = reference.split("://");
28
+ if (!scheme || !rest) {
29
+ throw new SecretProviderError({
30
+ message: `Invalid secret reference: ${reference}`,
31
+ provider: "unknown",
32
+ reference,
33
+ code: "INVALID"
34
+ });
35
+ }
36
+ const queryIndex = rest.indexOf("?");
37
+ if (queryIndex === -1) {
38
+ return {
39
+ provider: scheme,
40
+ path: rest
41
+ };
42
+ }
43
+ const path = rest.slice(0, queryIndex);
44
+ const query = rest.slice(queryIndex + 1);
45
+ const extras = Object.fromEntries(query.split("&").filter(Boolean).map((pair) => {
46
+ const [keyRaw, valueRaw] = pair.split("=");
47
+ const key = keyRaw ?? "";
48
+ const value = valueRaw ?? "";
49
+ return [decodeURIComponent(key), decodeURIComponent(value)];
50
+ }));
51
+ return {
52
+ provider: scheme,
53
+ path,
54
+ extras
55
+ };
56
+ }
57
+ function normalizeSecretPayload(payload) {
58
+ if (payload.data instanceof Uint8Array) {
59
+ return payload.data;
60
+ }
61
+ if (payload.encoding === "base64") {
62
+ return Buffer.from(payload.data, "base64");
63
+ }
64
+ if (payload.encoding === "binary") {
65
+ return Buffer.from(payload.data, "binary");
66
+ }
67
+ return Buffer.from(payload.data, "utf-8");
68
+ }
69
+
70
+ // src/integrations/secrets/scaleway-secret-manager.ts
71
+ import { Buffer as Buffer2 } from "node:buffer";
72
+ var UUID_V4_LIKE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
73
+
74
+ class ScalewaySecretManagerProvider {
75
+ id = "scaleway-secret-manager";
76
+ token;
77
+ defaultRegion;
78
+ defaultProjectId;
79
+ baseUrl;
80
+ fetchFn;
81
+ constructor(options = {}) {
82
+ this.token = options.token ?? process.env.SCW_SECRET_KEY ?? process.env.SCALEWAY_SECRET_KEY ?? "";
83
+ this.defaultRegion = options.defaultRegion ?? process.env.SCW_DEFAULT_REGION ?? process.env.SCW_REGION;
84
+ this.defaultProjectId = options.defaultProjectId ?? process.env.SCW_DEFAULT_PROJECT_ID ?? process.env.SCW_PROJECT_ID;
85
+ this.baseUrl = options.baseUrl ?? "https://api.scaleway.com";
86
+ this.fetchFn = options.fetch ?? fetch;
87
+ }
88
+ canHandle(reference) {
89
+ try {
90
+ const parsed = parseSecretUri(reference);
91
+ return parsed.provider === "scw" && (parsed.path === "secret-manager" || parsed.path.startsWith("secret-manager/"));
92
+ } catch {
93
+ return false;
94
+ }
95
+ }
96
+ async getSecret(reference, options) {
97
+ const location = this.parseReference(reference);
98
+ if (!this.token) {
99
+ throw new SecretProviderError({
100
+ message: "Scaleway secret manager token is missing (set SCW_SECRET_KEY / SCALEWAY_SECRET_KEY).",
101
+ provider: this.id,
102
+ reference,
103
+ code: "FORBIDDEN"
104
+ });
105
+ }
106
+ if (!UUID_V4_LIKE.test(location.secretIdOrName)) {
107
+ throw new SecretProviderError({
108
+ message: "Scaleway getSecret requires a secretId (uuid) reference, not a secret name.",
109
+ provider: this.id,
110
+ reference,
111
+ code: "INVALID"
112
+ });
113
+ }
114
+ const revision = options?.version ?? location.revision ?? "latest";
115
+ const url = `${this.baseUrl}/secret-manager/v1beta1/regions/${encodeURIComponent(location.region)}/secrets/${encodeURIComponent(location.secretIdOrName)}/versions/${encodeURIComponent(revision)}/access`;
116
+ const response = await this.fetchFn(url, {
117
+ method: "GET",
118
+ headers: {
119
+ "X-Auth-Token": this.token
120
+ }
121
+ });
122
+ if (!response.ok) {
123
+ throw await toScalewayError({
124
+ response,
125
+ provider: this.id,
126
+ reference,
127
+ operation: "getSecret"
128
+ });
129
+ }
130
+ const payload = await response.json();
131
+ const dataB64 = extractScalewayData(payload);
132
+ return {
133
+ data: Buffer2.from(dataB64, "base64"),
134
+ version: revision,
135
+ metadata: {
136
+ region: location.region,
137
+ secretId: location.secretIdOrName
138
+ },
139
+ retrievedAt: new Date
140
+ };
141
+ }
142
+ async setSecret(reference, payload) {
143
+ const location = this.parseReference(reference);
144
+ if (!this.token) {
145
+ throw new SecretProviderError({
146
+ message: "Scaleway secret manager token is missing (set SCW_SECRET_KEY / SCALEWAY_SECRET_KEY).",
147
+ provider: this.id,
148
+ reference,
149
+ code: "FORBIDDEN"
150
+ });
151
+ }
152
+ const bytes = normalizeSecretPayload(payload);
153
+ const encoded = Buffer2.from(bytes).toString("base64");
154
+ const secretId = UUID_V4_LIKE.test(location.secretIdOrName) ? location.secretIdOrName : await this.createSecret({
155
+ region: location.region,
156
+ name: location.secretIdOrName,
157
+ reference
158
+ });
159
+ const version = await this.createSecretVersion({
160
+ region: location.region,
161
+ secretId,
162
+ dataB64: encoded,
163
+ reference
164
+ });
165
+ return {
166
+ reference: this.buildReference(location.region, secretId, {
167
+ version
168
+ }),
169
+ version
170
+ };
171
+ }
172
+ async rotateSecret(reference, payload) {
173
+ return this.setSecret(reference, payload);
174
+ }
175
+ async deleteSecret(reference) {
176
+ const location = this.parseReference(reference);
177
+ if (!this.token) {
178
+ throw new SecretProviderError({
179
+ message: "Scaleway secret manager token is missing (set SCW_SECRET_KEY / SCALEWAY_SECRET_KEY).",
180
+ provider: this.id,
181
+ reference,
182
+ code: "FORBIDDEN"
183
+ });
184
+ }
185
+ if (!UUID_V4_LIKE.test(location.secretIdOrName)) {
186
+ throw new SecretProviderError({
187
+ message: "Scaleway deleteSecret requires a secretId (uuid) reference, not a secret name.",
188
+ provider: this.id,
189
+ reference,
190
+ code: "INVALID"
191
+ });
192
+ }
193
+ const url = `${this.baseUrl}/secret-manager/v1beta1/regions/${encodeURIComponent(location.region)}/secrets/${encodeURIComponent(location.secretIdOrName)}`;
194
+ const response = await this.fetchFn(url, {
195
+ method: "DELETE",
196
+ headers: {
197
+ "X-Auth-Token": this.token
198
+ }
199
+ });
200
+ if (!response.ok) {
201
+ throw await toScalewayError({
202
+ response,
203
+ provider: this.id,
204
+ reference,
205
+ operation: "deleteSecret"
206
+ });
207
+ }
208
+ }
209
+ parseReference(reference) {
210
+ const parsed = parseSecretUri(reference);
211
+ if (parsed.provider !== "scw") {
212
+ throw new SecretProviderError({
213
+ message: `Unsupported secret provider: ${parsed.provider}`,
214
+ provider: this.id,
215
+ reference,
216
+ code: "INVALID"
217
+ });
218
+ }
219
+ const segments = parsed.path.split("/").filter(Boolean);
220
+ if (segments.length < 2 || segments[0] !== "secret-manager") {
221
+ throw new SecretProviderError({
222
+ message: "Expected secret reference format scw://secret-manager/{region}/{secretIdOrName}[?version=...]",
223
+ provider: this.id,
224
+ reference,
225
+ code: "INVALID"
226
+ });
227
+ }
228
+ const region = segments[1] ?? this.defaultRegion;
229
+ if (!region) {
230
+ throw new SecretProviderError({
231
+ message: "Scaleway region must be provided either in reference (scw://secret-manager/{region}/...) or via SCW_DEFAULT_REGION/SCW_REGION.",
232
+ provider: this.id,
233
+ reference,
234
+ code: "INVALID"
235
+ });
236
+ }
237
+ const secretIdOrName = segments.slice(2).join("/");
238
+ if (!secretIdOrName) {
239
+ throw new SecretProviderError({
240
+ message: `Unable to resolve secret id/name from reference "${parsed.path}"`,
241
+ provider: this.id,
242
+ reference,
243
+ code: "INVALID"
244
+ });
245
+ }
246
+ return {
247
+ region,
248
+ secretIdOrName,
249
+ revision: parsed.extras?.version
250
+ };
251
+ }
252
+ async createSecret(params) {
253
+ const projectId = this.defaultProjectId;
254
+ if (!projectId) {
255
+ throw new SecretProviderError({
256
+ message: "Scaleway project id is required to create secrets by name (set SCW_DEFAULT_PROJECT_ID/SCW_PROJECT_ID).",
257
+ provider: this.id,
258
+ reference: params.reference,
259
+ code: "INVALID"
260
+ });
261
+ }
262
+ const url = `${this.baseUrl}/secret-manager/v1beta1/regions/${encodeURIComponent(params.region)}/secrets`;
263
+ const response = await this.fetchFn(url, {
264
+ method: "POST",
265
+ headers: {
266
+ "Content-Type": "application/json",
267
+ "X-Auth-Token": this.token
268
+ },
269
+ body: JSON.stringify({
270
+ name: params.name,
271
+ project_id: projectId
272
+ })
273
+ });
274
+ if (!response.ok) {
275
+ throw await toScalewayError({
276
+ response,
277
+ provider: this.id,
278
+ reference: params.reference,
279
+ operation: "createSecret"
280
+ });
281
+ }
282
+ const payload = await response.json();
283
+ const secretId = extractScalewaySecretId(payload);
284
+ return secretId;
285
+ }
286
+ async createSecretVersion(params) {
287
+ const url = `${this.baseUrl}/secret-manager/v1beta1/regions/${encodeURIComponent(params.region)}/secrets/${encodeURIComponent(params.secretId)}/versions`;
288
+ const response = await this.fetchFn(url, {
289
+ method: "POST",
290
+ headers: {
291
+ "Content-Type": "application/json",
292
+ "X-Auth-Token": this.token
293
+ },
294
+ body: JSON.stringify({
295
+ data: params.dataB64
296
+ })
297
+ });
298
+ if (!response.ok) {
299
+ throw await toScalewayError({
300
+ response,
301
+ provider: this.id,
302
+ reference: params.reference,
303
+ operation: "createSecretVersion"
304
+ });
305
+ }
306
+ const payload = await response.json();
307
+ return extractScalewayRevision(payload) ?? "latest";
308
+ }
309
+ buildReference(region, secretId, extras) {
310
+ const base = `scw://secret-manager/${region}/${secretId}`;
311
+ const query = extras ? Object.entries(extras).filter(([, value]) => Boolean(value)).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&") : "";
312
+ return query ? `${base}?${query}` : base;
313
+ }
314
+ }
315
+ function extractScalewayData(payload) {
316
+ if (!payload || typeof payload !== "object") {
317
+ throw new Error("Invalid scaleway secret payload");
318
+ }
319
+ const record = payload;
320
+ if (typeof record.data === "string" && record.data) {
321
+ return record.data;
322
+ }
323
+ throw new Error("Scaleway secret payload is missing data");
324
+ }
325
+ function extractScalewaySecretId(payload) {
326
+ if (!payload || typeof payload !== "object") {
327
+ throw new Error("Invalid scaleway createSecret payload");
328
+ }
329
+ const record = payload;
330
+ if (typeof record.id === "string" && record.id) {
331
+ return record.id;
332
+ }
333
+ throw new Error("Scaleway createSecret response is missing id");
334
+ }
335
+ function extractScalewayRevision(payload) {
336
+ if (!payload || typeof payload !== "object") {
337
+ return;
338
+ }
339
+ const record = payload;
340
+ if (typeof record.revision === "number") {
341
+ return String(record.revision);
342
+ }
343
+ if (typeof record.revision === "string" && record.revision) {
344
+ return record.revision;
345
+ }
346
+ if (typeof record.id === "string" && record.id) {
347
+ return record.id;
348
+ }
349
+ return;
350
+ }
351
+ async function toScalewayError(params) {
352
+ const { response, provider, reference, operation } = params;
353
+ const code = response.status === 404 ? "NOT_FOUND" : response.status === 401 || response.status === 403 ? "FORBIDDEN" : response.status >= 400 && response.status < 500 ? "INVALID" : "UNKNOWN";
354
+ const bodyText = await safeReadBody(response);
355
+ const message = bodyText ? `Scaleway Secret Manager ${operation} failed (${response.status}): ${bodyText}` : `Scaleway Secret Manager ${operation} failed (${response.status})`;
356
+ return new SecretProviderError({
357
+ message,
358
+ provider,
359
+ reference,
360
+ code
361
+ });
362
+ }
363
+ async function safeReadBody(response) {
364
+ try {
365
+ const text = await response.text();
366
+ const trimmed = text.trim();
367
+ return trimmed.length ? trimmed : undefined;
368
+ } catch {
369
+ return;
370
+ }
371
+ }
372
+ export {
373
+ ScalewaySecretManagerProvider
374
+ };
File without changes
@@ -0,0 +1,21 @@
1
+ // src/integrations/spec.ts
2
+ import { SpecContractRegistry } from "@contractspec/lib.contracts-spec/registry";
3
+ var integrationKey = (meta) => `${meta.key}.v${meta.version}`;
4
+
5
+ class IntegrationSpecRegistry extends SpecContractRegistry {
6
+ constructor(items) {
7
+ super("integration", items);
8
+ }
9
+ getByCategory(category) {
10
+ return this.list().filter((spec) => spec.meta.category === category);
11
+ }
12
+ }
13
+ function makeIntegrationSpecKey(meta) {
14
+ return integrationKey(meta);
15
+ }
16
+ var defineIntegration = (spec) => spec;
17
+ export {
18
+ makeIntegrationSpecKey,
19
+ defineIntegration,
20
+ IntegrationSpecRegistry
21
+ };