@fluid-internal/client-utils 2.118.0 → 3.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 (120) hide show
  1. package/CHANGELOG.md +1 -5
  2. package/README.md +13 -15
  3. package/dist/base64EncodingBrowser.d.ts +2 -1
  4. package/dist/base64EncodingBrowser.d.ts.map +1 -1
  5. package/dist/base64EncodingBrowser.js +1 -1
  6. package/dist/base64EncodingBrowser.js.map +1 -1
  7. package/dist/base64EncodingNode.d.ts +2 -1
  8. package/dist/base64EncodingNode.d.ts.map +1 -1
  9. package/dist/base64EncodingNode.js.map +1 -1
  10. package/dist/buffer.d.ts +93 -0
  11. package/dist/buffer.d.ts.map +1 -0
  12. package/dist/buffer.js +7 -0
  13. package/dist/buffer.js.map +1 -0
  14. package/dist/bufferBrowser.d.ts +18 -49
  15. package/dist/bufferBrowser.d.ts.map +1 -1
  16. package/dist/bufferBrowser.js +49 -52
  17. package/dist/bufferBrowser.js.map +1 -1
  18. package/dist/bufferNode.d.ts +67 -19
  19. package/dist/bufferNode.d.ts.map +1 -1
  20. package/dist/bufferNode.js +10 -12
  21. package/dist/bufferNode.js.map +1 -1
  22. package/dist/bufferShared.d.ts +38 -3
  23. package/dist/bufferShared.d.ts.map +1 -1
  24. package/dist/bufferShared.js +67 -5
  25. package/dist/bufferShared.js.map +1 -1
  26. package/dist/events/emitter.d.ts.map +1 -1
  27. package/dist/events/emitter.js +4 -3
  28. package/dist/events/emitter.js.map +1 -1
  29. package/dist/hashFileBrowser.d.ts +1 -1
  30. package/dist/hashFileBrowser.d.ts.map +1 -1
  31. package/dist/hashFileBrowser.js +23 -11
  32. package/dist/hashFileBrowser.js.map +1 -1
  33. package/dist/hashFileNode.d.ts +10 -2
  34. package/dist/hashFileNode.d.ts.map +1 -1
  35. package/dist/hashFileNode.js +10 -3
  36. package/dist/hashFileNode.js.map +1 -1
  37. package/dist/indexBrowser.d.ts +5 -3
  38. package/dist/indexBrowser.d.ts.map +1 -1
  39. package/dist/indexBrowser.js +5 -5
  40. package/dist/indexBrowser.js.map +1 -1
  41. package/dist/indexNode.d.ts +4 -3
  42. package/dist/indexNode.d.ts.map +1 -1
  43. package/dist/indexNode.js +5 -2
  44. package/dist/indexNode.js.map +1 -1
  45. package/dist/layerCompat.d.ts +37 -13
  46. package/dist/layerCompat.d.ts.map +1 -1
  47. package/dist/layerCompat.js +19 -14
  48. package/dist/layerCompat.js.map +1 -1
  49. package/dist/trace.js +2 -0
  50. package/dist/trace.js.map +1 -1
  51. package/dist/typedEventEmitter.js +7 -0
  52. package/dist/typedEventEmitter.js.map +1 -1
  53. package/eslint.config.mts +2 -2
  54. package/lib/base64EncodingBrowser.d.ts +2 -1
  55. package/lib/base64EncodingBrowser.d.ts.map +1 -1
  56. package/lib/base64EncodingBrowser.js +1 -1
  57. package/lib/base64EncodingBrowser.js.map +1 -1
  58. package/lib/base64EncodingNode.d.ts +2 -1
  59. package/lib/base64EncodingNode.d.ts.map +1 -1
  60. package/lib/base64EncodingNode.js.map +1 -1
  61. package/lib/buffer.d.ts +93 -0
  62. package/lib/buffer.d.ts.map +1 -0
  63. package/lib/buffer.js +6 -0
  64. package/lib/buffer.js.map +1 -0
  65. package/lib/bufferBrowser.d.ts +18 -49
  66. package/lib/bufferBrowser.d.ts.map +1 -1
  67. package/lib/bufferBrowser.js +29 -40
  68. package/lib/bufferBrowser.js.map +1 -1
  69. package/lib/bufferNode.d.ts +67 -19
  70. package/lib/bufferNode.d.ts.map +1 -1
  71. package/lib/bufferNode.js +7 -9
  72. package/lib/bufferNode.js.map +1 -1
  73. package/lib/bufferShared.d.ts +38 -3
  74. package/lib/bufferShared.d.ts.map +1 -1
  75. package/lib/bufferShared.js +64 -3
  76. package/lib/bufferShared.js.map +1 -1
  77. package/lib/events/emitter.d.ts.map +1 -1
  78. package/lib/events/emitter.js +2 -1
  79. package/lib/events/emitter.js.map +1 -1
  80. package/lib/hashFileBrowser.d.ts +1 -1
  81. package/lib/hashFileBrowser.d.ts.map +1 -1
  82. package/lib/hashFileBrowser.js +4 -1
  83. package/lib/hashFileBrowser.js.map +1 -1
  84. package/lib/hashFileNode.d.ts +10 -2
  85. package/lib/hashFileNode.d.ts.map +1 -1
  86. package/lib/hashFileNode.js +8 -0
  87. package/lib/hashFileNode.js.map +1 -1
  88. package/lib/indexBrowser.d.ts +5 -3
  89. package/lib/indexBrowser.d.ts.map +1 -1
  90. package/lib/indexBrowser.js +3 -5
  91. package/lib/indexBrowser.js.map +1 -1
  92. package/lib/indexNode.d.ts +4 -3
  93. package/lib/indexNode.d.ts.map +1 -1
  94. package/lib/indexNode.js +2 -2
  95. package/lib/indexNode.js.map +1 -1
  96. package/lib/layerCompat.d.ts +37 -13
  97. package/lib/layerCompat.d.ts.map +1 -1
  98. package/lib/layerCompat.js +17 -12
  99. package/lib/layerCompat.js.map +1 -1
  100. package/lib/trace.js +2 -0
  101. package/lib/trace.js.map +1 -1
  102. package/lib/typedEventEmitter.js +7 -0
  103. package/lib/typedEventEmitter.js.map +1 -1
  104. package/package.json +65 -62
  105. package/playwright.config.ts +23 -0
  106. package/src/base64EncodingBrowser.ts +3 -2
  107. package/src/base64EncodingNode.ts +2 -1
  108. package/src/buffer.ts +112 -0
  109. package/src/bufferBrowser.ts +98 -60
  110. package/src/bufferNode.ts +86 -30
  111. package/src/bufferShared.ts +74 -4
  112. package/src/hashFileBrowser.ts +6 -3
  113. package/src/hashFileNode.ts +10 -2
  114. package/src/indexBrowser.ts +13 -2
  115. package/src/indexNode.ts +13 -2
  116. package/src/layerCompat.ts +41 -13
  117. package/tsconfig.json +1 -2
  118. package/webpack.playwright.cjs +22 -0
  119. package/jest-puppeteer.config.cjs +0 -16
  120. package/jest.config.cjs +0 -21
