@distilled.cloud/core 0.21.2 → 0.21.3

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.
@@ -6,45 +6,66 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
- * import { SensitiveString } from "@distilled.cloud/core/sensitive";
9
+ * import { SensitiveString, SensitiveOutputString } from "@distilled.cloud/core/sensitive";
10
10
  *
11
- * const Password = Schema.Struct({ plain_text: SensitiveString });
11
+ * // Inputs: use Sensitive* accepts plain string OR Redacted (convenience).
12
+ * const CreateInput = Schema.Struct({ plain_text: SensitiveString });
13
+ * API.create({ plain_text: "my-secret" }); // ok
14
+ * API.create({ plain_text: Redacted.make("my-secret") }); // also ok
12
15
  *
13
- * // Users can pass raw strings (convenient):
14
- * API.createPassword({ plain_text: "my-secret" })
15
- *
16
- * // Or Redacted values (explicit):
17
- * API.createPassword({ plain_text: Redacted.make("my-secret") })
18
- *
19
- * // Response values are always Redacted (safe):
20
- * console.log(result.plain_text); // logs "<redacted>"
16
+ * // Outputs: use SensitiveOutput* decoded type is strictly Redacted, so
17
+ * // consumers never need to coerce.
18
+ * const CreateOutput = Schema.Struct({ plain_text: SensitiveOutputString });
19
+ * console.log(result.plain_text); // Redacted<string>, logs "<redacted>"
21
20
  * ```
22
21
  */
23
22
  import * as Redacted from "effect/Redacted";
24
23
  import * as S from "effect/Schema";
25
24
  /**
26
- * Sensitive - Marks data as sensitive, wrapping in Effect's Redacted type.
25
+ * Sensitive (input-friendly) - Marks data as sensitive, wrapping in Redacted.
27
26
  *
28
- * This schema provides a convenient way to handle sensitive data:
29
- * - TypeScript type: `A | Redacted.Redacted<A>` (accepts both for inputs)
30
- * - Decode (responses): Always wraps wire value in Redacted
31
- * - Encode (requests): Accepts BOTH raw values and Redacted, extracts the raw value
27
+ * Use for **request body** / **input** fields. The decoded TypeScript type is
28
+ * `A | Redacted<A>` so callers can pass either a plain value or a Redacted one.
29
+ * On the wire, plain values are sent through and Redacted values are unwrapped
30
+ * back to their underlying value. Decoded responses are always Redacted.
32
31
  *
33
- * The union type allows users to conveniently pass plain values for inputs while
34
- * still getting proper Redacted types for outputs. Response values will always
35
- * be Redacted, which prevents accidental logging.
32
+ * For **output** / **response** fields use {@link SensitiveOutput} instead
33
+ * its decoded type is strictly `Redacted<A>`, removing the need for callers to
34
+ * narrow / coerce on every read site.
36
35
  */
37
36
  export declare const Sensitive: <A>(schema: S.Schema<A>) => S.Schema<A | Redacted.Redacted<A>>;
38
37
  /**
39
- * Sensitive string - a string marked as sensitive.
40
- * Wire format is plain string, TypeScript type is string | Redacted<string>.
41
- * At runtime, decoded values are always Redacted<string>.
38
+ * Sensitive string (input-friendly).
39
+ * Wire format: plain string. Decoded TypeScript type: `string | Redacted<string>`.
40
+ * Decoded runtime values are always `Redacted<string>`.
41
+ *
42
+ * Prefer {@link SensitiveOutputString} on response schemas — its static type
43
+ * matches the runtime (strict `Redacted<string>`).
42
44
  */
43
45
  export declare const SensitiveString: S.Schema<string | Redacted.Redacted<string>>;
44
46
  /**
45
- * Sensitive nullable string - a nullable string marked as sensitive.
46
- * Wire format is plain string | null, TypeScript type is string | null | Redacted<string>.
47
- * At runtime, decoded non-null values are always Redacted<string>.
47
+ * Sensitive nullable string (input-friendly).
48
+ * Wire format: `string | null`. Decoded type: `string | null | Redacted<string>`.
49
+ * Prefer {@link SensitiveOutputNullableString} on response schemas.
48
50
  */
49
51
  export declare const SensitiveNullableString: S.NullOr<S.Schema<string | Redacted.Redacted<string>>>;
52
+ /**
53
+ * SensitiveOutput - strict variant of {@link Sensitive} for response fields.
54
+ *
55
+ * Decoded TypeScript type is `Redacted<A>` (no union). Wire format is the
56
+ * underlying `A`. Use on response schemas so consumers don't have to narrow
57
+ * `string | Redacted<string>` at every read site.
58
+ */
59
+ export declare const SensitiveOutput: <A>(schema: S.Schema<A>) => S.Schema<Redacted.Redacted<A>>;
60
+ /**
61
+ * SensitiveOutput string - strict variant of {@link SensitiveString} for
62
+ * response fields. Decoded type is `Redacted<string>`.
63
+ */
64
+ export declare const SensitiveOutputString: S.Schema<Redacted.Redacted<string>>;
65
+ /**
66
+ * SensitiveOutput nullable string - strict variant of
67
+ * {@link SensitiveNullableString} for response fields. Decoded type is
68
+ * `Redacted<string> | null`.
69
+ */
70
+ export declare const SensitiveOutputNullableString: S.NullOr<S.Schema<Redacted.Redacted<string>>>;
50
71
  //# sourceMappingURL=sensitive.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sensitive.d.ts","sourceRoot":"","sources":["../src/sensitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAC;AAGnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,UACjB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAClB,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAe9B,CAAC;AAEP;;;;GAIG;AACH,eAAO,MAAM,eAAe,8CAE1B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,wDAElC,CAAC"}
1
+ {"version":3,"file":"sensitive.d.ts","sourceRoot":"","sources":["../src/sensitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAC;AAGnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,UACjB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAClB,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAe9B,CAAC;AAEP;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,8CAE1B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,wDAElC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,UACvB,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAClB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAa1B,CAAC;AAEP;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qCAEhC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,+CAIxC,CAAC"}
package/lib/sensitive.js CHANGED
@@ -6,34 +6,33 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
- * import { SensitiveString } from "@distilled.cloud/core/sensitive";
9
+ * import { SensitiveString, SensitiveOutputString } from "@distilled.cloud/core/sensitive";
10
10
  *
11
- * const Password = Schema.Struct({ plain_text: SensitiveString });
11
+ * // Inputs: use Sensitive* accepts plain string OR Redacted (convenience).
12
+ * const CreateInput = Schema.Struct({ plain_text: SensitiveString });
13
+ * API.create({ plain_text: "my-secret" }); // ok
14
+ * API.create({ plain_text: Redacted.make("my-secret") }); // also ok
12
15
  *
13
- * // Users can pass raw strings (convenient):
14
- * API.createPassword({ plain_text: "my-secret" })
15
- *
16
- * // Or Redacted values (explicit):
17
- * API.createPassword({ plain_text: Redacted.make("my-secret") })
18
- *
19
- * // Response values are always Redacted (safe):
20
- * console.log(result.plain_text); // logs "<redacted>"
16
+ * // Outputs: use SensitiveOutput* decoded type is strictly Redacted, so
17
+ * // consumers never need to coerce.
18
+ * const CreateOutput = Schema.Struct({ plain_text: SensitiveOutputString });
19
+ * console.log(result.plain_text); // Redacted<string>, logs "<redacted>"
21
20
  * ```
