@evolu/common 7.3.0 → 7.4.1

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 (45) hide show
  1. package/dist/src/Buffer.d.ts +2 -1
  2. package/dist/src/Buffer.d.ts.map +1 -1
  3. package/dist/src/Buffer.js +1 -0
  4. package/dist/src/Callbacks.d.ts +1 -1
  5. package/dist/src/Callbacks.d.ts.map +1 -1
  6. package/dist/src/Callbacks.js +1 -1
  7. package/dist/src/Console.d.ts +1 -9
  8. package/dist/src/Console.d.ts.map +1 -1
  9. package/dist/src/Console.js +1 -41
  10. package/dist/src/Instances.d.ts.map +1 -1
  11. package/dist/src/Redacted.d.ts +89 -0
  12. package/dist/src/Redacted.d.ts.map +1 -0
  13. package/dist/src/Redacted.js +48 -0
  14. package/dist/src/Type.d.ts +5 -9
  15. package/dist/src/Type.d.ts.map +1 -1
  16. package/dist/src/Type.js +0 -202
  17. package/dist/src/index.d.ts +1 -0
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +1 -0
  20. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  21. package/dist/src/local-first/Owner.d.ts +4 -0
  22. package/dist/src/local-first/Owner.d.ts.map +1 -1
  23. package/dist/src/local-first/Public.d.ts +6 -1
  24. package/dist/src/local-first/Public.d.ts.map +1 -1
  25. package/dist/src/local-first/Public.js +6 -1
  26. package/dist/src/local-first/Storage.d.ts.map +1 -1
  27. package/dist/src/local-first/Sync.d.ts.map +1 -1
  28. package/dist/src/local-first/Sync.js +10 -13
  29. package/dist/src/local-first/index.d.ts +9 -1
  30. package/dist/src/local-first/index.d.ts.map +1 -1
  31. package/dist/src/local-first/index.js +9 -1
  32. package/package.json +3 -4
  33. package/src/Buffer.ts +2 -1
  34. package/src/Callbacks.ts +1 -1
  35. package/src/Console.ts +1 -10
  36. package/src/Instances.ts +4 -2
  37. package/src/Redacted.ts +118 -0
  38. package/src/Type.ts +229 -221
  39. package/src/index.ts +1 -0
  40. package/src/local-first/Evolu.ts +3 -2
  41. package/src/local-first/Owner.ts +4 -0
  42. package/src/local-first/Public.ts +6 -1
  43. package/src/local-first/Storage.ts +10 -12
  44. package/src/local-first/Sync.ts +10 -21
  45. package/src/local-first/index.ts +9 -1
