@distilled.cloud/digitalocean 0.0.0 → 1.0.0-rc.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/lib/credentials.d.ts +32 -0
- package/lib/credentials.d.ts.map +1 -0
- package/lib/credentials.js +38 -0
- package/lib/credentials.js.map +1 -0
- package/lib/errors.d.ts +36 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +26 -0
- package/lib/errors.js.map +1 -0
- package/lib/index.d.ts +25 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +33 -0
- package/lib/index.js.map +1 -0
- package/lib/pagination.d.ts +10 -0
- package/lib/pagination.d.ts.map +1 -0
- package/lib/pagination.js +10 -0
- package/lib/pagination.js.map +1 -0
- package/lib/protocol.d.ts +18 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +66 -0
- package/lib/protocol.js.map +1 -0
- package/lib/retry.d.ts +32 -0
- package/lib/retry.d.ts.map +1 -0
- package/lib/retry.js +32 -0
- package/lib/retry.js.map +1 -0
- package/lib/services/digitalocean.d.ts +21516 -0
- package/lib/services/digitalocean.d.ts.map +1 -0
- package/lib/services/digitalocean.js +21134 -0
- package/lib/services/digitalocean.js.map +1 -0
- package/lib/services/index.d.ts +2 -0
- package/lib/services/index.d.ts.map +1 -0
- package/lib/services/index.js +3 -0
- package/lib/services/index.js.map +1 -0
- package/lib/traits.d.ts +11 -0
- package/lib/traits.d.ts.map +1 -0
- package/lib/traits.js +11 -0
- package/lib/traits.js.map +1 -0
- package/package.json +77 -6
- package/src/credentials.ts +62 -0
- package/src/errors.ts +50 -0
- package/src/index.ts +42 -0
- package/src/pagination.ts +12 -0
- package/src/protocol.ts +92 -0
- package/src/retry.ts +56 -0
- package/src/services/digitalocean.ts +54116 -0
- package/src/services/index.ts +2 -0
- package/src/traits.ts +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAC"}
|
package/lib/traits.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean SDK trait surface — hand-written.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the generic protocol traits from core (so generated operations
|
|
5
|
+
* import everything from one place). DigitalOcean has no envelope of its
|
|
6
|
+
* own; the REST-protocol traits (SensitiveValue / RawResponse) come from
|
|
7
|
+
* `core/protocol-rest`.
|
|
8
|
+
*/
|
|
9
|
+
export { Body, Header, Query, Label, Http, ResponseCode, HttpBody, FormDataFile, KeyDictionary, UnionCases, applyErrorMatchers, getErrorMatchers, type HttpTrait, type ErrorMatcher, bodySymbol, headerSymbol, querySymbol, labelSymbol, httpSymbol, responseCodeSymbol, httpBodySymbol, formDataFileSymbol, keyDictionarySymbol, unionCasesSymbol, errorMatchersSymbol, } from "@distilled.cloud/core/trait";
|
|
10
|
+
export { SensitiveValue, RawResponse, RawResponseRoot, sensitiveValueSymbol, rawResponseSymbol, rawResponseRootSymbol, } from "@distilled.cloud/core/protocol-rest";
|
|
11
|
+
//# sourceMappingURL=traits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traits.d.ts","sourceRoot":"","sources":["../src/traits.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,IAAI,EACJ,MAAM,EACN,KAAK,EACL,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,qCAAqC,CAAC"}
|
package/lib/traits.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean SDK trait surface — hand-written.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the generic protocol traits from core (so generated operations
|
|
5
|
+
* import everything from one place). DigitalOcean has no envelope of its
|
|
6
|
+
* own; the REST-protocol traits (SensitiveValue / RawResponse) come from
|
|
7
|
+
* `core/protocol-rest`.
|
|
8
|
+
*/
|
|
9
|
+
export { Body, Header, Query, Label, Http, ResponseCode, HttpBody, FormDataFile, KeyDictionary, UnionCases, applyErrorMatchers, getErrorMatchers, bodySymbol, headerSymbol, querySymbol, labelSymbol, httpSymbol, responseCodeSymbol, httpBodySymbol, formDataFileSymbol, keyDictionarySymbol, unionCasesSymbol, errorMatchersSymbol, } from "@distilled.cloud/core/trait";
|
|
10
|
+
export { SensitiveValue, RawResponse, RawResponseRoot, sensitiveValueSymbol, rawResponseSymbol, rawResponseRootSymbol, } from "@distilled.cloud/core/protocol-rest";
|
|
11
|
+
//# sourceMappingURL=traits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traits.js","sourceRoot":"","sources":["../src/traits.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EACL,IAAI,EACJ,MAAM,EACN,KAAK,EACL,KAAK,EACL,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,aAAa,EACb,UAAU,EACV,kBAAkB,EAClB,gBAAgB,EAGhB,UAAU,EACV,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,cAAc,EACd,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,qCAAqC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,14 +1,85 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@distilled.cloud/digitalocean",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Placeholder package for an upcoming distilled SDK.",
|
|
5
|
-
"license": "Apache-2.0",
|
|
3
|
+
"version": "1.0.0-rc.11",
|
|
6
4
|
"repository": {
|
|
7
5
|
"type": "git",
|
|
8
6
|
"url": "https://github.com/alchemy-run/distilled",
|
|
9
7
|
"directory": "packages/digitalocean"
|
|
10
8
|
},
|
|
11
|
-
"
|
|
12
|
-
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"files": [
|
|
13
|
+
"lib",
|
|
14
|
+
"src"
|
|
15
|
+
],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/index.d.ts",
|
|
19
|
+
"bun": "./src/index.ts",
|
|
20
|
+
"worker": "./src/index.ts",
|
|
21
|
+
"default": "./lib/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./Credentials": {
|
|
24
|
+
"types": "./lib/credentials.d.ts",
|
|
25
|
+
"bun": "./src/credentials.ts",
|
|
26
|
+
"worker": "./src/credentials.ts",
|
|
27
|
+
"default": "./lib/credentials.js"
|
|
28
|
+
},
|
|
29
|
+
"./Errors": {
|
|
30
|
+
"types": "./lib/errors.d.ts",
|
|
31
|
+
"bun": "./src/errors.ts",
|
|
32
|
+
"worker": "./src/errors.ts",
|
|
33
|
+
"default": "./lib/errors.js"
|
|
34
|
+
},
|
|
35
|
+
"./Traits": {
|
|
36
|
+
"types": "./lib/traits.d.ts",
|
|
37
|
+
"bun": "./src/traits.ts",
|
|
38
|
+
"worker": "./src/traits.ts",
|
|
39
|
+
"default": "./lib/traits.js"
|
|
40
|
+
},
|
|
41
|
+
"./Pagination": {
|
|
42
|
+
"types": "./lib/pagination.d.ts",
|
|
43
|
+
"bun": "./src/pagination.ts",
|
|
44
|
+
"worker": "./src/pagination.ts",
|
|
45
|
+
"default": "./lib/pagination.js"
|
|
46
|
+
},
|
|
47
|
+
"./Protocol": {
|
|
48
|
+
"types": "./lib/protocol.d.ts",
|
|
49
|
+
"bun": "./src/protocol.ts",
|
|
50
|
+
"worker": "./src/protocol.ts",
|
|
51
|
+
"default": "./lib/protocol.js"
|
|
52
|
+
},
|
|
53
|
+
"./Retry": {
|
|
54
|
+
"types": "./lib/retry.d.ts",
|
|
55
|
+
"bun": "./src/retry.ts",
|
|
56
|
+
"worker": "./src/retry.ts",
|
|
57
|
+
"default": "./lib/retry.js"
|
|
58
|
+
},
|
|
59
|
+
"./*": {
|
|
60
|
+
"types": "./lib/services/*.d.ts",
|
|
61
|
+
"bun": "./src/services/*.ts",
|
|
62
|
+
"worker": "./src/services/*.ts",
|
|
63
|
+
"default": "./lib/services/*.js"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"dependencies": {
|
|
67
|
+
"@distilled.cloud/core": "1.0.0-rc.11"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/bun": "1.3.14",
|
|
71
|
+
"@types/node": "26.2.0",
|
|
72
|
+
"@effect/platform-bun": ">=4.0.0-rc.115 || >=4.0.0"
|
|
73
|
+
},
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"effect": ">=4.0.0-rc.115 || >=4.0.0"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"build": "tsc -b",
|
|
79
|
+
"convert": "bun scripts/convert.ts",
|
|
80
|
+
"generate": "bun scripts/convert.ts && bun scripts/generate.ts",
|
|
81
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
82
|
+
"specs:fetch": "git submodule update --force --init --depth=1 specs/spec-mirror-digitalocean",
|
|
83
|
+
"specs:update": "git submodule update --remote --force --depth=1 specs/spec-mirror-digitalocean"
|
|
13
84
|
}
|
|
14
|
-
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean credentials — hand-written.
|
|
3
|
+
*
|
|
4
|
+
* The `Credentials` service holds an *effect* that resolves the current
|
|
5
|
+
* credentials on every request (the protocol layer resolves it per request
|
|
6
|
+
* on the calling fiber). A Personal Access Token is sent as
|
|
7
|
+
* `Authorization: Bearer <apiKey>`.
|
|
8
|
+
*/
|
|
9
|
+
import * as Context from "effect/Context";
|
|
10
|
+
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Layer from "effect/Layer";
|
|
12
|
+
import * as Redacted from "effect/Redacted";
|
|
13
|
+
import { ConfigError } from "@distilled.cloud/core/errors";
|
|
14
|
+
|
|
15
|
+
export const DEFAULT_API_BASE_URL = "https://api.digitalocean.com";
|
|
16
|
+
|
|
17
|
+
export interface Config {
|
|
18
|
+
readonly apiKey: Redacted.Redacted<string>;
|
|
19
|
+
readonly apiBaseUrl: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class Credentials extends Context.Service<
|
|
23
|
+
Credentials,
|
|
24
|
+
Effect.Effect<Config>
|
|
25
|
+
>()("DigitalOceanCredentials") {}
|
|
26
|
+
|
|
27
|
+
/** Layer from a plain API token + optional base URL. */
|
|
28
|
+
export const fromApiKey = (config: {
|
|
29
|
+
readonly apiKey: string;
|
|
30
|
+
readonly apiBaseUrl?: string;
|
|
31
|
+
}): Layer.Layer<Credentials> =>
|
|
32
|
+
Layer.succeed(
|
|
33
|
+
Credentials,
|
|
34
|
+
Effect.succeed({
|
|
35
|
+
apiKey: Redacted.make(config.apiKey),
|
|
36
|
+
apiBaseUrl: config.apiBaseUrl ?? DEFAULT_API_BASE_URL,
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Reads DIGITALOCEAN_ACCESS_TOKEN or DIGITALOCEAN_API_KEY (required) and
|
|
42
|
+
* DIGITALOCEAN_API_BASE_URL (optional). `doctl` uses DIGITALOCEAN_ACCESS_TOKEN.
|
|
43
|
+
*/
|
|
44
|
+
export const CredentialsFromEnv: Layer.Layer<Credentials> = Layer.succeed(
|
|
45
|
+
Credentials,
|
|
46
|
+
Effect.gen(function* () {
|
|
47
|
+
const apiKey =
|
|
48
|
+
process.env.DIGITALOCEAN_ACCESS_TOKEN ?? process.env.DIGITALOCEAN_API_KEY;
|
|
49
|
+
|
|
50
|
+
if (!apiKey) {
|
|
51
|
+
return yield* new ConfigError({
|
|
52
|
+
message:
|
|
53
|
+
"DIGITALOCEAN_ACCESS_TOKEN or DIGITALOCEAN_API_KEY environment variable is required",
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
apiKey: Redacted.make(apiKey),
|
|
59
|
+
apiBaseUrl: process.env.DIGITALOCEAN_API_BASE_URL ?? DEFAULT_API_BASE_URL,
|
|
60
|
+
};
|
|
61
|
+
}).pipe(Effect.orDie),
|
|
62
|
+
);
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean-specific error types.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the common HTTP errors from core and adds the DigitalOcean
|
|
5
|
+
* fallback errors. Note the generated service module additionally defines
|
|
6
|
+
* its own per-status matcher classes (BadRequest/NotFound/…) for the
|
|
7
|
+
* statuses each operation declares — those share `_tag`s with the core
|
|
8
|
+
* classes here, so `catchTag` works against either.
|
|
9
|
+
*/
|
|
10
|
+
export {
|
|
11
|
+
BadGateway,
|
|
12
|
+
BadRequest,
|
|
13
|
+
Conflict,
|
|
14
|
+
ConfigError,
|
|
15
|
+
Forbidden,
|
|
16
|
+
GatewayTimeout,
|
|
17
|
+
InternalServerError,
|
|
18
|
+
Locked,
|
|
19
|
+
NotFound,
|
|
20
|
+
ServiceUnavailable,
|
|
21
|
+
TooManyRequests,
|
|
22
|
+
Unauthorized,
|
|
23
|
+
UnprocessableEntity,
|
|
24
|
+
HTTP_STATUS_MAP,
|
|
25
|
+
DEFAULT_ERRORS,
|
|
26
|
+
API_ERRORS,
|
|
27
|
+
} from "@distilled.cloud/core/errors";
|
|
28
|
+
export type { DefaultErrors } from "@distilled.cloud/core/errors";
|
|
29
|
+
|
|
30
|
+
import * as Schema from "effect/Schema";
|
|
31
|
+
import * as Category from "@distilled.cloud/core/category";
|
|
32
|
+
|
|
33
|
+
/** Unknown DigitalOcean error — returned when nothing else matches the failure. */
|
|
34
|
+
export class UnknownDigitalOceanError extends Schema.TaggedError<UnknownDigitalOceanError>()(
|
|
35
|
+
"UnknownDigitalOceanError",
|
|
36
|
+
{
|
|
37
|
+
code: Schema.optional(Schema.String),
|
|
38
|
+
message: Schema.optional(Schema.String),
|
|
39
|
+
body: Schema.Unknown,
|
|
40
|
+
},
|
|
41
|
+
).pipe(Category.withServerError) {}
|
|
42
|
+
|
|
43
|
+
/** Schema parse error wrapper. */
|
|
44
|
+
export class DigitalOceanParseError extends Schema.TaggedError<DigitalOceanParseError>()(
|
|
45
|
+
"DigitalOceanParseError",
|
|
46
|
+
{
|
|
47
|
+
body: Schema.Unknown,
|
|
48
|
+
cause: Schema.Unknown,
|
|
49
|
+
},
|
|
50
|
+
).pipe(Category.withParseError) {}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @distilled.cloud/digitalocean — DigitalOcean SDK for Effect.
|
|
3
|
+
*
|
|
4
|
+
* `./services` is generated by `scripts/generate.ts` from the Smithy model
|
|
5
|
+
* in `.generated-specs` (written by `scripts/convert.ts` from the OpenAPI
|
|
6
|
+
* spec). Everything else in this folder is hand-written.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import * as DigitalOcean from "@distilled.cloud/digitalocean";
|
|
11
|
+
*
|
|
12
|
+
* const account = yield* DigitalOcean.Services.digitalocean.accountGet({});
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export * from "./credentials.ts";
|
|
16
|
+
export * from "./errors.ts";
|
|
17
|
+
export * as T from "./traits.ts";
|
|
18
|
+
export {
|
|
19
|
+
DigitalOceanProtocol,
|
|
20
|
+
type DigitalOceanOpError,
|
|
21
|
+
type DigitalOceanOpContext,
|
|
22
|
+
} from "./protocol.ts";
|
|
23
|
+
export { paginateCursor, paginatePageNumber } from "./pagination.ts";
|
|
24
|
+
export * as Retry from "./retry.ts";
|
|
25
|
+
export * as Services from "./services/index.ts";
|
|
26
|
+
// Operations (and their request/response types) are importable off the package
|
|
27
|
+
// root. The service-local typed error classes shadow the same-named shared
|
|
28
|
+
// ones from ./errors.ts — the ops actually raise the service-local classes.
|
|
29
|
+
//
|
|
30
|
+
// The API also models a `Credentials` schema (a database connection's
|
|
31
|
+
// user/password), which would collide with the `Credentials` service from
|
|
32
|
+
// ./credentials.ts. That one stays at the root; the schema is reachable as
|
|
33
|
+
// `Services.digitalocean.Credentials`.
|
|
34
|
+
export * from "./services/digitalocean.ts";
|
|
35
|
+
export { Credentials } from "./credentials.ts";
|
|
36
|
+
export {
|
|
37
|
+
BadRequest,
|
|
38
|
+
Conflict,
|
|
39
|
+
Forbidden,
|
|
40
|
+
NotFound,
|
|
41
|
+
UnprocessableEntity,
|
|
42
|
+
} from "./services/digitalocean.ts";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean pagination — hand-written.
|
|
3
|
+
*
|
|
4
|
+
* List operations that the converter stamps as paginated are either
|
|
5
|
+
* page-number mode (`page` in, `next_page` out) or cursor mode (`cursor`
|
|
6
|
+
* in, `pagination.cursor` out). Generated operations pass the matching
|
|
7
|
+
* core strategy to `API.makePaginated`.
|
|
8
|
+
*/
|
|
9
|
+
export {
|
|
10
|
+
paginateCursor,
|
|
11
|
+
paginatePageNumber,
|
|
12
|
+
} from "@distilled.cloud/core/pagination";
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOceanProtocol — hand-written.
|
|
3
|
+
*
|
|
4
|
+
* DigitalOcean speaks plain bearer-authenticated JSON REST (no success
|
|
5
|
+
* envelope), so the whole protocol is one `makeRestProtocol` call from
|
|
6
|
+
* `core/protocol-rest`:
|
|
7
|
+
*
|
|
8
|
+
* request: credentials → `Authorization: Bearer <apiKey>` + base URL
|
|
9
|
+
* (default https://api.digitalocean.com), resolved from the
|
|
10
|
+
* calling fiber on every request. Generated operations carry
|
|
11
|
+
* `/v2/...` routes.
|
|
12
|
+
*
|
|
13
|
+
* response: 2xx JSON is the payload (sensitive members delivered as
|
|
14
|
+
* `Redacted`); non-2xx `{ id, message, request_id? }` bodies
|
|
15
|
+
* map to the operation's typed error classes by status, then
|
|
16
|
+
* the shared HTTP-status classes, then
|
|
17
|
+
* {@link UnknownDigitalOceanError}.
|
|
18
|
+
*/
|
|
19
|
+
import * as Effect from "effect/Effect";
|
|
20
|
+
import type * as Layer from "effect/Layer";
|
|
21
|
+
import * as Redacted from "effect/Redacted";
|
|
22
|
+
import type * as HttpClient from "effect/unstable/http/HttpClient";
|
|
23
|
+
import type * as HttpClientError from "effect/unstable/http/HttpClientError";
|
|
24
|
+
import type * as API from "@distilled.cloud/core/api";
|
|
25
|
+
import {
|
|
26
|
+
makeRestProtocol,
|
|
27
|
+
type RestErrorEnvelope,
|
|
28
|
+
} from "@distilled.cloud/core/protocol-rest";
|
|
29
|
+
import type { API_ERRORS, ConfigError } from "@distilled.cloud/core/errors";
|
|
30
|
+
import { Credentials, type Config } from "./credentials.ts";
|
|
31
|
+
import { UnknownDigitalOceanError } from "./errors.ts";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Error channel shared by every generated DigitalOcean operation. Generated
|
|
35
|
+
* service files annotate operations with `API.OperationMethod<I, O,
|
|
36
|
+
* DigitalOceanOpError, DigitalOceanOpContext>` explicitly so the compiler
|
|
37
|
+
* never infers these back out of the schema generics.
|
|
38
|
+
*/
|
|
39
|
+
export type DigitalOceanOpError =
|
|
40
|
+
| InstanceType<(typeof API_ERRORS)[number]>
|
|
41
|
+
| UnknownDigitalOceanError
|
|
42
|
+
| ConfigError
|
|
43
|
+
| HttpClientError.HttpClientError;
|
|
44
|
+
|
|
45
|
+
/** Context (requirements) shared by every generated DigitalOcean operation. */
|
|
46
|
+
export type DigitalOceanOpContext = Credentials | HttpClient.HttpClient;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* DigitalOcean error bodies are `{ id, message, request_id? }` — `id` is the
|
|
50
|
+
* machine-readable half (`not_found`, `forbidden`, …). A response that
|
|
51
|
+
* arrives without that envelope falls through to the protocol's `HTTP
|
|
52
|
+
* <status>` default.
|
|
53
|
+
*/
|
|
54
|
+
const errorEnvelope = (body: unknown): RestErrorEnvelope | undefined => {
|
|
55
|
+
if (body === null || typeof body !== "object") return undefined;
|
|
56
|
+
const b = body as Record<string, unknown>;
|
|
57
|
+
const code =
|
|
58
|
+
typeof b.id === "string"
|
|
59
|
+
? b.id
|
|
60
|
+
: typeof b.code === "string"
|
|
61
|
+
? b.code
|
|
62
|
+
: undefined;
|
|
63
|
+
const message = typeof b.message === "string" ? b.message : undefined;
|
|
64
|
+
if (code === undefined && message === undefined) return undefined;
|
|
65
|
+
return { code, message };
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const DigitalOceanProtocol: Layer.Layer<API.Protocol> =
|
|
69
|
+
makeRestProtocol<Config>({
|
|
70
|
+
// The Credentials service holds an effect — resolving it here (per
|
|
71
|
+
// request, on the calling fiber) picks up context-provided credentials.
|
|
72
|
+
credentials: Effect.gen(function* () {
|
|
73
|
+
const resolve = yield* Credentials;
|
|
74
|
+
return yield* resolve;
|
|
75
|
+
}),
|
|
76
|
+
baseUrl: (creds) => creds.apiBaseUrl,
|
|
77
|
+
headers: (creds) => ({
|
|
78
|
+
Authorization: `Bearer ${Redacted.value(creds.apiKey)}`,
|
|
79
|
+
}),
|
|
80
|
+
errorEnvelope,
|
|
81
|
+
unknownError: ({ code, message, body }) =>
|
|
82
|
+
new UnknownDigitalOceanError({
|
|
83
|
+
code:
|
|
84
|
+
typeof code === "string"
|
|
85
|
+
? code
|
|
86
|
+
: code !== undefined
|
|
87
|
+
? String(code)
|
|
88
|
+
: undefined,
|
|
89
|
+
message,
|
|
90
|
+
body,
|
|
91
|
+
}),
|
|
92
|
+
});
|
package/src/retry.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DigitalOcean retry configuration.
|
|
3
|
+
*
|
|
4
|
+
* Defines the per-SDK `Retry` Context.Service tag that generated operations
|
|
5
|
+
* wire into `API.make`. Callers can install a blanket retry policy at the
|
|
6
|
+
* layer level and have every DigitalOcean API call below it pick it up:
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import * as DigitalOcean from "@distilled.cloud/digitalocean";
|
|
11
|
+
*
|
|
12
|
+
* myEffect.pipe(DigitalOcean.Retry.transient);
|
|
13
|
+
* Effect.provide(myEffect, Layer.succeed(DigitalOcean.Retry.Retry, customPolicy));
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import * as Context from "effect/Context";
|
|
17
|
+
import * as Effect from "effect/Effect";
|
|
18
|
+
import * as Layer from "effect/Layer";
|
|
19
|
+
import {
|
|
20
|
+
type Policy,
|
|
21
|
+
throttlingFactory,
|
|
22
|
+
transientFactory,
|
|
23
|
+
} from "@distilled.cloud/core/retry";
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
type Options,
|
|
27
|
+
type Factory,
|
|
28
|
+
type Policy,
|
|
29
|
+
makeDefault,
|
|
30
|
+
jittered,
|
|
31
|
+
capped,
|
|
32
|
+
throttlingOptions,
|
|
33
|
+
transientOptions,
|
|
34
|
+
throttlingFactory,
|
|
35
|
+
transientFactory,
|
|
36
|
+
} from "@distilled.cloud/core/retry";
|
|
37
|
+
|
|
38
|
+
/** Context tag for configuring retry behavior of DigitalOcean API calls. */
|
|
39
|
+
export class Retry extends Context.Service<Retry, Policy>()(
|
|
40
|
+
"DigitalOceanRetry",
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
/** Provides a custom retry policy to every DigitalOcean API call below it. */
|
|
44
|
+
export const policy = (optionsOrFactory: Policy) =>
|
|
45
|
+
Effect.provide(Layer.succeed(Retry, optionsOrFactory));
|
|
46
|
+
|
|
47
|
+
/** Disables all automatic retries. */
|
|
48
|
+
export const none = Effect.provide(
|
|
49
|
+
Layer.succeed(Retry, { while: () => false }),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
/** Apply the throttling retry policy (retries throttling errors indefinitely). */
|
|
53
|
+
export const throttling = policy(throttlingFactory);
|
|
54
|
+
|
|
55
|
+
/** Apply the transient retry policy (retries all transient errors indefinitely). */
|
|
56
|
+
export const transient = policy(transientFactory);
|