package/src/buffer.ts ADDED
@@ -0,0 +1,112 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /**
7
+ * Declare the subset of node:Buffer functionality we want to make available instead of
8
+ * exposing the entirely of Node's typings. These serve as the public interface
9
+ * of the browser and node implementation.
10
+ */
11
+
12
+ /**
13
+ * Set of supported encodings for string to buffer conversion.
14
+ *
15
+ * @internal
16
+ */
17
+ export type IsoBufferEncoding =
18
+ | "utf8"
19
+ // eslint-disable-next-line unicorn/text-encoding-identifier-case -- "utf-8" value is supported, just discouraged
20
+ | "utf-8"
21
+ | "base64";
22
+
23
+ /**
24
+ * Minimal Buffer interface for our usages in the browser or Node environments.
25
+ *
26
+ * @internal
27
+ */
28
+ export interface IsoBuffer<TArrayBuffer extends ArrayBufferLike>
29
+ extends Uint8Array<TArrayBuffer> {
30
+ /**
31
+ * Convert the buffer to a string.
32
+ * Only supports encoding the whole string (unlike the Node `Buffer` equivalent)
33
+ * and only utf8 and base64 encodings.
34
+ *
35
+ * @param encoding - The encoding to use.
36
+ */
37
+ toString(encoding?: IsoBufferEncoding): string;
38
+ }
39
+
40
+ /**
41
+ * Constructor for {@link (IsoBuffer:type)}.
42
+ *
43
+ * @internal
44
+ */
45
+ export interface IsoBufferConstructor {
46
+ readonly prototype: IsoBuffer<ArrayBufferLike>;
47
+
48
+ new (length: number): IsoBuffer<ArrayBuffer>;
49
+ new (array: ArrayLike<number>): IsoBuffer<ArrayBuffer>;
50
+ new <TArrayBuffer extends ArrayBufferLike = ArrayBuffer>(
51
+ buffer: TArrayBuffer,
52
+ byteOffset?: number,
53
+ length?: number,
54
+ ): IsoBuffer<TArrayBuffer>;
55
+
56
+ /**
57
+ * Creates a new `IsoBuffer` containing `string`. The `encoding` parameter identifies
58
+ * the character encoding to be used when converting `string` into bytes.
59
+ *
60
+ * @param string - A string to encode.
61
+ * @param encoding - The encoding of `string`. **Default:** `'utf8'`.
62
+ *
63
+ * @privateRemarks
64
+ * Node buffer.buffer.d.ts declaration:
65
+ * `from(string: WithImplicitCoercion<string>, encoding?: IsoBufferEncoding): Buffer<ArrayBuffer>;`
66
+ */
67
+ from(string: string, encoding?: IsoBufferEncoding): IsoBuffer<ArrayBuffer>;
68
+ /**
69
+ * Allocates a new `IsoBuffer` using an `array` of bytes in the range `0` – `255`.
70
+ * Array entries outside that range will be truncated to fit into it.
71
+ *
72
+ * @param value - `Uint8Array`.
73
+ *
74
+ * @remarks
75
+ * This is limited to `Uint8Array` input to match Node implementation that
76
+ * takes `ArrayLike<number>` and returns `IsoBuffer<ArrayBuffer>`.
77
+ *
78
+ * @privateRemarks
79
+ * Node buffer.buffer.d.ts declaration:
80
+ * `from(array: WithImplicitCoercion<ArrayLike<number>>): IsoBuffer<ArrayBuffer>;`
81
+ */
82
+ from(array: Uint8Array): IsoBuffer<ArrayBuffer>;
83
+ /**
84
+ * This creates a view of the `ArrayBuffer` without copying the underlying
85
+ * memory. For example, when passed a reference to the `.buffer` property of a
86
+ * `TypedArray` instance, the newly created `Buffer` will share the same
87
+ * allocated memory as the `TypedArray`'s underlying `ArrayBuffer`.
88
+ *
89
+ * @param arrayBuffer - An `ArrayBuffer`, `SharedArrayBuffer`, for example the
90
+ * `.buffer` property of a `TypedArray`.
91
+ * @param byteOffset - Index of first byte to expose. **Default:** `0`.
92
+ * @param length - Number of bytes to expose. **Default:**
93
+ * `arrayBuffer.byteLength - byteOffset`.
94
+ *
95
+ * @privateRemarks
96
+ * Node buffer.buffer.d.ts declaration:
97
+ * ```
98
+ * public static from<TArrayBuffer extends WithImplicitCoercion<ArrayBufferLike>>(
99
+ * arrayBuffer: TArrayBuffer,
100
+ * byteOffset?: number,
101
+ * length?: number,
102
+ * ): Buffer<ImplicitArrayBuffer<TArrayBuffer>>;
103
+ * ```
104
+ */
105
+ from<TArrayBuffer extends ArrayBufferLike>(
106
+ arrayBuffer: TArrayBuffer,
107
+ byteOffset?: number,
108
+ length?: number,
109
+ ): IsoBuffer<TArrayBuffer>;
110
+
111
+ isBuffer(obj: unknown): obj is IsoBuffer<ArrayBufferLike>;
112
+ }
@@ -5,9 +5,30 @@
5
5
 