@@ -0,0 +1,118 @@
1
+ import { assert } from "./Assert.js";
2
+ import type { Brand } from "./Brand.js";
3
+ import type { Eq } from "./Eq.js";
4
+
5
+ /**
6
+ * A wrapper type that prevents sensitive values from being accidentally exposed
7
+ * through logging, serialization, or inspection.
8
+ *
9
+ * The wrapped value is hidden and can only be accessed explicitly via
10
+ * {@link revealRedacted}. All standard methods (`toString`, `toJSON`, and
11
+ * Node.js inspect) return `<redacted>`.
12
+ *
13
+ * For type-level distinction between different secrets, use branded types.
14
+ *
15
+ * The actual value lives in a `WeakMap`, so it never appears as a property and
16
+ * is automatically garbage collected when the wrapper is dropped. This is
17
+ * better than a class with a private field because private fields are still
18
+ * visible in devtools. Symbols can't be used because they don't support custom
19
+ * `toString`.
20
+ *
21
+ * Implements `Disposable` for automatic cleanup via the `using` syntax.
22
+ *
23
+ * ### Example
24
+ *
25
+ * ```ts
26
+ * // Define branded types for your secrets
27
+ * type ApiKey = string & Brand<"ApiKey">;
28
+ * type DbPassword = string & Brand<"DbPassword">;
29
+ *
30
+ * // Wrap them with Redacted for safe passing
31
+ * type RedactedApiKey = Redacted<ApiKey>;
32
+ * type RedactedDbPassword = Redacted<DbPassword>;
33
+ *
34
+ * // Create a redacted secret
35
+ * const apiKey: ApiKey = "secret-123" as ApiKey;
36
+ * const redactedKey: RedactedApiKey = createRedacted(apiKey);
37
+ *
38
+ * console.log(redactedKey); // <redacted>
39
+ * console.log(revealRedacted(redactedKey)); // secret-123
40
+ *
41
+ * // Type safety: RedactedApiKey ≠ RedactedDbPassword
42
+ * const fetchUser = (key: RedactedApiKey) => {
43
+ * const value: ApiKey = revealRedacted(key);
44
+ * // use value...
45
+ * };
46
+ *
47
+ * fetchUser(redactedKey); // ✅
48
+ * // fetchUser(createRedacted("x" as DbPassword)); // ❌ type error
49
+ *
50
+ * // Automatic cleanup with `using`
51
+ * {
52
+ * using secret = createRedacted("sensitive" as ApiKey);
53
+ * // ... use secret ...
54
+ * } // automatically wiped from memory
55
+ * ```
56
+ *
57
+ * @experimental
58
+ */
59
+ export interface Redacted<A> extends Brand<"Redacted">, Disposable {
60
+ /** The inner type. Useful for inference via `typeof redacted.Type`. */
61
+ readonly Type: A;
62
+ }
63
+
64
+ /** Creates a {@link Redacted} wrapper for a sensitive value. */
65
+ export const createRedacted = <A>(value: A): Redacted<A> => {
66
+ const redacted = Object.create(proto) as Redacted<A>;
67
+ registry.set(redacted, value);
68
+ return redacted;
69
+ };
70
+
71
+ const proto = {
72
+ toString: () => redactedString,
73
+ toJSON: () => redactedString,
74
+ [Symbol.for("nodejs.util.inspect.custom")]: () => redactedString,
75
+ [Symbol.dispose](this: Redacted<unknown>) {
76
+ registry.delete(this);
77
+ },
78
+ };
79
+ const redactedString = "<redacted>";
80
+ const registry = new WeakMap<Redacted<unknown>, unknown>();
81
+
82
+ /**
83
+ * Reveals the original value from a {@link Redacted} wrapper.
84
+ *
85
+ * This is a separate function rather than a method on {@link Redacted} to make
86
+ * access visually explicit and easy to grep in code reviews. Accessing
87
+ * sensitive values should feel intentional, not convenient.
88
+ */
89
+ export const revealRedacted = <A>(redacted: Redacted<A>): A => {
90
+ assert(registry.has(redacted), "Redacted value was not in registry");
91
+ return registry.get(redacted) as A;
92
+ };
93
+
94
+ /** Checks if a value is a {@link Redacted} wrapper. */
95
+ export const isRedacted = (value: unknown): value is Redacted<unknown> =>
96
+ typeof value === "object" &&
97
+ value !== null &&
98
+ Object.getPrototypeOf(value) === proto;
99
+
100
+ /**
101
+ * Creates an {@link Eq} for {@link Redacted} values based on an equality function
102
+ * for the underlying type.
103
+ *
104
+ * ### Example
105
+ *
106
+ * ```ts
107
+ * type ApiKey = string & Brand<"ApiKey">;
108
+ * const eqRedactedApiKey = createEqRedacted<ApiKey>(eqString);
109
+ *
110
+ * const a = createRedacted("x" as ApiKey);
111
+ * const b = createRedacted("x" as ApiKey);
112
+ * eqRedactedApiKey(a, b); // true
113
+ * ```
114
+ */
115
+ export const createEqRedacted =
116
+ <A>(eq: Eq<A>): Eq<Redacted<A>> =>
117
+ (x, y) =>
118
+ eq(revealRedacted(x), revealRedacted(y));