@small-web/kitten-types 1.0.0 → 1.0.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.
package/index.d.ts CHANGED
@@ -1,14 +1,11 @@
1
- // @small-web/kitten-types
2
- //
3
- // Types-only package for Kitten (https://kitten.small-web.org).
4
- //
5
- // This entry point re-exports the reusable Kitten types (for explicit
6
- // annotations) and pulls in the ambient `declare global` declaration of the
7
- // `kitten` namespace (so that the global `kitten` object is typed wherever
8
- // this package is on the type-checker’s include path).
1
+ /**
2
+ Kitten Types (@small-web/kitten-types)
3
+
4
+ This entry point re-exports Kitten types for use in explicit annotations.
5
+
6
+ Side-effect: Brings `declare global { var kitten }` into scope for any project that depends on this package.
7
+ */
9
8
 
10
- // Side-effect import: brings the ambient `declare global { var kitten }` into
11
- // scope for any project that depends on this package.
12
9
  import './global.d.ts'
13
10
 
14
11
  // Reusable types, re-exported for explicit annotations
@@ -38,6 +35,5 @@ export type {
38
35
 
39
36
  export { yaml } from './types.d.ts'
40
37
 
41
- // Also export the hand-written global namespace shape, in case a consumer
42
- // wants to reference it explicitly (e.g. `typeof kitten`).
43
- export type { KittenGlobal } from './global.d.ts'
38
+ // Also export hand-written global namespace shape, in case an author wants to reference it explicitly (e.g. `typeof kitten`).
39
+ export type { kitten } from './global.d.ts'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@small-web/kitten-types",
3
- "version": "1.0.0",
4
- "description": "Types-only package for Kitten: declares the global `kitten` namespace and exports Kitten types.",
3
+ "version": "1.0.1",
4
+ "description": "Types-only package for Kitten: declares global `kitten` namespace and exports Kitten types.",
5
5
  "types": "index.d.ts",
6
6
  "exports": {
7
7
  ".": {
package/types.d.ts CHANGED
@@ -1,5 +1,33 @@
1
1
  import EventEmitter from 'node:events'
2
2
 
3
+ /**
4
+ Represents a browser session.
5
+
6
+ Sessions are automatically persisted (and expired) in Kitten’s internal database.
7
+
8
+ Used by Kitten to provide automatic authentication for Small Web places.
9
+
10
+ You can also add/update arbitrary session data by adding/updating properties on this object in your routes (this object maps a session object in the `kitten._db.sessions` table in the internal database).
11
+
12
+ You can emit events on a session in order to communicate with other browser tabs and windows that share the same session.
13
+
14
+ @example
15
+ export default function ({ request }) {
16
+ request.session.kittens ??= { count: 1 }
17
+
18
+ return kitten.html`
19
+ <h1>Kitten count</h1>
20
+ <p>${'🐱️'.repeat(request.session.kittens.count++)}</p>
21
+ `
22
+ }
23
+
24
+ @see https://kitten.small-web.org/tutorials/sessions/
25
+
26
+ @remarks
27
+ Do not save custom classes on request.session or JSDB will throw an error when it tries to open the internal `_db` database and cannot find your custom class to instantiate.
28
+
29
+ If you want persisted session-level custom objects, store them in your own database table in `kitten.db` keyed by `session.id`.
30
+ */
3
31
  export class Session extends EventEmitter {
4
32
  id: string
5
33
  authenticated: boolean
@@ -34,10 +62,31 @@ interface UploadConstructorParameters {
34
62
  done: boolean
35
63
  }
36
64
 
65
+ /**
66
+ Represents an uploaded file.
67
+
68
+ @see https://kitten.small-web.org/tutorials/multipart-forms-and-file-uploads/
69
+ */
37
70
  export class Upload {
71
+ /**
72
+ @private
73
+ @remarks For internal use by Kitten’s automatic POST route handler
74
+ */
38
75
  constructor(parameters:UploadConstructorParameters)
76
+
77
+ /**
78
+ The absolute resource path for accessing this upload via a GET request (content-disposition: inline).
79
+ */
39
80
  resourcePath: string
81
+
82
+ /**
83
+ Returns the absolute download path (content-disposition: attachment).
84
+ */
40
85
  downloadPath: string
86
+
87
+ /**
88
+ Deletes this uploaded file from the file system and also from the internal database.
89
+ */
41
90
  delete(): Promise<void>
42
91
  }
43
92
 
@@ -104,12 +153,21 @@ export class MessageSender {
104
153
  */
105
154
  type JavaScriptFunction = (...args: any[]) => any
106
155
 
156
+ /**
157
+ A class constructor.
158
+ */
107
159
  type Constructor<T> = new (...args: any[]) => T
108
160
 
161
+ /**
162
+ Represents the render function of a component that is bound to component (as its `this`).
163
+ */
109
164
  type BoundComponentRenderFunction<T extends KittenComponent> =
110
165
  ((...args: Parameters<T['html']>) => Promise<Awaited<ReturnType<T['html']>>>)
111
166
  & { boundObject: T }
112
167
 
168
+ /**
169
+ An EventEmitter listener with a `remove()` method for cleanup.
170
+ */
113
171
  export class Listener {
114
172
  /**
115
173
  Create EventEmitter listener.
@@ -122,6 +180,60 @@ export class Listener {
122
180
  remove ():void
123
181
  }
124
182
 
183
+ /**
184
+ Kitten Component.
185
+
186
+ A class that makes it easier to author hierarchies of connected components.
187
+
188
+ Handles wiring up and disposal of event listeners for you and gives you an ergonomic, class-based way of writing maintainable applications that take advantage of Kitten’s Streaming HTML workflow while working with well-encapsulated, self-contained components in a clear hierarchy.
189
+
190
+ Extend and instantiate this class to create your own components (components, fragments, layout components, and pages) and provide at least an override for the `html()` method, which is just a function that returns `kitten.html`.
191
+
192
+ The example, below, updates a persisted counter via the + and - buttons (index.page.ts).
193
+
194
+ @example
195
+ // Initialise the database with a persisted counter object.
196
+ kitten.db.counter ??= { count: 0 }
197
+
198
+ export default class CounterPage extends kitten.Page {
199
+ counter
200
+
201
+ constructor () {
202
+ super()
203
+ this.counter = this.addChild(new Counter())
204
+ }
205
+
206
+ override html () {
207
+ return kitten.html`
208
+ <page css>
209
+ <h1>Counter</h1>
210
+ <${this.counter} />
211
+ `
212
+ }
213
+ }
214
+
215
+ class Counter extends kitten.Component {
216
+ override html () {
217
+ return kitten.html`
218
+ <div>
219
+ <div
220
+ id='count'
221
+ aria-live='assertive'
222
+ >
223
+ ${kitten.db.counter.count}
224
+ </div>
225
+ <button name='update' connect data='{value: -1}' aria-label='decrement'>-</button>
226
+ <button name='update' connect data='{value: 1}' aria-label='increment'>+</button>
227
+ </div>
228
+ `
229
+ }
230
+
231
+ onUpdate (data: { value: number } ) {
232
+ kitten.db.counter.count += data.value
233
+ this.update()
234
+ }
235
+ }
236
+ */
125
237
  export class KittenComponent {
126
238
  /**
127
239
  Unique ID (crypto.uuid) of this component. You can overwrite it with your own if you like.
@@ -195,16 +307,11 @@ export class KittenComponent {
195
307
  onAddToParent() :void
196
308
 
197
309
  /**
198
- Optional hook: override this method to provide custom logic for your app
199
- to be run when the page this component is on connects to the client via its WebSocket.
310
+ Optional hook: override this method to provide custom logic for your app to be run when the page this component is on connects to the client via its WebSocket.
200
311
 
201
- This hook will get called not just for initially-rendered components when the page
202
- first connects but also for any components dynamically added to an already-connected
203
- page/component hierarchy after the fact.
312
+ This hook will get called not just for initially-rendered components when the page first connects but also for any components dynamically added to an already-connected page/component hierarchy after the fact.
204
313
 
205
- (So you can be sure that this handler will be called once when a component is fully
206
- initialised on a connected page. This is a good place to add event handlers or to
207
- start streaming updates to the client.)
314
+ (So you can be sure that this handler will be called once when a component is fully initialised on a connected page. This is a good place to add event handlers or to start streaming updates to the client.)
208
315
  */
209
316
  onConnect(parameterObject: {
210
317
  page?: KittenComponent,
@@ -213,10 +320,7 @@ export class KittenComponent {
213
320
  }):void
214
321
 
215
322
  /**
216
- Optional hook: override this method to run custom logic when the page
217
- this component is on disconnects from the client via its WebSocket.
218
- (This usually means the page the about to be unloaded, either because the
219
- person is nagivating away from it or reloading it.)
323
+ Optional hook: override this method to run custom logic when the page this component is on disconnects from the client via its WebSocket. (This usually means the page the about to be unloaded, either because the person is nagivating away from it or reloading it.)
220
324
  */
221
325
  onDisconnect(parameterObject: {
222
326
  page?: KittenComponent,
@@ -227,8 +331,7 @@ export class KittenComponent {
227
331
  /**
228
332
  Adds child component to this one.
229
333
 
230
- Child components are entered into the event bubbling hierarchy and
231
- contain a reference to the page that they’re on.
334
+ Child components are entered into the event bubbling hierarchy and contain a reference to the page that they’re on.
232
335
  */
233
336
  addChild<T extends KittenComponent> (component: T): T
234
337
 
@@ -256,9 +359,7 @@ export class KittenComponent {
256
359
  /**
257
360
  Add event handler for an EventEmitter.
258
361
 
259
- Event listening and listener clean-up are automatically handled so the
260
- author doesn’t have to worry about implementing this finickety
261
- aspect manually.
362
+ Event listening and listener clean-up are automatically handled so the author doesn’t have to worry about implementing this finickety aspect manually.
262
363
  */
263
364
  addEventHandler (target: object, eventName: string, eventHandler: JavaScriptFunction):void
264
365
 
@@ -270,8 +371,7 @@ export class KittenComponent {
270
371
  /**
271
372
  Helper for removing this component from the live component hierachy.
272
373
 
273
- Also handles removal of event listeners for itself and all its
274
- children so we don’t have any leaks.
374
+ Also handles removal of event listeners for itself and all its children so we don’t have any leaks.
275
375
  */
276
376
  remove ():void
277
377
 
@@ -291,16 +391,14 @@ export class KittenComponent {
291
391
  emit (eventName:string, data?:any, event?:any, target?:string, self?:any):void
292
392
 
293
393
  /**
294
- Helper for adding an event handler for a page event and streaming an
295
- updated version of the component to the client (a common pattern).
394
+ Helper for adding an event handler for a page event and streaming an updated version of the component to the client (a common pattern).
296
395
 
297
396
  @param {string} eventName
298
397
  */
299
398
  updateOnEvent (eventName:string):void
300
399
 
301
400
  /**
302
- Helper (handler) for sending an updated version of this
303
- component to the page.
401
+ Helper (handler) for sending an updated version of this component to the page.
304
402
  */
305
403
  sendUpdatedComponentToPage ():void
306
404
  }
@@ -308,13 +406,13 @@ export class KittenComponent {
308
406
  /**
309
407
  KittenPage class.
310
408
 
311
- A KittenPage is a specialised KittenComponent that represents a live
312
- page in memory (a live page is one that has an automatic WebSocket connection
313
- to the page rendered by the PageRoute). It constitutes the root of the
314
- server-side component hierarchy. Its `html()` method is dynamically bound
315
- by the PageRoute to the authored page route function in function-based authoring
316
- and it is used directly via its global `kitten.Page` reference when authoring
317
- class-based page routes.
409
+ A KittenPage is a specialised KittenComponent that represents a live page in memory (a live page is one that has an automatic WebSocket connection to the page rendered by the PageRoute).
410
+
411
+ A KittenPage constitutes the root of the server-side component hierarchy.
412
+
413
+ Instance data in a `KittenPage` instance sticks around for the lifetime of the page (e.g., until a reload or a navigation event away from it in the browser).
414
+
415
+ If you need greater persistence, use session storage (`request.session`) or the built-in JSDB database (`kitten.db`).
318
416
  */
319
417
  export class KittenPage extends KittenComponent {
320
418
  request: KittenRequest
@@ -327,35 +425,20 @@ export class KittenPage extends KittenComponent {
327
425
  everyoneElse: MessageSender
328
426
 
329
427
  /**
330
- Construct new KittenPage instance with a reference to the
331
- request and response objects for the page.
428
+ Construct new KittenPage instance.
429
+
430
+ Note that the `request` and `response` properties are set after construction by Kitten to keep the constructor easy to override in your own subclasses. (The constructor is a good place to initialise known child components, etc.)
332
431
 
333
- Every page has a unique ID (inherited from KittenComponent) and can hold
334
- ephemeral page-level data. e.g., the states of components when
335
- using the Streaming HTML workfow. (Page storage is ephemeral is that
336
- it only lasts for the lifetime of a page in the browser and is destroyed
337
- when the page is closed or reloaded.)
432
+ Every page has a unique ID (inherited from KittenComponent) and can hold ephemeral page-level instance data. e.g., child components. (Page storage is ephemeral is that it only lasts for the lifetime of a page in the browser and is destroyed when the page is closed or reloaded.)
338
433
 
339
- If you need greater persistence, use session storage (request.session) or
340
- the built-in JSDB database (kitten.db).
434
+ If you need greater persistence, please use session storage (request.session) or the built-in JavaScript Database (JSDB) that you can reference at `kitten.db`.
341
435
 
342
- A page is an authored page if the author of the Kitten app/site wrote
343
- and exported a KittenPage subclass in the route (as opposed to exporting a
344
- simple function and function-based event handlers that then resulted
345
- in a generic page being created by Kitten’s PageRoute class). Keeping
346
- track of this is an optimisation that enabled the PageSocketRoute to
347
- not have to import the source file again if the page was authored
348
- as a page instance and thus already contains its event handlers (as
349
- opposed to the event handlers being exported as separate functions that
350
- have be read in and mixed into the generic KittenPage instance by the PageSocketRoute).
436
+ A page is known as an authored page if the author of the Kitten app/site wrote and exported a KittenPage subclass in the route (as opposed to exporting a simple function and function-based event handlers that then resulted in a generic page being created by Kitten’s `PageRoute` class). Keeping track of this is an optimisation that enables the `PageSocketRoute` to not have to import the source file again if the page was authored as a page instance and thus already contains its event handlers (as opposed to the event handlers being exported as separate functions that have be read in and mixed into the generic `KittenPage` instance by the `PageSocketRoute`).
351
437
  */
352
438
  constructor()
353
439
 
354
440
  /**
355
- The page has connected to its WebSocket. The list of sockets and the
356
- specific socket for this page are passed for storage on the page and the
357
- list of event handlers imported from the page (when using function-based
358
- page routes), if any, to be mixed into this instance as methods.
441
+ The page has connected to its WebSocket. The list of sockets and the specific socket for this page are passed for storage on the page and the list of event handlers imported from the page (when using function-based page routes), if any, to be mixed into this instance as methods.
359
442
  */
360
443
  connect (socket: WebSocketWithIsAlive, sockets: [WebSocketWithIsAlive], eventHandlers: Record<string, JavaScriptFunction>):void
361
444
 
@@ -369,11 +452,7 @@ export class KittenPage extends KittenComponent {
369
452
  /**
370
453
  Send specified message to just this page’s socket.
371
454
 
372
- You can specify an optional swap target that intelligently
373
- wraps what you’re sending with the necessary envelope tag.
374
- This is normally rather confusing with htmx’s oob swaps,
375
- especially when inserting table rows. See:
376
- https://htmx.org/attributes/hx-swap-oob/#using-alternate-swap-strategies
455
+ You can specify an optional swap target that intelligently wraps what you’re sending with the necessary envelope tag. This is normally rather confusing with htmx’s oob swaps, especially when inserting table rows. See: https://htmx.org/attributes/hx-swap-oob/#using-alternate-swap-strategies
377
456
  */
378
457
  send (message: BufferLike|Promise<BufferLike>, swapTarget?: {
379
458
  before?: string,
@@ -386,10 +465,7 @@ export class KittenPage extends KittenComponent {
386
465
  /**
387
466
  Request and response types.
388
467
 
389
- These vary per route type but are all built on the base of
390
- Polka’s request and response objects which are, themselves,
391
- based on the incoming message and server response types of
392
- Node’s own http module.
468
+ These vary per route type but are all built on the base of Polka’s request and response objects which are, themselves, based on the incoming message and server response types of Node’s own http module.
393
469
  */
394
470
 
395
471
  import type { IncomingMessage, ServerResponse } from 'http'
@@ -416,6 +492,11 @@ export interface PolkaRequest extends IncomingMessage {
416
492
  _parsedUrl: ParsedURL
417
493
  }
418
494
 
495
+ /**
496
+ The request objects received by Kitten routes.
497
+
498
+ Based on Polka’s request object with extra Kitten-specific properties and methods.
499
+ */
419
500
  export interface KittenRequest extends PolkaRequest {
420
501
  /**
421
502
  The raw body of non-multipart requests. Used, for example, for validation webhook signatures.
@@ -423,8 +504,7 @@ export interface KittenRequest extends PolkaRequest {
423
504
  rawBody: Buffer
424
505
 
425
506
  /**
426
- Check if the incoming request contains the "Content-Type"
427
- header field, and it contains the given mime `type`.
507
+ Check if the incoming request contains the `"Content-Type"` header field, and, if so, if it contains the specified mime `type`.
428
508
 
429
509
  Examples:
430
510
 
@@ -446,43 +526,71 @@ export interface KittenRequest extends PolkaRequest {
446
526
  */
447
527
  is (types:Array<string>|string):string|false|null
448
528
 
449
- session: Session
529
+ /**
530
+ Represents a browser session.
531
+
532
+ Sessions are automatically persisted (and expired) in Kitten’s internal database.
533
+
534
+ Used by Kitten to provide automatic authentication for Small Web places.
535
+
536
+ You can also add/update arbitrary session data by adding/updating properties on this object in your routes (this object maps a session object in the `kitten._db.sessions` table in the internal database).
537
+
538
+ You can emit events on a session in order to communicate with other browser tabs and windows that share the same session.
539
+
540
+ @example
541
+ export default function ({ request }) {
542
+ request.session.kittens ??= { count: 1 }
543
+
544
+ return kitten.html`
545
+ <h1>Kitten count</h1>
546
+ <p>${'🐱️'.repeat(request.session.kittens.count++)}</p>
547
+ `
548
+ }
549
+
550
+ @see https://kitten.small-web.org/tutorials/sessions/
551
+
552
+ @remarks
553
+ Do not save custom classes on request.session or JSDB will throw an error when it tries to open the internal `_db` database and cannot find your custom class to instantiate.
554
+
555
+ If you want persisted session-level custom objects, store them in your own database table in `kitten.db` keyed by `session.id`.
556
+ */
557
+ session: Session & {
558
+ [key: string]: any
559
+ }
450
560
  }
451
561
 
452
562
  type TypedArray = Int8Array|Uint8Array|Uint8ClampedArray|Int16Array|Uint16Array|Int32Array|Uint32Array|Float32Array|Float64Array|BigInt64Array|BigUint64Array
453
563
 
564
+ /**
565
+ The response objects received by Kitten routes.
566
+
567
+ Based on Polka’s response object with extra Kitten-specific properties and methods.
568
+ */
454
569
  export interface KittenResponse extends PolkaResponse {
455
570
  /**
456
- JSON.stringifies passed data and ends response with
457
- inline JSON using proper headers.
571
+ JSON.stringifies passed data and ends response with inline JSON using proper headers.
458
572
  */
459
573
  json (data:any):void
460
574
 
461
575
  /**
462
- JSON.stringifies passed data and ends response with JSON attachment
463
- using proper headers and requested file name (or data.json as fallback
464
- if no file name is provided).
576
+ JSON.stringifies passed data and ends response with JSON attachment using proper headers and requested file name (or data.json as fallback if no file name is provided).
465
577
  */
466
578
  jsonFile (data:any, fileName?:string):void
467
579
 
468
580
  /**
469
- Ends response with a file. Optionally, uses passed file name
470
- (or 'dowload' as fallback) and passed mime type (or
471
- 'application/octet-stream' as fallback).
581
+ Ends response with a file. Optionally, uses passed file name (or `'download'` as fallback) and passed mime type (or `'application/octet-stream'` as fallback).
472
582
  */
473
583
  file (data:string|Buffer|TypedArray|DataView, fileName?:string, mimeType?:string):void
474
584
 
475
585
  /**
476
- Ends response with 200 OK response code, and the response body,
477
- if any (and '' if not).
586
+ Ends response with 200 OK response code, and the response body, if any (and `''` if not).
478
587
 
479
588
  @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200
480
589
  */
481
590
  ok (body?:string):void
482
591
 
483
592
  /**
484
- Ends response with 201 Created response code, and the response body,
485
- if any (and '' if not).
593
+ Ends response with 201 Created response code, and the response body, if any (and `''` if not).
486
594
 
487
595
  @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
488
596
  */
@@ -508,9 +616,7 @@ export interface KittenResponse extends PolkaResponse {
508
616
  /**
509
617
  400 Bad Request
510
618
 
511
- Indicates that the server cannot or will not process the request due to something
512
- that is perceived to be a client error (e.g., malformed request syntax, invalid request
513
- message framing, or deceptive request routing).
619
+ Indicates that the server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
514
620
 
515
621
  @link https://httpwg.org/specs/rfc9110.html#rfc.section.15.5.1
516
622
  */
@@ -524,13 +630,9 @@ export interface KittenResponse extends PolkaResponse {
524
630
  unauthenticated (body?:string):void
525
631
 
526
632
  /**
527
- 403 Forbidden response. This should be returned
528
- if the request is authenticated but lacks the authorisation
529
- (i.e., sufficient rights) to access the resource.
633
+ 403 Forbidden response. This should be returned if the request is authenticated but lacks the authorisation (i.e., sufficient rights) to access the resource.
530
634
 
531
- If the request requires authentication but has not been
532
- authenticated, you should return a “401 Unauthorized”
533
- (actually: unauthenticated) response.
635
+ If the request requires authentication but has not been authenticated, you should return a “401 Unauthorized” (actually: unauthenticated) response.
534
636
 
535
637
  @see unauthenticated
536
638
  */
@@ -549,12 +651,18 @@ export interface KittenResponse extends PolkaResponse {
549
651
  error (body?:string):void
550
652
 
551
653
  /**
552
- General shorthand helper for setting the status code and
553
- ending the response with an optional body.
654
+ General shorthand helper for setting the status code and ending the response with an optional body.
554
655
  */
555
656
  withCode (statusCode:number, body?:string):void
556
657
  }
557
658
 
659
+ /**
660
+ Kitten’s POST request object.
661
+
662
+ If the POST route had a multi-part form with file uploads, they will be handled automatically by Kitten and you can find them in the `uploads` property.
663
+
664
+ @see https://kitten.small-web.org/tutorials/multipart-forms-and-file-uploads/
665
+ */
558
666
  export interface KittenPostRequest extends KittenRequest {
559
667
  uploads: Upload[]
560
668
  }