22
21
  */
23
22
  import * as Redacted from "effect/Redacted";
24
23
  import * as S from "effect/Schema";
25
24
  import * as SchemaTransformation from "effect/SchemaTransformation";
26
25
  /**
27
- * Sensitive - Marks data as sensitive, wrapping in Effect's Redacted type.
26
+ * Sensitive (input-friendly) - Marks data as sensitive, wrapping in Redacted.
28
27
  *
29
- * This schema provides a convenient way to handle sensitive data:
30
- * - TypeScript type: `A | Redacted.Redacted<A>` (accepts both for inputs)
31
- * - Decode (responses): Always wraps wire value in Redacted
32
- * - Encode (requests): Accepts BOTH raw values and Redacted, extracts the raw value
28
+ * Use for **request body** / **input** fields. The decoded TypeScript type is
29
+ * `A | Redacted<A>` so callers can pass either a plain value or a Redacted one.
30
+ * On the wire, plain values are sent through and Redacted values are unwrapped
31
+ * back to their underlying value. Decoded responses are always Redacted.
33
32
  *
34
- * The union type allows users to conveniently pass plain values for inputs while
35
- * still getting proper Redacted types for outputs. Response values will always
36
- * be Redacted, which prevents accidental logging.
33
+ * For **output** / **response** fields use {@link SensitiveOutput} instead
34
+ * its decoded type is strictly `Redacted<A>`, removing the need for callers to
35
+ * narrow / coerce on every read site.
37
36
  */