6
6
  import * as base64js from "base64-js";
7
7
 
8
+ import type {
9
+ IsoBufferConstructor,
10
+ IsoBufferEncoding,
11
+ IsoBuffer as IsoBufferInterface,
12
+ } from "./buffer.js";
13
+
14
+ export type {
15
+ IsoBufferConstructor,
16
+ IsoBufferEncoding,
17
+ } from "./buffer.js";
8
18
  /**
9
- * Converts a Uint8Array to a string of the provided encoding
10
- * Useful when the array might be an {@link IsoBuffer}.
19
+ * Minimal `Buffer` interface for our usages in the browser or Node.js environments.
20
+ *
21
+ * @internal
22
+ *
23
+ * @privateRemarks
24
+ * This type is exported specifically from here to respect combo type+value of same name rules.
25
+ */
26
+ export type IsoBuffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> =
27
+ IsoBufferInterface<TArrayBuffer>;
28
+
29
+ /**
30
+ * Converts a `Uint8Array` to a string of the provided encoding
31
+ * Useful when the array might be an {@link (IsoBuffer:type)}.
11
32
  *
12
33
  * @param arr - The array to convert.
13
34
  * @param encoding - Optional target encoding; only "utf8" and "base64" are
@@ -16,11 +37,7 @@ import * as base64js from "base64-js";
16
37
  *
17
38
  * @internal
18
39
  */
