0
# Assertion System
1
2
Enhanced assertion library with detailed error reporting, causal console integration, and rich diagnostic capabilities. The SES assertion system provides more informative error messages while protecting sensitive information from leaking through the exception path.
3
4
## Capabilities
5
6
### Core Assertion Function
7
8
The main assertion function with rich error reporting and type assertion capabilities.
9
10
```javascript { .api }
11
/**
12
* Assert that a condition is truthy with detailed error reporting
13
* @param flag - The condition to test
14
* @param details - Descriptive details about the assertion
15
* @param errConstructor - Optional error constructor to use
16
* @param options - Additional error creation options
17
*/
18
function assert(
19
flag: any,
20
details?: Details,
21
errConstructor?: GenericErrorConstructor,
22
options?: AssertMakeErrorOptions
23
): asserts flag;
24
```
25
26
**Usage Examples:**
27
28
```javascript
29
import 'ses';
30
31
lockdown();
32
33
// Basic assertion
34
assert(x > 0, 'x must be positive');
35
36
// Assertion with template literal details
37
const user = { name: 'Alice', age: 25 };
38
assert(user.age >= 18, assert.details`User ${user.name} must be adult, got age ${user.age}`);
39
40
// Assertion with custom error type
41
assert(user.name, 'User must have name', TypeError);
42
```
43
44
### Type Assertions
45
46
Specialized assertion methods for common type checking scenarios.
47
48
```javascript { .api }
49
/**
50
* Assert expected typeof result with overloaded signatures for different types
51
*/
52
interface AssertTypeof {
53
(specimen: any, typeName: 'bigint', details?: Details): asserts specimen is bigint;
54
(specimen: any, typeName: 'boolean', details?: Details): asserts specimen is boolean;
55
(specimen: any, typeName: 'function', details?: Details): asserts specimen is Function;
56
(specimen: any, typeName: 'number', details?: Details): asserts specimen is number;
57
(specimen: any, typeName: 'object', details?: Details): asserts specimen is Record<any, any> | null;
58
(specimen: any, typeName: 'string', details?: Details): asserts specimen is string;
59
(specimen: any, typeName: 'symbol', details?: Details): asserts specimen is symbol;
60
(specimen: any, typeName: 'undefined', details?: Details): asserts specimen is undefined;
61
}
62
63
/**
64
* Assert that a value is a string (shorthand for typeof)
65
* @param specimen - Value to check
66
* @param details - Optional error details
67
*/
68
function string(specimen: any, details?: Details): asserts specimen is string;
69
70
/**
71
* Assert that two values are Object.is equal
72
* @param actual - The actual value
73
* @param expected - The expected value
74
* @param details - Optional error details
75
* @param errConstructor - Optional error constructor
76
* @param options - Optional error creation options
77
*/
78
function equal<T>(
79
actual: unknown,
80
expected: T,
81
details?: Details,
82
errConstructor?: GenericErrorConstructor,
83
options?: AssertMakeErrorOptions
84
): asserts actual is T;
85
86
/**
87
* Unconditionally fail an assertion
88
* @param details - Error details
89
* @param errConstructor - Optional error constructor
90
* @param options - Optional error creation options
91
*/
92
function fail(
93
details?: Details,
94
errConstructor?: GenericErrorConstructor,
95
options?: AssertMakeErrorOptions
96
): never;
97
```
98
99
**Usage Examples:**
100
101
```javascript
102
import 'ses';
103
104
lockdown();
105
106
// Type checking
107
const value = getUserInput();
108
assert.typeof(value, 'string');
109
// Now TypeScript knows value is a string
110
111
// String assertion shorthand
112
assert.string(value, 'Input must be string');
113
114
// Equality assertion
115
const result = calculate();
116
assert.equal(result, 42, 'Calculation should return 42');
117
118
// Conditional failure
119
if (dangerousCondition) {
120
assert.fail('System is in dangerous state');
121
}
122
```
123
124
### Error Utilities
125
126
Utilities for creating, annotating, and formatting errors with enhanced diagnostic information.
127
128
```javascript { .api }
129
/**
130
* Create error objects with detailed logging
131
* @param details - Error details
132
* @param errConstructor - Optional error constructor
133
* @param options - Optional error creation options
134
* @returns Created error object
135
*/
136
function error(
137
details?: Details,
138
errConstructor?: GenericErrorConstructor,
139
options?: AssertMakeErrorOptions
140
): Error;
141
142
/**
143
* Annotate errors with additional diagnostic details
144
* @param error - Error object to annotate
145
* @param details - Additional details to attach
146
*/
147
function note(error: Error, details: Details): void;
148
```
149
150
**Usage Examples:**
151
152
```javascript
153
import 'ses';
154
155
lockdown();
156
157
// Create detailed errors
158
const createValidationError = (field, value) => {
159
return assert.error(
160
assert.details`Invalid ${field}: expected string, got ${value}`,
161
TypeError,
162
{ errorName: 'ValidationError' }
163
);
164
};
165
166
// Annotate existing errors
167
try {
168
riskyOperation();
169
} catch (originalError) {
170
assert.note(originalError, assert.details`Failed during step ${currentStep}`);
171
throw originalError;
172
}
173
```
174
175
### Template Literal Utilities
176
177
Template literal functions for creating rich, secure error messages.
178
179
```javascript { .api }
180
/**
181
* Create details tokens for error messages with safe substitution
182
* @param template - Template literal strings
183
* @param args - Substitution arguments
184
* @returns Details token for error reporting
185
*/
186
function details(template: TemplateStringsArray | string[], ...args: any): DetailsToken;
187
188
/**
189
* Create and throw errors using template literal syntax
190
* @param template - Template literal strings
191
* @param args - Substitution arguments
192
* @throws Error with formatted message
193
*/
194
function Fail(template: TemplateStringsArray | string[], ...args: any): never;
195
196
/**
197
* Quote values for safe inclusion in error messages
198
* @param payload - Value to quote
199
* @param spaces - JSON.stringify spacing option
200
* @returns Stringable payload that renders quoted
201
*/
202
function quote(payload: any, spaces?: string | number): StringablePayload;
203
204
/**
205
* Embed strings directly without quotes (use with caution)
206
* @param payload - Value to embed
207
* @param spaces - JSON.stringify spacing option
208
* @returns Stringable payload without quotes
209
*/
210
function bare(payload: any, spaces?: string | number): StringablePayload;
211
```
212
213
**Usage Examples:**
214
215
```javascript
216
import 'ses';
217
218
lockdown();
219
220
// Template literal details
221
const { details: X, quote: q } = assert;
222
223
const validateUser = (user) => {
224
user.age >= 18 || assert.fail(X`User ${user.name} must be adult, got age ${user.age}`);
225
226
// Conditional failure with Fail
227
user.email || Fail`User ${user.name} must have email address`;
228
229
// Quote sensitive values
230
const token = getSecretToken();
231
assert(isValidToken(token), X`Invalid token format: ${q(token)}`);
232
};
233
234
// Create reusable details
235
const createUserError = (user, issue) => {
236
return X`User validation failed for ${user.name}: ${issue}`;
237
};
238
```
239
240
## Configuration and Error Handling
241
242
### Error Creation Options
243
244
Options for controlling error creation behavior and metadata.
245
246
```javascript { .api }
247
interface AssertMakeErrorOptions {
248
/** Custom error name for console output identification */
249
errorName?: string;
250
251
/** Causal error that led to this error */
252
cause?: Error;
253
254
/** Array of errors for AggregateError-style reporting */
255
errors?: Error[];
256
257
/** Whether to sanitize error for security (default: true) */
258
sanitize?: boolean;
259
}
260
```
261
262
### Assert Factory Function
263
264
Factory function for creating custom assert functions with different error handling behavior.
265
266
```javascript { .api }
267
/**
268
* Create custom assert function with specific error handling
269
* @param raise - Optional function called before throwing
270
* @param unredacted - Whether to include unredacted details
271
* @returns Configured assert function
272
*/
273
type MakeAssert = (raise?: Raise, unredacted?: boolean) => Assert;
274
275
/**
276
* Function called when assertion fails, for custom termination behavior
277
* @param reason - The error that caused the assertion failure
278
*/
279
type Raise = (reason: Error) => void;
280
```
281
282
**Usage Examples:**
283
284
```javascript
285
import 'ses';
286
287
lockdown();
288
289
// Create custom assert with logging
290
const logAndAssert = assert.makeAssert((error) => {
291
console.error('Assertion failed:', error.message);
292
// Could terminate vat, process, etc.
293
});
294
295
// Use custom assert
296
logAndAssert(condition, 'This will be logged before throwing');
297
298
// Create unredacted assert for debugging
299
const debugAssert = assert.makeAssert(undefined, true);
300
debugAssert(false, debugAssert.details`Debug info: ${sensitiveData}`);
301
```
302
303
## Advanced Usage Patterns
304
305
### Secure Error Reporting
306
307
The SES assertion system provides two levels of detail for errors:
308
309
1. **Console Output**: Full, unredacted details visible to console (assumed privileged)
310
2. **Exception Path**: Redacted details that hide sensitive substitution values
311
312
```javascript
313
import 'ses';
314
315
lockdown();
316
317
const sensitiveData = { token: 'secret-abc-123', userId: 'user-456' };
318
319
try {
320
assert(false, assert.details`Authentication failed for user ${sensitiveData.userId} with token ${sensitiveData.token}`);
321
} catch (error) {
322
// Exception message is redacted: "Authentication failed for user (a string) with token (a string)"
323
console.log('Exception:', error.message);
324
325
// Console sees full details when the error is logged
326
console.error(error); // Shows actual values
327
}
328
```
329
330
### Validation Pipelines
331
332
Using assertions for data validation with detailed error reporting:
333
334
```javascript
335
import 'ses';
336
337
lockdown();
338
339
const validateUserData = (userData) => {
340
const { details: X, quote: q } = assert;
341
342
// Chain validations with descriptive errors
343
assert.typeof(userData, 'object', X`User data must be object, got ${q(userData)}`);
344
assert(userData !== null, 'User data cannot be null');
345
346
assert.string(userData.name, X`User name must be string, got ${q(userData.name)}`);
347
assert(userData.name.length > 0, X`User name cannot be empty`);
348
349
assert.typeof(userData.age, 'number', X`User age must be number, got ${q(userData.age)}`);
350
assert(userData.age >= 0, X`User age must be non-negative, got ${userData.age}`);
351
assert(userData.age < 150, X`User age must be realistic, got ${userData.age}`);
352
353
if (userData.email !== undefined) {
354
assert.string(userData.email, X`User email must be string, got ${q(userData.email)}`);
355
assert(/\S+@\S+\.\S+/.test(userData.email), X`User email must be valid format, got ${q(userData.email)}`);
356
}
357
358
return userData; // TypeScript now knows userData is properly shaped
359
};
360
361
// Usage
362
try {
363
const validUser = validateUserData({
364
name: 'Alice',
365
age: 30,
366
email: 'alice@example.com'
367
});
368
console.log('Valid user:', validUser);
369
} catch (error) {
370
console.error('Validation failed:', error.message);
371
}
372
```
373
374
### Contract-Based Programming
375
376
Using assertions for pre-conditions, post-conditions, and invariants:
377
378
```javascript
379
import 'ses';
380
381
lockdown();
382
383
class BankAccount {
384
#balance = 0;
385
386
constructor(initialBalance) {
387
assert.typeof(initialBalance, 'number', 'Initial balance must be number');
388
assert(initialBalance >= 0, assert.details`Initial balance must be non-negative, got ${initialBalance}`);
389
this.#balance = initialBalance;
390
}
391
392
deposit(amount) {
393
// Pre-condition
394
assert.typeof(amount, 'number', 'Deposit amount must be number');
395
assert(amount > 0, assert.details`Deposit amount must be positive, got ${amount}`);
396
397
const oldBalance = this.#balance;
398
this.#balance += amount;
399
400
// Post-condition
401
assert(this.#balance === oldBalance + amount, 'Balance should increase by deposit amount');
402
assert(this.#balance >= 0, 'Balance should never be negative');
403
404
return this.#balance;
405
}
406
407
withdraw(amount) {
408
// Pre-conditions
409
assert.typeof(amount, 'number', 'Withdrawal amount must be number');
410
assert(amount > 0, assert.details`Withdrawal amount must be positive, got ${amount}`);
411
assert(amount <= this.#balance, assert.details`Insufficient funds: requested ${amount}, available ${this.#balance}`);
412
413
const oldBalance = this.#balance;
414
this.#balance -= amount;
415
416
// Post-conditions
417
assert(this.#balance === oldBalance - amount, 'Balance should decrease by withdrawal amount');
418
assert(this.#balance >= 0, 'Balance should never be negative');
419
420
return this.#balance;
421
}
422
423
getBalance() {
424
// Invariant check
425
assert(this.#balance >= 0, 'Balance invariant violated');
426
return this.#balance;
427
}
428
}
429
```