38
37
  export const Sensitive = (schema) => schema
39
38
  .pipe(S.decodeTo(S.Union([S.toType(schema), S.Redacted(S.toType(schema))]), SchemaTransformation.transform({
@@ -46,19 +45,52 @@ export const Sensitive = (schema) => schema
46
45
  identifier: `Sensitive<${schema.ast.annotations?.identifier ?? "unknown"}>`,
47
46
  });
48
47
  /**
49
- * Sensitive string - a string marked as sensitive.
50
- * Wire format is plain string, TypeScript type is string | Redacted<string>.
51
- * At runtime, decoded values are always Redacted<string>.
48
+ * Sensitive string (input-friendly).
49
+ * Wire format: plain string. Decoded TypeScript type: `string | Redacted<string>`.
50
+ * Decoded runtime values are always `Redacted<string>`.
51
+ *
52
+ * Prefer {@link SensitiveOutputString} on response schemas — its static type
53
+ * matches the runtime (strict `Redacted<string>`).
52
54
  */
53
55
  export const SensitiveString = Sensitive(S.String).annotate({
54
56
  identifier: "SensitiveString",
55
57
  });
56
58
  /**
57
- * Sensitive nullable string - a nullable string marked as sensitive.
58
- * Wire format is plain string | null, TypeScript type is string | null | Redacted<string>.
59
- * At runtime, decoded non-null values are always Redacted<string>.
59
+ * Sensitive nullable string (input-friendly).
60
+ * Wire format: `string | null`. Decoded type: `string | null | Redacted<string>`.
61
+ * Prefer {@link SensitiveOutputNullableString} on response schemas.
60
62
  */
61
63
  export const SensitiveNullableString = S.NullOr(SensitiveString).annotate({
62
64
  identifier: "SensitiveNullableString",
63
65
  });
66
+ /**
67
+ * SensitiveOutput - strict variant of {@link Sensitive} for response fields.
68
+ *
69
+ * Decoded TypeScript type is `Redacted<A>` (no union). Wire format is the
70
+ * underlying `A`. Use on response schemas so consumers don't have to narrow
71
+ * `string | Redacted<string>` at every read site.
72
+ */
73
+ export const SensitiveOutput = (schema) => schema
74
+ .pipe(S.decodeTo(S.Redacted(S.toType(schema)), SchemaTransformation.transform({
75
+ decode: (a) => Redacted.make(a),
76
+ encode: (v) => Redacted.value(v),
77
+ })))
78
+ .annotate({
79
+ identifier: `SensitiveOutput<${schema.ast.annotations?.identifier ?? "unknown"}>`,
80
+ });
81
+ /**
82
+ * SensitiveOutput string - strict variant of {@link SensitiveString} for
83
+ * response fields. Decoded type is `Redacted<string>`.
84
+ */
85
+ export const SensitiveOutputString = SensitiveOutput(S.String).annotate({
86
+ identifier: "SensitiveOutputString",
87
+ });
88
+ /**
89
+ * SensitiveOutput nullable string - strict variant of
90
+ * {@link SensitiveNullableString} for response fields. Decoded type is
91
+ * `Redacted<string> | null`.
92
+ */
93
+ export const SensitiveOutputNullableString = S.NullOr(SensitiveOutputString).annotate({
94
+ identifier: "SensitiveOutputNullableString",
95
+ });
64
96
  //# sourceMappingURL=sensitive.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sensitive.js","sourceRoot":"","sources":["../src/sensitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAC;AACnC,OAAO,KAAK,oBAAoB,MAAM,6BAA6B,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,MAAmB,EACiB,EAAE,CACtC,MAAM;KACH,IAAI,CACH,CAAC,CAAC,QAAQ,CACR,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EACzD,oBAAoB,CAAC,SAAS,CAAC;IAC7B,iDAAiD;IACjD,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAQ;IACtC,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC,CACH,CACF;KACA,QAAQ,CAAC;IACR,UAAU,EAAE,aAAa,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,SAAS,GAAG;CAC5E,CAAC,CAAC;AAEP;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;IAC1D,UAAU,EAAE,iBAAiB;CAC9B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC;IACxE,UAAU,EAAE,yBAAyB;CACtC,CAAC,CAAC"}
1
+ {"version":3,"file":"sensitive.js","sourceRoot":"","sources":["../src/sensitive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,CAAC,MAAM,eAAe,CAAC;AACnC,OAAO,KAAK,oBAAoB,MAAM,6BAA6B,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,MAAmB,EACiB,EAAE,CACtC,MAAM;KACH,IAAI,CACH,CAAC,CAAC,QAAQ,CACR,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EACzD,oBAAoB,CAAC,SAAS,CAAC;IAC7B,iDAAiD;IACjD,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAQ;IACtC,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAChE,CAAC,CACH,CACF;KACA,QAAQ,CAAC;IACR,UAAU,EAAE,aAAa,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,SAAS,GAAG;CAC5E,CAAC,CAAC;AAEP;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;IAC1D,UAAU,EAAE,iBAAiB;CAC9B,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC;IACxE,UAAU,EAAE,yBAAyB;CACtC,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAC7B,MAAmB,EACa,EAAE,CAClC,MAAM;KACH,IAAI,CACH,CAAC,CAAC,QAAQ,CACR,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAC5B,oBAAoB,CAAC,SAAS,CAAC;IAC7B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;CACjC,CAAC,CACH,CACF;KACA,QAAQ,CAAC;IACR,UAAU,EAAE,mBAAmB,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,SAAS,GAAG;CAClF,CAAC,CAAC;AAEP;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC;IACtE,UAAU,EAAE,uBAAuB;CACpC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CACnD,qBAAqB,CACtB,CAAC,QAAQ,CAAC;IACT,UAAU,EAAE,+BAA+B;CAC5C,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@distilled.cloud/core",
3
- "version": "0.21.2",
3
+ "version": "0.21.3",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/alchemy-run/distilled",
package/src/sensitive.ts CHANGED
@@ -6,18 +6,17 @@
6
6
  *
7
7
  * @example
8
8
  * ```ts
9
- * import { SensitiveString } from "@distilled.cloud/core/sensitive";
9
+ * import { SensitiveString, SensitiveOutputString } from "@distilled.cloud/core/sensitive";
10
10
  *
11
- * const Password = Schema.Struct({ plain_text: SensitiveString });
11
+ * // Inputs: use Sensitive* accepts plain string OR Redacted (convenience).
12
+ * const CreateInput = Schema.Struct({ plain_text: SensitiveString });
13
+ * API.create({ plain_text: "my-secret" }); // ok
14
+ * API.create({ plain_text: Redacted.make("my-secret") }); // also ok
12
15
  *
13
- * // Users can pass raw strings (convenient):
14
- * API.createPassword({ plain_text: "my-secret" })
15
- *
16
- * // Or Redacted values (explicit):
17
- * API.createPassword({ plain_text: Redacted.make("my-secret") })
18
- *
19
- * // Response values are always Redacted (safe):
20
- * console.log(result.plain_text); // logs "<redacted>"
16
+ * // Outputs: use SensitiveOutput* decoded type is strictly Redacted, so
17
+ * // consumers never need to coerce.
18
+ * const CreateOutput = Schema.Struct({ plain_text: SensitiveOutputString });
19
+ * console.log(result.plain_text); // Redacted<string>, logs "<redacted>"
21
20
  * ```
22
21
  */
23
22
  import * as Redacted from "effect/Redacted";
@@ -25,16 +24,16 @@ import * as S from "effect/Schema";
25
24
  import * as SchemaTransformation from "effect/SchemaTransformation";
26
25
 
27
26
  /**
28
- * Sensitive - Marks data as sensitive, wrapping in Effect's Redacted type.
27
+ * Sensitive (input-friendly) - Marks data as sensitive, wrapping in Redacted.
29
28
  *
30
- * This schema provides a convenient way to handle sensitive data:
31
- * - TypeScript type: `A | Redacted.Redacted<A>` (accepts both for inputs)
32
- * - Decode (responses): Always wraps wire value in Redacted
33
- * - Encode (requests): Accepts BOTH raw values and Redacted, extracts the raw value
29
+ * Use for **request body** / **input** fields. The decoded TypeScript type is
30
+ * `A | Redacted<A>` so callers can pass either a plain value or a Redacted one.
31
+ * On the wire, plain values are sent through and Redacted values are unwrapped
32
+ * back to their underlying value. Decoded responses are always Redacted.
34
33
  *
35
- * The union type allows users to conveniently pass plain values for inputs while
36
- * still getting proper Redacted types for outputs. Response values will always
37
- * be Redacted, which prevents accidental logging.
34
+ * For **output** / **response** fields use {@link SensitiveOutput} instead
35
+ * its decoded type is strictly `Redacted<A>`, removing the need for callers to
36
+ * narrow / coerce on every read site.
38
37
  */
39
38
  export const Sensitive = <A>(
40
39
  schema: S.Schema<A>,
@@ -56,19 +55,65 @@ export const Sensitive = <A>(
56
55
  });
57
56
 
58
57
  /**
59
- * Sensitive string - a string marked as sensitive.
60
- * Wire format is plain string, TypeScript type is string | Redacted<string>.
61
- * At runtime, decoded values are always Redacted<string>.
58
+ * Sensitive string (input-friendly).
59
+ * Wire format: plain string. Decoded TypeScript type: `string | Redacted<string>`.
60
+ * Decoded runtime values are always `Redacted<string>`.
61
+ *
62
+ * Prefer {@link SensitiveOutputString} on response schemas — its static type
63
+ * matches the runtime (strict `Redacted<string>`).
62
64
  */
63
65
  export const SensitiveString = Sensitive(S.String).annotate({
64
66
  identifier: "SensitiveString",
65
67
  });
66
68
 
67
69
  /**
68
- * Sensitive nullable string - a nullable string marked as sensitive.
69
- * Wire format is plain string | null, TypeScript type is string | null | Redacted<string>.
70
- * At runtime, decoded non-null values are always Redacted<string>.
70
+ * Sensitive nullable string (input-friendly).
71
+ * Wire format: `string | null`. Decoded type: `string | null | Redacted<string>`.
72
+ * Prefer {@link SensitiveOutputNullableString} on response schemas.
71
73
  */
72
74
  export const SensitiveNullableString = S.NullOr(SensitiveString).annotate({
73
75
  identifier: "SensitiveNullableString",
74
76
  });
77
+
78
+ /**
79
+ * SensitiveOutput - strict variant of {@link Sensitive} for response fields.
80
+ *
81
+ * Decoded TypeScript type is `Redacted<A>` (no union). Wire format is the
82
+ * underlying `A`. Use on response schemas so consumers don't have to narrow
83
+ * `string | Redacted<string>` at every read site.
84
+ */
85
+ export const SensitiveOutput = <A>(
86
+ schema: S.Schema<A>,
87
+ ): S.Schema<Redacted.Redacted<A>> =>
88
+ schema
89
+ .pipe(
90
+ S.decodeTo(
91
+ S.Redacted(S.toType(schema)),
92
+ SchemaTransformation.transform({
93
+ decode: (a) => Redacted.make(a),
94
+ encode: (v) => Redacted.value(v),
95
+ }),
96
+ ),
97
+ )
98
+ .annotate({
99
+ identifier: `SensitiveOutput<${schema.ast.annotations?.identifier ?? "unknown"}>`,
100
+ });
101
+
102
+ /**
103
+ * SensitiveOutput string - strict variant of {@link SensitiveString} for
104
+ * response fields. Decoded type is `Redacted<string>`.
105
+ */
106
+ export const SensitiveOutputString = SensitiveOutput(S.String).annotate({
107
+ identifier: "SensitiveOutputString",
108
+ });
109
+
110
+ /**
111
+ * SensitiveOutput nullable string - strict variant of
112
+ * {@link SensitiveNullableString} for response fields. Decoded type is
113
+ * `Redacted<string> | null`.
114
+ */
115
+ export const SensitiveOutputNullableString = S.NullOr(
116
+ SensitiveOutputString,
117
+ ).annotate({
118
+ identifier: "SensitiveOutputNullableString",
119
+ });