19
- export function Uint8ArrayToString(
20
- arr: Uint8Array,
21
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
22
- encoding?: "utf8" | "utf-8" | "base64",
23
- ): string {
40
+ export function Uint8ArrayToString(arr: Uint8Array, encoding?: IsoBufferEncoding): string {
24
41
  switch (encoding) {
25
42
  case "base64": {
26
43
  return base64js.fromByteArray(arr);
@@ -45,7 +62,7 @@ export function Uint8ArrayToString(
45
62
  *
46
63
  * @internal
47
64
  */
48
- export const stringToBuffer = (input: string, encoding: string): ArrayBuffer =>
65
+ export const stringToBuffer = (input: string, encoding: IsoBufferEncoding): ArrayBuffer =>
49
66
  IsoBuffer.from(input, encoding).buffer;
50
67
 
51
68
  /**
@@ -58,10 +75,10 @@ export const stringToBuffer = (input: string, encoding: string): ArrayBuffer =>
58
75
  * @internal
59
76
  */
60
77
  export const bufferToString = (
61
- blob: ArrayBufferLike,
62
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
63
- encoding: "utf8" | "utf-8" | "base64",
64
- ): string => IsoBuffer.from(blob).toString(encoding);
78
+ blob: ArrayBufferLike | Uint8Array,
79
+ encoding: IsoBufferEncoding,
80
+ ): string =>
81
+ Uint8ArrayToString(blob instanceof Uint8Array ? blob : IsoBuffer.from(blob), encoding);
65
82
 
66
83
  /**
67
84
  * Determines if an object is an array buffer.
@@ -73,12 +90,16 @@ export const bufferToString = (
73
90
  * new TypedArray(buffer, byteOffset, length), but passing TypedArray will result in fist path (and
74
91
  * ignoring byteOffice, length).
75
92
  *
76
- * @param obj - The object to determine if it is an ArrayBuffer.
93
+ * @param obj - The object to determine if it is `ArrayBufferLike`.
77
94
  *
78
- * @internal
95
+ * @privateRemarks
96
+ * This function preserves runtime functionality as it has been in the past.
97
+ * Typing has been adjusted to reflect the only use that remains and (assuming
98
+ * is called as expected) will always return true as the input is already
99
+ * expected to be `ArrayBufferLike`.
79
100
  */
80
- export function isArrayBuffer(obj: unknown): obj is ArrayBuffer {
81
- const maybe = obj as (Partial<ArrayBuffer> & Partial<Uint8Array>) | undefined;
101
+ function isArrayBufferLike(obj: ArrayBufferLike): obj is ArrayBufferLike {
102
+ const maybe = obj as (Partial<ArrayBufferLike> & Partial<Uint8Array>) | undefined;
82
103
  return (
83
104
  obj instanceof ArrayBuffer ||
84
105
  (typeof maybe === "object" &&
@@ -91,59 +112,71 @@ export function isArrayBuffer(obj: unknown): obj is ArrayBuffer {
91
112
  }
92
113
 
93
114
  /**
94
- * Minimal implementation of Buffer for our usages in the browser environment.
115
+ * Minimal implementation of `Buffer` for our usages in the browser environment.
95
116
  *
96
117
  * @internal
97
118
  */
98
- export class IsoBuffer extends Uint8Array {
99
- /**
100
- * Convert the buffer to a string.
101
- * Only supports encoding the whole string (unlike the Node Buffer equivalent)
102
- * and only utf8 and base64 encodings.
103
- *
104
- * @param encoding - The encoding to use.
105
- */
106
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
107
- public toString(encoding?: "utf8" | "utf-8" | "base64"): string {
119
+ // eslint-disable-next-line @typescript-eslint/no-shadow -- Preserve the public constructor name at runtime.
120
+ export const IsoBuffer: IsoBufferConstructor = class IsoBuffer<
121
+ TArrayBuffer extends ArrayBufferLike = ArrayBufferLike,
122
+ >
123
+ extends Uint8Array<TArrayBuffer>
124
+ implements IsoBufferInterface<TArrayBuffer>
125
+ {
126
+ public constructor(
127
+ value: number | ArrayLike<number> | TArrayBuffer,
128
+ byteOffset?: number,
129
+ length?: number,
130
+ ) {
131
+ super(value as TArrayBuffer, byteOffset, length);
132
+ }
133
+
134
+ public toString(encoding?: IsoBufferEncoding): string {
108
135
  return Uint8ArrayToString(this, encoding);
109
136
  }
110
137
 
111
- /**
112
- * Static constructor
113
- * @param value - (string | ArrayBuffer)
114
- * @param encodingOrOffset - (string | number)
115
- * @param length - (number)
116
- *
117
- * @privateRemarks TODO: Use actual types
118
- */
119
- // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
120
- public static from(value: any, encodingOrOffset?: any, length?: any): IsoBuffer {
138
+ public static from(
139
+ value: string,
140
+ encoding?: IsoBufferEncoding,
141
+ ): IsoBufferInterface<ArrayBuffer>;
142
+ public static from(value: Uint8Array): IsoBufferInterface<ArrayBuffer>;
143
+ public static from<TFArrayBuffer extends ArrayBufferLike>(
144
+ value: TFArrayBuffer,
145
+ byteOffset?: number,
146
+ length?: number,
147
+ ): IsoBufferInterface<TFArrayBuffer>;
148
+ public static from<TFArrayBuffer extends ArrayBufferLike>(
149
+ value: string | Uint8Array | TFArrayBuffer,
150
+ encodingOrOffset?: IsoBufferEncoding | number,
151
+ length?: number,
152
+ ): IsoBufferInterface<ArrayBufferLike> {
121
153
  if (typeof value === "string") {
122
- return IsoBuffer.fromString(value, encodingOrOffset as string | undefined);
154
+ return IsoBuffer.fromString(
155
+ value,
156
+ encodingOrOffset as IsoBufferEncoding | undefined,
157
+ ) satisfies IsoBufferInterface<ArrayBuffer>;
123
158
  // Capture any typed arrays, including Uint8Array (and thus - IsoBuffer!)
124
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
125
- } else if (value !== null && typeof value === "object" && isArrayBuffer(value.buffer)) {
159
+ } else if (value instanceof Uint8Array) {
126
160
  // The version of the from function for the node buffer, which takes a buffer or typed array
127
161
  // as first parameter, does not have any offset or length parameters. Those are just silently
128
162
  // ignored and not taken into account
129
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
130
- return IsoBuffer.fromArrayBuffer(value.buffer, value.byteOffset, value.byteLength);
131
- } else if (isArrayBuffer(value)) {
132
- return IsoBuffer.fromArrayBuffer(
133
- value,
134
- encodingOrOffset as number | undefined,
135
- length as number,
136
- );
163
+ const copy = new Uint8Array(value.byteLength);
164
+ copy.set(value);
165
+ return new IsoBuffer<ArrayBuffer>(copy.buffer) satisfies IsoBufferInterface<ArrayBuffer>;
166
+ } else if (isArrayBufferLike(value)) {
167
+ return IsoBuffer.fromArrayBuffer(value, encodingOrOffset as number | undefined, length);
137
168
  } else {
138
- throw new TypeError("Input value was neither a string nor an ArrayBuffer.");
169
+ throw new TypeError(
170
+ "Input value was neither a string, a Uint8Array, nor an ArrayBuffer.",
171
+ );
139
172
  }
140
173
  }
141
174
 
142
- public static fromArrayBuffer(
143
- arrayBuffer: ArrayBuffer,
175
+ public static fromArrayBuffer<TFArrayBuffer extends ArrayBufferLike>(
176
+ arrayBuffer: TFArrayBuffer,
144
177
  byteOffset?: number,
145
178
  byteLength?: number,
146
- ): IsoBuffer {
179
+ ): IsoBufferInterface<TFArrayBuffer> {
147
180
  const offset = byteOffset ?? 0;
148
181
  const validLength = byteLength ?? arrayBuffer.byteLength - offset;
149
182
  if (
@@ -155,22 +188,27 @@ export class IsoBuffer extends Uint8Array {
155
188
  throw new RangeError("Invalid range specified.");
156
189
  }
157
190
 
158
- return new IsoBuffer(arrayBuffer, offset, validLength);
191
+ return new IsoBuffer<TFArrayBuffer>(arrayBuffer, offset, validLength);
159
192
  }
160
193
 
161
- public static fromString(str: string, encoding?: string): IsoBuffer {
194
+ public static fromString(
195
+ str: string,
196
+ encoding?: IsoBufferEncoding,
197
+ ): IsoBufferInterface<ArrayBuffer> {
162
198
  switch (encoding) {
163
199
  case "base64": {
164
200
  const sanitizedString = this.sanitizeBase64(str);
165
- const encoded = base64js.toByteArray(sanitizedString);
166
- return new IsoBuffer(encoded.buffer);
201
+ // base64-js will always return a Uint8Array<ArrayBuffer>, but typing has not
202
+ // been updated to reflect that yet. Cast to the correct type here.
203
+ const encoded = base64js.toByteArray(sanitizedString) as Uint8Array<ArrayBuffer>;
204
+ return new IsoBuffer(encoded.buffer, encoded.byteOffset, encoded.byteLength);
167
205
  }
168
206
  case "utf8":
169
207
  // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
170
208
  case "utf-8":
171
209
  case undefined: {
172
210
  const encoded = new TextEncoder().encode(str);
173
- return new IsoBuffer(encoded.buffer);
211
+ return new IsoBuffer(encoded.buffer, encoded.byteOffset, encoded.byteLength);
174
212
  }
175
213
  default: {
176
214
  throw new Error("invalid/unsupported encoding");
@@ -178,8 +216,8 @@ export class IsoBuffer extends Uint8Array {
178
216
  }
179
217
  }
180
218
 
181
- public static isBuffer(obj: unknown): boolean {
182
- throw new Error("unimplemented");
219
+ public static isBuffer(obj: unknown): obj is IsoBuffer {
220
+ return obj instanceof IsoBuffer;
183
221
  }
184
222
 
185
223
  /**
@@ -205,4 +243,4 @@ export class IsoBuffer extends Uint8Array {
205
243
  }
206
244
  return sanitizedStr;
207
245
  }
208
- }
246
+ } satisfies IsoBufferConstructor;
package/src/bufferNode.ts CHANGED
@@ -3,43 +3,103 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
 
6
+ import type {
7
+ IsoBufferConstructor,
8
+ IsoBufferEncoding,
9
+ IsoBuffer as IsoBufferInterface,
10
+ } from "./buffer.js";
11
+
12
+ export type { IsoBufferConstructor, IsoBufferEncoding } from "./buffer.js";
13
+ /**
14
+ * Minimal `Buffer` interface for our usages in the browser or Node.js environments.
15
+ *
16
+ * @internal
17
+ *
18
+ * @privateRemarks
19
+ * This type is exported specifically from here to respect combo type+value of same name rules.
20
+ */
21
+ export type IsoBuffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike> =
22
+ IsoBufferInterface<TArrayBuffer>;
23
+
6
24
  /**
7
25
  * Declare the subset of Buffer functionality we want to make available instead of
8
26
  * exposing the entirely of Node's typings. This should match the public interface
9
- * of the browser implementation, so any changes made in one should be made in both.
27
+ * of the browser implementation -- see {@link (IsoBuffer:type)} and {@link IsoBufferConstructor}.
10
28
  *
11
29
  * @internal
12
30
  */
13
- export declare class Buffer extends Uint8Array {
14
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
15
- public toString(encoding?: "utf8" | "utf-8" | "base64"): string;
16
-
31
+ export declare class Buffer<TArrayBuffer extends ArrayBufferLike = ArrayBufferLike>
32
+ extends Uint8Array<TArrayBuffer>
33
+ implements IsoBuffer<TArrayBuffer>
34
+ {
17
35
  /**
18
- * Static constructor
36
+ * Creates a new `IsoBuffer` containing `string`. The `encoding` parameter identifies
37
+ * the character encoding to be used when converting `string` into bytes.
19
38
  *
20
- * @param value - (string | ArrayBuffer).
21
- * @param encodingOrOffset - (string | number).
22
- * @param length - (number).
39
+ * @param string - A string to encode.
40
+ * @param encoding - The encoding of `string`. **Default:** `'utf8'`.
23
41
  *
24
- * @privateRemarks TODO: Use actual types
42
+ * @privateRemarks
43
+ * Node buffer.buffer.d.ts declaration:
44
+ * `from(string: WithImplicitCoercion<string>, encoding?: IsoBufferEncoding): Buffer<ArrayBuffer>;`
25
45
  */
26
- public static from(value: unknown, encodingOrOffset?: unknown, length?: unknown): IsoBuffer;
46
+ public static from(string: string, encoding?: IsoBufferEncoding): IsoBuffer<ArrayBuffer>;
47
+ /**
48
+ * Allocates a new `IsoBuffer` using an `array` of bytes in the range `0` – `255`.
49
+ * Array entries outside that range will be truncated to fit into it.
50
+ *
51
+ * @param value - `Uint8Array`.
52
+ *
53
+ * @remarks
54
+ * This is limited to `Uint8Array` input to match Node implementation that
55
+ * takes `ArrayLike<number>` and returns `IsoBuffer<ArrayBuffer>`.
56
+ *
57
+ * @privateRemarks
58
+ * Node buffer.buffer.d.ts declaration:
59
+ * `from(array: WithImplicitCoercion<ArrayLike<number>>): IsoBuffer<ArrayBuffer>;`
60
+ */
61
+ public static from(array: Uint8Array): IsoBuffer<ArrayBuffer>;
62
+ /**
63
+ * This creates a view of the `ArrayBuffer` without copying the underlying
64
+ * memory. For example, when passed a reference to the `.buffer` property of a
65
+ * `TypedArray` instance, the newly created `Buffer` will share the same
66
+ * allocated memory as the `TypedArray`'s underlying `ArrayBuffer`.
67
+ *
68
+ * @param arrayBuffer - An `ArrayBuffer`, `SharedArrayBuffer`, for example the
69
+ * `.buffer` property of a `TypedArray`.
70
+ * @param byteOffset - Index of first byte to expose. **Default:** `0`.
71
+ * @param length - Number of bytes to expose. **Default:**
72
+ * `arrayBuffer.byteLength - byteOffset`.
73
+ *
74
+ * @privateRemarks
75
+ * Node buffer.buffer.d.ts declaration:
76
+ * ```
77
+ * public static from<TArrayBuffer extends WithImplicitCoercion<ArrayBufferLike>>(
78
+ * arrayBuffer: TArrayBuffer,
79
+ * byteOffset?: number,
80
+ * length?: number,
81
+ * ): Buffer<ImplicitArrayBuffer<TArrayBuffer>>;
82
+ * ```
83
+ */
84
+ public static from<TArrayBuffer extends ArrayBufferLike>(
85
+ arrayBuffer: TArrayBuffer,
86
+ byteOffset?: number,
87
+ length?: number,
88
+ ): IsoBuffer<TArrayBuffer>;
89
+
27
90
  public static isBuffer(obj: unknown): obj is Buffer;
28
91
  }
29
92
 
30
93
  /**
94
+ * The native Node.js Buffer implementation.
95
+ *
31
96
  * @internal
32
97
  */
33
- export const IsoBuffer = Buffer;
34
-
35
- /**
36
- * @internal
37
- */
38
- export type IsoBuffer = Buffer;
98
+ export const IsoBuffer: IsoBufferConstructor = Buffer;
39
99
 
40
100
  /**
41
- * Converts a Uint8Array to a string of the provided encoding.
42
- * @remarks Useful when the array might be an IsoBuffer.
101
+ * Converts a `Uint8Array` to a string of the provided encoding.
102
+ * @remarks Useful when the array might be an `IsoBuffer`.
43
103
  * @param arr - The array to convert.
44
104
  * @param encoding - Optional target encoding; only "utf8" and "base64" are
45
105
  * supported, with "utf8" being default.
@@ -47,14 +107,10 @@ export type IsoBuffer = Buffer;
47
107
  *
48
108
  * @internal
49
109
  */
50
- export function Uint8ArrayToString(
51
- arr: Uint8Array,
52
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
53
- encoding?: "utf8" | "utf-8" | "base64",
54
- ): string {
110
+ export function Uint8ArrayToString(arr: Uint8Array, encoding?: IsoBufferEncoding): string {
55
111
  // Buffer extends Uint8Array. Therefore, 'arr' may already be a Buffer, in
56
112
  // which case we can avoid copying the Uint8Array into a new Buffer instance.
57
- return (Buffer.isBuffer(arr) ? arr : Buffer.from(arr)).toString(encoding);
113
+ return (IsoBuffer.isBuffer(arr) ? arr : IsoBuffer.from(arr)).toString(encoding);
58
114
  }
59
115
 
60
116
  /**
@@ -63,7 +119,7 @@ export function Uint8ArrayToString(
63
119
  *
64
120
  * @internal
65
121
  */
66
- export function stringToBuffer(input: string, encoding: string): ArrayBuffer {
122
+ export function stringToBuffer(input: string, encoding: IsoBufferEncoding): ArrayBuffer {
67
123
  const iso = IsoBuffer.from(input, encoding);
68
124
  // In a Node environment, IsoBuffer may be a Node.js Buffer. Node.js will
69
125
  // pool multiple small Buffer instances into a single ArrayBuffer, in which
@@ -83,7 +139,7 @@ export function stringToBuffer(input: string, encoding: string): ArrayBuffer {
83
139
  * @internal
84
140
  */
85
141
  export const bufferToString = (
86
- blob: ArrayBufferLike,
87
- // eslint-disable-next-line unicorn/text-encoding-identifier-case -- this value is supported, just discouraged
88
- encoding: "utf8" | "utf-8" | "base64",
89
- ): string => IsoBuffer.from(blob).toString(encoding);
142
+ blob: ArrayBufferLike | Uint8Array,
143
+ encoding: IsoBufferEncoding,
144
+ ): string =>
145
+ Uint8ArrayToString(blob instanceof Uint8Array ? blob : IsoBuffer.from(blob), encoding);
@@ -4,14 +4,84 @@
4
4
  */
5
5
 
6
6
  /**
7
- * Converts a Uint8Array array to an ArrayBuffer.
8
- * @param array - Array to convert to ArrayBuffer.
7
+ * Distills an `ArrayBuffer` from an `ArrayBufferLike` or creates one from `SharedArrayBuffer` if needed.
8
+ *
9
+ * @param buffer - `ArrayBuffer` or any `ArrayBufferLike` to convert to `ArrayBuffer`.
10
+ *
11
+ * @remarks The returned buffer may be the input array's backing buffer or a copy of its bytes.
12
+ * Callers should not mutate the returned buffer unless they own the input array and its backing
13
+ * storage, and should not rely on mutations being reflected in the input array.
14
+ *
15
+ * @internal
16
+ */
17
+ export function ArrayBufferLikeToArrayBuffer(buffer: ArrayBufferLike): ArrayBuffer {
18
+ if (buffer instanceof ArrayBuffer) {
19
+ return buffer;
20
+ }
21
+ // Use .slice to get `ArrayBuffer` backing that `Blob` requires.
22
+ // Note that `buffer.slice` preserves the underlying buffer type, so we
23
+ // need to use .slice on a `Uint8Array` to ensure we get an `ArrayBuffer`.
24
+ // eslint-disable-next-line unicorn/prefer-spread -- spread is not the same as slice for `Uint8Array`
25
+ return new Uint8Array(buffer).slice().buffer;
26
+ }
27
+
28
+ /**
29
+ * Distills a `Uint8Array` array to an `ArrayBuffer | SharedArrayBuffer` copying
30
+ * data as needed per the array's `byteOffset` and `byteLength` to have a full
31
+ * buffer (no offset and entire length).
32
+ *
33
+ * @param array - Array to extract buffer from.
34
+ *
35
+ * @remarks The returned buffer may be the input array's backing buffer or a copy of its bytes.
36
+ * Callers should not mutate the returned buffer unless they own the input array and its backing
37
+ * storage, and should not rely on mutations being reflected in the input array.
9
38
  *
10
39
  * @internal
11
40
  */
12
- export function Uint8ArrayToArrayBuffer(array: Uint8Array): ArrayBuffer {
41
+ export function Uint8ArrayToArrayBufferLike<TArrayBuffer extends ArrayBufferLike>(
42
+ array: Uint8Array<TArrayBuffer>,
43
+ ): TArrayBuffer {
13
44
  if (array.byteOffset === 0 && array.byteLength === array.buffer.byteLength) {
14
45
  return array.buffer;
15
46
  }
16
- return array.buffer.slice(array.byteOffset, array.byteOffset + array.byteLength);
47
+ // Both ArrayBuffer and SharedArrayBuffer have a slice method and will return
48
+ // their same type, but TypeScript does not correctly determine that will
49
+ // satisfy TArrayBuffer. Cast to TArrayBuffer to indicate that.
50
+ return array.buffer.slice(
51
+ array.byteOffset,
52
+ array.byteOffset + array.byteLength,
53
+ ) as TArrayBuffer;
54
+ }
55
+
56
+ /**
57
+ * Distills a `Uint8Array` array to an `ArrayBuffer` copying data as needed per the array's
58
+ * `byteOffset` and `byteLength` or if underlying buffer is not `ArrayBuffer`.
59
+ *
60
+ * @param array - Array to convert to `ArrayBuffer`.
61
+ *
62
+ * @remarks The returned buffer may be the input array's backing buffer or a copy of its bytes.
63
+ * Callers should not mutate the returned buffer unless they own the input array and its backing
64
+ * storage, and should not rely on mutations being reflected in the input array.
65
+ *
66
+ * Prefer to use {@link Uint8ArrayToArrayBufferLike} if the call site doesn't require
67
+ * an explicit `ArrayBuffer` (can also handle `SharedArrayBuffer`).
68
+ *
69
+ * @internal
70
+ */
71
+ export function AnyUint8ArrayToArrayBuffer(array: Uint8Array): ArrayBuffer {
72
+ // Shortcut to provider underlying buffer as-is when possible.
73
+ if (
74
+ array.byteOffset === 0 &&
75
+ array.byteLength === array.buffer.byteLength &&
76
+ array.buffer instanceof ArrayBuffer
77
+ ) {
78
+ return array.buffer;
79
+ }
80
+ // .slice is used to ensure underlying buffer is:
81
+ // 1. ArrayBuffer
82
+ // 2. the complete data (no offset, full length)
83
+ // Cloning specified per https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/slice
84
+ // and ECMAScript https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.slice.
85
+ // eslint-disable-next-line unicorn/prefer-spread -- spread is not the same as slice for `Uint8Array`
86
+ return array.slice().buffer;
17
87
  }
@@ -9,7 +9,7 @@ import * as base64js from "base64-js";
9
9
  import { IsoBuffer } from "./bufferBrowser.js";
10
10
 
11
11
  async function digestBuffer(
12
- file: IsoBuffer,
12
+ file: IsoBuffer<ArrayBuffer>,
13
13
  algorithm: "SHA-1" | "SHA-256",
14
14
  ): Promise<Uint8Array> {
15
15
  const hash = await crypto.subtle.digest(algorithm, file);
@@ -48,7 +48,7 @@ function encodeDigest(hashArray: Uint8Array, encoding: "hex" | "base64"): string
48
48
  * @internal
49
49
  */
50
50
  export async function hashFile(
51
- file: IsoBuffer,
51
+ file: IsoBuffer<ArrayBuffer>,
52
52
  algorithm: "SHA-1" | "SHA-256" = "SHA-1",
53
53
  hashEncoding: "hex" | "base64" = "hex",
54
54
  ): Promise<string> {
@@ -82,7 +82,10 @@ export async function gitHashFile(file: IsoBuffer): Promise<string> {
82
82
  const size = file.byteLength;
83
83
  // eslint-disable-next-line unicorn/prefer-code-point
84
84
  const filePrefix = `blob ${size.toString()}${String.fromCharCode(0)}`;
85
- const hashBuffer = IsoBuffer.from(filePrefix + file.toString());
85
+ const prefixBuffer = IsoBuffer.from(filePrefix);
86
+ const hashBuffer = new IsoBuffer(prefixBuffer.byteLength + file.byteLength);
87
+ hashBuffer.set(prefixBuffer);
88
+ hashBuffer.set(file, prefixBuffer.byteLength);
86
89
 
87
90
  // hashFile uses sha1; if that changes this will need to change too
88
91
  return hashFile(hashBuffer);
@@ -20,9 +20,13 @@ import type { IsoBuffer } from "./bufferNode.js";
20
20
  * @returns The hash of the content of the buffer.
21
21
  *
22
22
  * @internal
23
+ *
24
+ * @privateRemarks
25
+ * This function does not strictly require `<ArrayBuffer>` backing, but it is typed
26
+ * that way to match the browser implementation, which does require `<ArrayBuffer>` backing.
23
27
  */
24
28
  export async function hashFile(
25
- file: IsoBuffer,
29
+ file: IsoBuffer<ArrayBuffer>,
26
30
  algorithm: "SHA-1" | "SHA-256" = "SHA-1",
27
31
  hashEncoding: "hex" | "base64" = "hex",
28
32
  ): Promise<string> {
@@ -50,8 +54,12 @@ export async function hashFile(
50
54
  * @returns The sha1 hash of the content of the buffer with the `blob` prefix and size
51
55
  *
52
56
  * @internal
57
+ *
58
+ * @privateRemarks
59
+ * This function does not strictly require `<ArrayBuffer>` backing, but it is typed
60
+ * that way to match the browser implementation, which does require `<ArrayBuffer>` backing.
53
61
  */
54
- export async function gitHashFile(file: IsoBuffer): Promise<string> {
62
+ export async function gitHashFile(file: IsoBuffer<ArrayBuffer>): Promise<string> {
55
63
  const size = file.byteLength;
56
64
  // eslint-disable-next-line unicorn/prefer-code-point
57
65
  const filePrefix = `blob ${size.toString()}${String.fromCharCode(0)}`;