Skip to content

Commit 8d8a604

Browse files
committed
chore(pass-style): make CopyArray compatible with ReadonlyArray
1 parent 3b533de commit 8d8a604

File tree

3 files changed

+44
-6
lines changed

3 files changed

+44
-6
lines changed

packages/pass-style/NEWS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,16 @@ User-visible changes in `@endo/pass-style`:
22

33
# Next release
44

5-
- deprecates `assertChecker`. Use `Fail` in the confirm/reject pattern instead, as supported by `@endo/errors/rejector.js`.
5+
- Deprecates `assertChecker`. Use `Fail` in the confirm/reject pattern instead,
6+
as supported by `@endo/errors/rejector.js`.
67
- Enables `passStyleOf` to make errors passable as a side-effect when SES locks
78
down with `hardenTaming` set to `unsafe`, which impacts errors on V8 starting
89
with Node.js 21, and similar engines, that own a `stack` getter and setter
910
that would otherwise be repaired as a side-effect of `harden`.
11+
- Updates `CopyArray<T>` to be a `ReadonlyArray<Passable>`. We know dynamically
12+
that the CopyArray is hardened, so this typing statically reveals where
13+
mutation is attempted. Use `Passable[]` if you want to have a mutable array
14+
that contains only passable values.
1015

1116
# 1.6.3 (2025-07-11)
1217

packages/pass-style/src/types.d.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ export type Container<PC extends PassableCap, E extends Error> =
116116
| CopyArrayInterface<PC, E>
117117
| CopyRecordInterface<PC, E>
118118
| CopyTaggedInterface<PC, E>;
119-
interface CopyArrayInterface<PC extends PassableCap, E extends Error>
119+
export interface CopyArrayInterface<PC extends PassableCap, E extends Error>
120120
extends CopyArray<Passable<PC, E>> {}
121-
interface CopyRecordInterface<PC extends PassableCap, E extends Error>
121+
export interface CopyRecordInterface<PC extends PassableCap, E extends Error>
122122
extends CopyRecord<Passable<PC, E>> {}
123-
interface CopyTaggedInterface<PC extends PassableCap, E extends Error>
123+
export interface CopyTaggedInterface<PC extends PassableCap, E extends Error>
124124
extends CopyTagged<string, Passable<PC, E>> {}
125125

126126
export type PassStyleOf = {
@@ -134,7 +134,7 @@ export type PassStyleOf = {
134134
(p: Promise<any>): 'promise';
135135
(p: Error): 'error';
136136
(p: CopyTagged): 'tagged';
137-
(p: any[]): 'copyArray';
137+
(p: readonly any[]): 'copyArray';
138138
(p: Iterable<any>): 'remotable';
139139
(p: Iterator<any, any, undefined>): 'remotable';
140140
<T extends PassStyled<PassStyleMarker, any>>(p: T): ExtractStyle<T>;
@@ -199,10 +199,15 @@ export type PassableCap =
199199
| RemotableObject
200200
| RemotableBrand<any, any>;
201201

202+
/**
203+
* Types you would get from Awaited<PassableCap>
204+
*/
205+
export type AwaitedPassableCap = RemotableObject | RemotableBrand<any, any>;
206+
202207
/**
203208
* A Passable sequence of Passable values.
204209
*/
205-
export type CopyArray<T extends Passable = any> = Array<T>;
210+
export type CopyArray<T extends Passable = any> = ReadonlyArray<T>;
206211

207212
/**
208213
* A hardened immutable ArrayBuffer.

packages/pass-style/src/types.test-d.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,28 @@ expectType<PassStyle>(passStyleOf(someUnknown));
3333

3434
const expectPassable = (val: Passable) => {};
3535

36+
() => {
37+
const arr = ['hello'] as string[];
38+
expectPassable(arr);
39+
arr.push('world');
40+
const slice = arr.slice();
41+
expectPassable(slice);
42+
arr.shift();
43+
slice.shift();
44+
};
45+
46+
() => {
47+
const roArr = ['hello'] as Readonly<string[]>;
48+
expectPassable(roArr);
49+
// @ts-expect-error immutable
50+
roArr.push('world');
51+
const roSlice = roArr.slice();
52+
expectPassable(roSlice);
53+
// @ts-expect-error immutable
54+
roArr.shift();
55+
roSlice.shift();
56+
};
57+
3658
const fn = () => {};
3759

3860
expectPassable(1);
@@ -44,6 +66,12 @@ expectPassable(fn());
4466

4567
expectPassable({});
4668
expectPassable({ a: {} });
69+
expectPassable({ a: { b: {} } });
70+
expectPassable(['car', 'cdr']);
71+
expectPassable(['car', 'cdr'] as string[]);
72+
expectPassable([['a'], ['b']] as const);
73+
expectPassable(['car', 'cdr'] as Readonly<string[]>);
74+
expectPassable(['car', 'cdr'] as Readonly<[string, string]>);
4775
// @ts-expect-error not passable
4876
expectPassable(fn);
4977
// FIXME promise for a non-Passable is not Passable

0 commit comments

Comments
 (0)