@typetron/framework 0.5.30 → 0.5.32

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 (128) hide show
  1. package/AGENTS.md +31 -0
  2. package/CLAUDE.md +1 -0
  3. package/Cache/Readme.md +63 -0
  4. package/Container/Readme.md +95 -0
  5. package/Database/Readme.md +139 -0
  6. package/Encryption/Readme.md +54 -0
  7. package/Forms/Readme.md +79 -0
  8. package/Framework/Readme.md +143 -0
  9. package/Frontend/Angular/Readme.md +68 -0
  10. package/Frontend/Websockets/Readme.md +96 -0
  11. package/Mail/Readme.md +99 -0
  12. package/Models/Readme.md +85 -0
  13. package/Router/Readme.md +145 -0
  14. package/Storage/Readme.md +188 -0
  15. package/Support/Readme.md +71 -0
  16. package/Testing/Readme.md +80 -0
  17. package/Validation/Readme.md +91 -0
  18. package/build/Cache/Stores/FileStore.d.ts +3 -3
  19. package/build/Cache/Stores/FileStore.d.ts.map +1 -1
  20. package/build/Cache/Stores/FileStore.js.map +1 -1
  21. package/build/Database/List.d.ts +1 -1
  22. package/build/Database/Migrations/Migrator.d.ts +3 -3
  23. package/build/Database/Migrations/Migrator.d.ts.map +1 -1
  24. package/build/Database/Migrations/Migrator.js.map +1 -1
  25. package/build/Database/Seeders/SeederManager.d.ts +3 -3
  26. package/build/Database/Seeders/SeederManager.d.ts.map +1 -1
  27. package/build/Database/Seeders/SeederManager.js +3 -3
  28. package/build/Database/Seeders/SeederManager.js.map +1 -1
  29. package/build/Framework/Application.js +1 -1
  30. package/build/Framework/Application.js.map +1 -1
  31. package/build/Framework/Config/StorageConfig.d.ts +7 -0
  32. package/build/Framework/Config/StorageConfig.d.ts.map +1 -0
  33. package/build/Framework/Config/StorageConfig.js +15 -0
  34. package/build/Framework/Config/StorageConfig.js.map +1 -0
  35. package/build/Framework/Config/index.d.ts +1 -0
  36. package/build/Framework/Config/index.d.ts.map +1 -1
  37. package/build/Framework/Config/index.js +1 -0
  38. package/build/Framework/Config/index.js.map +1 -1
  39. package/build/Framework/Middleware/StaticAssetsMiddleware.d.ts +2 -2
  40. package/build/Framework/Middleware/StaticAssetsMiddleware.d.ts.map +1 -1
  41. package/build/Framework/Middleware/StaticAssetsMiddleware.js +1 -1
  42. package/build/Framework/Middleware/StaticAssetsMiddleware.js.map +1 -1
  43. package/build/Framework/Providers/CacheProvider.js +1 -1
  44. package/build/Framework/Providers/CacheProvider.js.map +1 -1
  45. package/build/Framework/Providers/DatabaseProvider.d.ts +2 -2
  46. package/build/Framework/Providers/DatabaseProvider.d.ts.map +1 -1
  47. package/build/Framework/Providers/DatabaseProvider.js +1 -1
  48. package/build/Framework/Providers/DatabaseProvider.js.map +1 -1
  49. package/build/Framework/Providers/StorageProvider.d.ts +7 -0
  50. package/build/Framework/Providers/StorageProvider.d.ts.map +1 -0
  51. package/build/Framework/Providers/StorageProvider.js +28 -0
  52. package/build/Framework/Providers/StorageProvider.js.map +1 -0
  53. package/build/Framework/Providers/WebsocketsProvider.d.ts.map +1 -1
  54. package/build/Framework/Providers/WebsocketsProvider.js +13 -0
  55. package/build/Framework/Providers/WebsocketsProvider.js.map +1 -1
  56. package/build/Framework/index.d.ts +1 -0
  57. package/build/Framework/index.d.ts.map +1 -1
  58. package/build/Framework/index.js +1 -0
  59. package/build/Framework/index.js.map +1 -1
  60. package/build/Router/Http/Request.d.ts.map +1 -1
  61. package/build/Router/Http/Request.js +3 -4
  62. package/build/Router/Http/Request.js.map +1 -1
  63. package/build/Router/Router.js +1 -1
  64. package/build/Router/Router.js.map +1 -1
  65. package/build/Router/Servers/uNetworking.d.ts.map +1 -1
  66. package/build/Router/Servers/uNetworking.js +3 -4
  67. package/build/Router/Servers/uNetworking.js.map +1 -1
  68. package/build/Storage/Drivers/LocalDriver.d.ts +26 -0
  69. package/build/Storage/Drivers/LocalDriver.d.ts.map +1 -0
  70. package/build/Storage/Drivers/LocalDriver.js +209 -0
  71. package/build/Storage/Drivers/LocalDriver.js.map +1 -0
  72. package/build/Storage/Drivers/MemoryDriver.d.ts +25 -0
  73. package/build/Storage/Drivers/MemoryDriver.d.ts.map +1 -0
  74. package/build/Storage/Drivers/MemoryDriver.js +108 -0
  75. package/build/Storage/Drivers/MemoryDriver.js.map +1 -0
  76. package/build/Storage/Drivers/ReadOnlyDriver.d.ts +22 -0
  77. package/build/Storage/Drivers/ReadOnlyDriver.d.ts.map +1 -0
  78. package/build/Storage/Drivers/ReadOnlyDriver.js +38 -0
  79. package/build/Storage/Drivers/ReadOnlyDriver.js.map +1 -0
  80. package/build/Storage/Drivers/S3Driver.d.ts +32 -0
  81. package/build/Storage/Drivers/S3Driver.d.ts.map +1 -0
  82. package/build/Storage/Drivers/S3Driver.js +248 -0
  83. package/build/Storage/Drivers/S3Driver.js.map +1 -0
  84. package/build/Storage/Drivers/ScopedDriver.d.ts +24 -0
  85. package/build/Storage/Drivers/ScopedDriver.d.ts.map +1 -0
  86. package/build/Storage/Drivers/ScopedDriver.js +50 -0
  87. package/build/Storage/Drivers/ScopedDriver.js.map +1 -0
  88. package/build/Storage/File.d.ts +14 -4
  89. package/build/Storage/File.d.ts.map +1 -1
  90. package/build/Storage/File.js +29 -47
  91. package/build/Storage/File.js.map +1 -1
  92. package/build/Storage/Filesystem.d.ts +31 -0
  93. package/build/Storage/Filesystem.d.ts.map +1 -0
  94. package/build/Storage/Filesystem.js +121 -0
  95. package/build/Storage/Filesystem.js.map +1 -0
  96. package/build/Storage/PathPrefixer.d.ts +9 -0
  97. package/build/Storage/PathPrefixer.d.ts.map +1 -0
  98. package/build/Storage/PathPrefixer.js +53 -0
  99. package/build/Storage/PathPrefixer.js.map +1 -0
  100. package/build/Storage/Storage.d.ts +37 -9
  101. package/build/Storage/Storage.d.ts.map +1 -1
  102. package/build/Storage/Storage.js +85 -101
  103. package/build/Storage/Storage.js.map +1 -1
  104. package/build/Storage/StorageDisk.d.ts +34 -0
  105. package/build/Storage/StorageDisk.d.ts.map +1 -0
  106. package/build/Storage/StorageDisk.js +104 -0
  107. package/build/Storage/StorageDisk.js.map +1 -0
  108. package/build/Storage/StorageDriver.d.ts +19 -0
  109. package/build/Storage/StorageDriver.d.ts.map +1 -0
  110. package/build/Storage/StorageDriver.js +7 -0
  111. package/build/Storage/StorageDriver.js.map +1 -0
  112. package/build/Storage/UploadedFile.d.ts +9 -0
  113. package/build/Storage/UploadedFile.d.ts.map +1 -0
  114. package/build/Storage/UploadedFile.js +22 -0
  115. package/build/Storage/UploadedFile.js.map +1 -0
  116. package/build/Storage/errors.d.ts +21 -0
  117. package/build/Storage/errors.d.ts.map +1 -0
  118. package/build/Storage/errors.js +43 -0
  119. package/build/Storage/errors.js.map +1 -0
  120. package/build/Storage/index.d.ts +12 -0
  121. package/build/Storage/index.d.ts.map +1 -1
  122. package/build/Storage/index.js +12 -0
  123. package/build/Storage/index.js.map +1 -1
  124. package/build/Storage/types.d.ts +73 -0
  125. package/build/Storage/types.d.ts.map +1 -0
  126. package/build/Storage/types.js +3 -0
  127. package/build/Storage/types.js.map +1 -0
  128. package/package.json +35 -3
@@ -0,0 +1,96 @@
1
+ # Typetron WebSockets
2
+
3
+ - [Introduction](#introduction)
4
+ - [Installation](#installation)
5
+ - [Using the Connection](#using-the-connection)
6
+ - [Errors and Reconnection](#errors-and-reconnection)
7
+ - [Message Format](#message-format)
8
+
9
+ ## Introduction
10
+
11
+ The `@typetron/websockets` package is a browser client for Typetron WebSocket actions. It queues outgoing actions until the connection
12
+ opens, exposes responses as RxJS observables, and correlates requests by ID.
13
+
14
+ ## Installation
15
+
16
+ Install the package and RxJS:
17
+
18
+ ```bash
19
+ npm install @typetron/websockets rxjs
20
+ ```
21
+
22
+ The client requires the browser `WebSocket` API and a module-aware frontend build.
23
+
24
+ ## Using the Connection
25
+
26
+ Construct `Websocket` with the server URL. Use `on` for a response stream, `emit` for a one-way action, or `request` to await one correlated
27
+ response:
28
+
29
+ ```ts
30
+ import { Websocket } from '@typetron/websockets'
31
+
32
+ interface User {
33
+ id: number
34
+ name: string
35
+ }
36
+
37
+ const socket = new Websocket('ws://localhost:3001')
38
+
39
+ const subscription = socket.on<User>('users.updated').subscribe(user => {
40
+ console.log(user.name)
41
+ })
42
+
43
+ socket.emit('users.save', {
44
+ body: {name: 'John'},
45
+ })
46
+
47
+ const user = await socket.request<User>('users.save', {
48
+ body: {name: 'John'},
49
+ })
50
+
51
+ socket.onError({except: ['health.check']}).subscribe(response => {
52
+ console.error(response.action, response.message)
53
+ })
54
+
55
+ socket.onConnect(() => console.log('connected'))
56
+ socket.onDisconnect(() => console.log('disconnected'))
57
+ ```
58
+
59
+ The optional message may contain `body` and `parameters`. Actions emitted before the connection opens remain queued. `request` resolves with
60
+ the response's `message`, while an error response rejects it. Unsubscribe from RxJS listeners when they are no longer needed.
61
+
62
+ ## Errors and Reconnection
63
+
64
+ Action errors reach the corresponding listener and the shared `onError` stream. The optional `except` list suppresses action names from that
65
+ shared stream.
66
+
67
+ When the browser socket closes, the client invokes the disconnect callback, resets its outgoing queue, and attempts to reconnect after one
68
+ second. `isConnected` is `true` only while the underlying socket is open.
69
+
70
+ ## Message Format
71
+
72
+ Actions sent to the server use this shape:
73
+
74
+ ```json
75
+ {
76
+ "$id": "request-id",
77
+ "action": "users.save",
78
+ "message": {
79
+ "body": {"name": "John"},
80
+ "parameters": [1]
81
+ }
82
+ }
83
+ ```
84
+
85
+ Responses use the same ID and action name:
86
+
87
+ ```json
88
+ {
89
+ "$id": "request-id",
90
+ "action": "users.save",
91
+ "status": "OK",
92
+ "message": {"id": 1, "name": "John"}
93
+ }
94
+ ```
95
+
96
+ The status is `OK` or `Error`.
package/Mail/Readme.md ADDED
@@ -0,0 +1,99 @@
1
+ # Mail
2
+
3
+ - [Introduction](#introduction)
4
+ - [Configuration](#configuration)
5
+ - [Defining Mailables](#defining-mailables)
6
+ - [Sending Mail](#sending-mail)
7
+ - [Recipients and Message Details](#recipients-and-message-details)
8
+ - [Transports and Testing](#transports-and-testing)
9
+
10
+ ## Introduction
11
+
12
+ Typetron provides a fluent API for building and sending email. A `Mailer` collects recipients and message details, while a transport
13
+ performs the delivery.
14
+
15
+ ## Configuration
16
+
17
+ Applications normally register `MailProvider` and configure `MailConfig`. The `default` mailer may be `memory` or `SendGrid`, and `from`
18
+ supplies the default sender.
19
+
20
+ ```ts
21
+ import { MailConfig } from '@Typetron/Framework'
22
+
23
+ export default new MailConfig({
24
+ default: 'memory',
25
+ from: {
26
+ email: 'hello@example.com',
27
+ name: 'Typetron',
28
+ },
29
+ })
30
+ ```
31
+
32
+ The SendGrid transport also requires `mailers.SendGrid.key`. Constructing `Mailer` without a transport throws an error instructing the
33
+ application to register `MailProvider` or bind a mailer manually.
34
+
35
+ ## Defining Mailables
36
+
37
+ Represent a reusable message by extending `Mailable` and implementing `content`:
38
+
39
+ ```ts
40
+ import { Mailable } from '@Typetron/Mail'
41
+
42
+ class OrderShipped extends Mailable {
43
+ constructor(public orderId: number) {
44
+ super()
45
+ }
46
+
47
+ content() {
48
+ return {
49
+ html: `<h1>Order #${this.orderId} shipped</h1>`,
50
+ text: `Order #${this.orderId} shipped`,
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ `content` may return a string or an object containing both `html` and `text`.
57
+
58
+ ## Sending Mail
59
+
60
+ Begin a message with `Mailer.to`, configure it, and call `send`:
61
+
62
+ ```ts
63
+ import { Mailer, MemoryTransport } from '@Typetron/Mail'
64
+
65
+ const transport = new MemoryTransport()
66
+ const mail = new Mailer({email: 'hello@example.com'}, transport)
67
+
68
+ const sent = await mail
69
+ .to('john@example.com')
70
+ .subject('Your order has shipped')
71
+ .send(new OrderShipped(1234))
72
+ ```
73
+
74
+ `send` resolves to the transport's `SentMessage` after the transport accepts the message.
75
+
76
+ ## Recipients and Message Details
77
+
78
+ The pending message supports `from`, `to`, `replyTo`, `cc`, `bcc`, and `subject`. A plain string or `{html, text}` object may be sent
79
+ instead of a custom mailable:
80
+
81
+ ```ts
82
+ await mail
83
+ .to('john@example.com')
84
+ .replyTo('support@example.com')
85
+ .cc('manager@example.com')
86
+ .send('Welcome to Typetron')
87
+ ```
88
+
89
+ ## Transports and Testing
90
+
91
+ `MemoryTransport` stores sent messages in its public `messages` array and is suitable for tests and local development. `SendGridTransport`
92
+ sends through `@sendgrid/mail` and converts SendGrid failures into an HTTP error.
93
+
94
+ ```ts
95
+ const [message] = transport.messages
96
+
97
+ console.log(message.to)
98
+ console.log(message.body)
99
+ ```
@@ -0,0 +1,85 @@
1
+ # Models
2
+
3
+ - [Introduction](#introduction)
4
+ - [Defining Models](#defining-models)
5
+ - [Transforming Values](#transforming-values)
6
+ - [Nested Models](#nested-models)
7
+ - [Transforming Collections](#transforming-collections)
8
+
9
+ ## Introduction
10
+
11
+ Models create explicit data projections. They are useful for returning only selected fields from entities or plain objects without modifying
12
+ the original value.
13
+
14
+ ## Defining Models
15
+
16
+ Extend `Model` and mark every exposed property with `@Field`:
17
+
18
+ ```ts
19
+ import { Field, Model } from '@Typetron/Models'
20
+
21
+ class UserModel extends Model {
22
+ @Field()
23
+ id: number
24
+
25
+ @Field()
26
+ name: string
27
+ }
28
+ ```
29
+
30
+ Properties without `@Field` are omitted. Pass a source name to `@Field('user_name')` when the model property reads from a differently named
31
+ input field.
32
+
33
+ ## Transforming Values
34
+
35
+ Call the model's static `from` method with an object:
36
+
37
+ ```ts
38
+ const user = UserModel.from({
39
+ id: 1,
40
+ name: 'John',
41
+ password: 'secret',
42
+ })
43
+ ```
44
+
45
+ The result contains `id` and `name`; `password` is not included.
46
+
47
+ ## Nested Models
48
+
49
+ A property typed as another model is transformed recursively. Use `@FieldMany` for a collection:
50
+
51
+ ```ts
52
+ import { Field, FieldMany, Model } from '@Typetron/Models'
53
+
54
+ class PostModel extends Model {
55
+ @Field()
56
+ title: string
57
+ }
58
+
59
+ class AuthorModel extends Model {
60
+ @Field()
61
+ name: string
62
+
63
+ @Field()
64
+ @FieldMany(PostModel)
65
+ posts: PostModel[]
66
+ }
67
+ ```
68
+
69
+ Nested values with a `toJSON` method are serialized before they are projected.
70
+
71
+ ## Transforming Collections
72
+
73
+ `Model.from` accepts a single object, any iterable of objects, or a promise resolving to either form:
74
+
75
+ ```ts
76
+ const users = UserModel.from([
77
+ {id: 1, name: 'John'},
78
+ {id: 2, name: 'Jane'},
79
+ ])
80
+
81
+ const promisedUser = await UserModel.from(Promise.resolve({
82
+ id: 3,
83
+ name: 'Alex',
84
+ }))
85
+ ```
@@ -0,0 +1,145 @@
1
+ # Router
2
+
3
+ - [Introduction](#introduction)
4
+ - [HTTP Controllers](#http-controllers)
5
+ - [Request Data and Responses](#request-data-and-responses)
6
+ - [Middleware and Guards](#middleware-and-guards)
7
+ - [WebSocket Actions](#websocket-actions)
8
+ - [Loading Controllers and Starting Servers](#loading-controllers-and-starting-servers)
9
+
10
+ ## Introduction
11
+
12
+ The Router maps HTTP routes and WebSocket action names to controller methods. Both transports resolve controllers and typed parameters
13
+ through the service container, and both support middleware and guards.
14
+
15
+ ## HTTP Controllers
16
+
17
+ Decorate a class with `@Controller` and methods with `@Get`, `@Post`, `@Put`, `@Patch`, or `@Delete`:
18
+
19
+ ```ts
20
+ import {
21
+ Body,
22
+ Controller,
23
+ Get,
24
+ Post,
25
+ Query,
26
+ } from '@Typetron/Router'
27
+ import { Http, Response } from '@Typetron/Router/Http'
28
+ import { User } from '../Entities/User'
29
+
30
+ @Controller('users')
31
+ class UserController {
32
+ @Get('', 'index')
33
+ index(@Query('active') active?: string) {
34
+ return User.where('active', active === 'true').get()
35
+ }
36
+
37
+ @Get(':User', 'read')
38
+ read(user: User) {
39
+ return user
40
+ }
41
+
42
+ @Post('', 'create')
43
+ async create(@Body() data: {name: string}) {
44
+ const user = await User.create(data)
45
+ return new Response(user, Http.Status.CREATED)
46
+ }
47
+ }
48
+ ```
49
+
50
+ These routes are named `users.index`, `users.read`, and `users.create`. Without an explicit method name, the controller method name is used.
51
+ Two routes may share a path when their HTTP methods differ; registering the same path and method twice throws an error.
52
+
53
+ ## Request Data and Responses
54
+
55
+ Prefix a path segment with `:` to capture it. Primitive `string` and `number` parameters are resolved in route order. `@Body` returns the
56
+ body, while `@Query('name')` reads one query value and converts it to a number when the decorated parameter is typed as `number`.
57
+
58
+ An entity parameter is loaded when the route parameter uses its class name, such as `:User` above. The Framework resolver calls `User.find`
59
+ and throws `EntityNotFoundError` when no row exists.
60
+
61
+ Plain controller return values become a status `200` `Response`. Return `Response` directly to control status and headers; convenience
62
+ constructors include `ok`, `notFound`, and `badRequest`.
63
+
64
+ ## Middleware and Guards
65
+
66
+ Middleware surrounds the action, while guards decide whether resolved parameters may reach it:
67
+
68
+ ```ts
69
+ import {
70
+ AllowIf,
71
+ Controller,
72
+ Get,
73
+ Middleware,
74
+ RequestHandler,
75
+ } from '@Typetron/Router'
76
+ import { Guard } from '@Typetron/Router/Guard'
77
+ import { Request } from '@Typetron/Router/Http'
78
+ import { HttpMiddleware } from '@Typetron/Router/Http/Middleware'
79
+ import { User } from '../Entities/User'
80
+
81
+ class ResponseHeader extends HttpMiddleware {
82
+ async handle(request: Request, next: RequestHandler) {
83
+ const response = await next(request)
84
+ response.setHeader('x-powered-by', 'Typetron')
85
+ return response
86
+ }
87
+ }
88
+
89
+ class CanReadUser extends Guard {
90
+ async condition(user: User) {
91
+ return user.id > 0
92
+ }
93
+ }
94
+
95
+ @Controller('users')
96
+ @Middleware(ResponseHeader)
97
+ class UserController {
98
+ @Get(':User', 'read')
99
+ @AllowIf(CanReadUser)
100
+ read(user: User) {
101
+ return user
102
+ }
103
+ }
104
+ ```
105
+
106
+ Apply `@Middleware` and `@AllowIf` to a controller or method. The default `Guard.onFail` throws HTTP `401 Unauthorized`; override it when
107
+ another failure is required. Transport-specific middleware extends `HttpMiddleware` or `WebsocketMiddleware`, while `GlobalMiddleware` may
108
+ wrap both.
109
+
110
+ ## WebSocket Actions
111
+
112
+ Use `@Action` for controller methods called by an action name:
113
+
114
+ ```ts
115
+ import { Action, Body, Controller, OnClose, OnOpen } from '@Typetron/Router'
116
+
117
+ @Controller('users')
118
+ class UserActions {
119
+ @Action('save')
120
+ save(@Body() data: {name: string}) {
121
+ return {id: 1, ...data}
122
+ }
123
+
124
+ @OnOpen
125
+ opened() {}
126
+
127
+ @OnClose
128
+ closed() {}
129
+ }
130
+ ```
131
+
132
+ The action is registered as `users.save`. Results use the same `Response` wrapper as HTTP routes. Duplicate action names throw during
133
+ registration; `@OnOpen` and `@OnClose` register optional connection lifecycle handlers.
134
+
135
+ Messages sent or published through the injected `WebSocket` while handling an action retain the incoming request's `$id`. This allows
136
+ clients to correlate every streamed message with the action that initiated it, including when multiple actions run concurrently on one
137
+ connection. Messages initiated outside an action may omit `$id`.
138
+
139
+ ## Loading Controllers and Starting Servers
140
+
141
+ `Router.loadControllers(directory)` imports TypeScript controller files recursively. Importing decorated classes registers their routes and
142
+ actions.
143
+
144
+ The Framework starts the Node or uWebSockets HTTP server selected by `AppConfig.server`. Configuring `websocketsPort` starts the separate
145
+ WebSocket provider.
@@ -0,0 +1,188 @@
1
+ # Storage
2
+
3
+ - [Introduction](#introduction)
4
+ - [Configuring Disks](#configuring-disks)
5
+ - [Using Disks](#using-disks)
6
+ - [Writing and Reading](#writing-and-reading)
7
+ - [Listing Files](#listing-files)
8
+ - [Uploads and Metadata](#uploads-and-metadata)
9
+ - [URLs and Visibility](#urls-and-visibility)
10
+ - [Scoped and Read-Only Disks](#scoped-and-read-only-disks)
11
+ - [Testing](#testing)
12
+ - [Custom Drivers](#custom-drivers)
13
+ - [Framework Filesystem](#framework-filesystem)
14
+ - [Image Data](#image-data)
15
+
16
+ ## Introduction
17
+
18
+ Storage provides one asynchronous API for local files, AWS S3, and S3-compatible object stores. Applications work with logical keys relative
19
+ to a configured disk rather than operating-system paths. `Storage` resolves named disks lazily and forwards methods to the default disk.
20
+
21
+ Each disk wraps a `StorageDriver`. Drivers translate portable operations to a backend while `StorageDisk` supplies shared conveniences such
22
+ as uploads, JSON decoding, URL generation, and testing assertions.
23
+
24
+ ## Configuring Disks
25
+
26
+ ```ts
27
+ import { StorageConfig } from '@Typetron/Framework'
28
+
29
+ export default new StorageConfig({
30
+ default: 'local',
31
+ disks: {
32
+ local: {
33
+ driver: 'local',
34
+ root: 'storage/app',
35
+ url: '/storage',
36
+ },
37
+ s3: {
38
+ driver: 's3',
39
+ bucket: process.env.AWS_BUCKET!,
40
+ region: process.env.AWS_REGION!,
41
+ },
42
+ objects: {
43
+ driver: 's3',
44
+ bucket: process.env.OBJECT_BUCKET!,
45
+ region: process.env.OBJECT_REGION!,
46
+ endpoint: process.env.OBJECT_ENDPOINT,
47
+ forcePathStyle: true,
48
+ credentials: {
49
+ accessKeyId: process.env.OBJECT_ACCESS_KEY!,
50
+ secretAccessKey: process.env.OBJECT_SECRET_KEY!,
51
+ },
52
+ },
53
+ },
54
+ })
55
+ ```
56
+
57
+ AWS disks use the SDK's default credential chain when `credentials` is omitted. The S3 driver is loaded only when an S3 disk is resolved;
58
+ applications using it must install `@aws-sdk/client-s3` and `@aws-sdk/s3-request-presigner`.
59
+
60
+ ## Using Disks
61
+
62
+ Register `StorageProvider`, then inject or resolve `Storage`. Methods on `Storage` target the default disk; `disk` selects another
63
+ configured
64
+ disk. `build` creates an uncached on-demand disk.
65
+
66
+ ```ts
67
+ await storage.put('users/1.txt', 'John')
68
+ await storage.disk('s3').put('backups/data.json', '{}')
69
+
70
+ const temporary = storage.build({driver: 'local', root: '/tmp/exports'})
71
+ ```
72
+
73
+ Named disks are constructed once and cached. Use `forget(name)` to discard a cached disk so its next lookup rebuilds it.
74
+
75
+ ## Writing and Reading
76
+
77
+ `put` accepts a string, `Buffer`, or Node `Readable` and returns a lightweight `File`. `read` returns a `Buffer`; use `readStream` for large
78
+ objects and `json` for JSON content.
79
+
80
+ ```ts
81
+ const file = await storage.put('users/1.json', JSON.stringify({name: 'John'}), {
82
+ mimeType: 'application/json',
83
+ visibility: 'private',
84
+ })
85
+
86
+ const user = await storage.json<{name: string}>(file.key)
87
+ const stream = await storage.readStream(file.key)
88
+ ```
89
+
90
+ Use `copy` and `move` within one disk. Moving copies first and deletes the source only after a successful copy. Cross-disk copies are not
91
+ implicit; stream the source into the destination when that behavior is required.
92
+
93
+ ## Listing Files
94
+
95
+ `list` is an `AsyncIterable`, so paginated object-store listings do not have to be retained in memory. `files` collects file entries into
96
+ `File` instances.
97
+
98
+ ```ts
99
+ for await (const entry of storage.list('users', {recursive: true})) {
100
+ console.log(entry.type, entry.key)
101
+ }
102
+
103
+ const files = await storage.files('users', {recursive: true})
104
+ ```
105
+
106
+ Directories are logical prefixes. Writing `users/1/avatar.jpg` creates required local directories, while S3 stores only the object key.
107
+ `deleteDirectory('users/1')` removes that non-empty prefix recursively; an empty prefix is rejected.
108
+
109
+ ## Uploads and Metadata
110
+
111
+ HTTP multipart parsing produces `UploadedFile` instances backed by temporary local files. `putFile` streams the upload to its destination.
112
+
113
+ ```ts
114
+ const avatar = request.files.avatar as UploadedFile
115
+ await storage.disk('s3').putFile(`avatars/${user.id}.jpg`, avatar)
116
+ ```
117
+
118
+ Use `stat` for size, last modification, MIME type, ETag, and backend metadata. A `File` may contain metadata already returned by a listing
119
+ or
120
+ write, but Storage does not issue an extra metadata request merely to fill every optional field.
121
+
122
+ ## URLs and Visibility
123
+
124
+ `url` returns a public URL when the disk supports or configures one. `temporaryUrl` creates an expiring S3 URL. Unsupported operations throw
125
+ `UnsupportedStorageOperation`.
126
+
127
+ ```ts
128
+ const publicUrl = storage.disk('s3').url('avatars/1.jpg')
129
+ const privateUrl = await storage.disk('s3').temporaryUrl('reports/1.pdf', 300)
130
+ await storage.setVisibility('avatars/1.jpg', 'public')
131
+ ```
132
+
133
+ Visibility is `public` or `private`. Local disks map it to file permissions; S3 maps it to object ACLs. Bucket policies may still forbid
134
+ public ACLs.
135
+
136
+ ## Scoped and Read-Only Disks
137
+
138
+ Composition drivers wrap any named disk without changing its implementation:
139
+
140
+ ```
141
+ disks: {
142
+ s3: {driver: 's3', bucket: 'app', region: 'eu-central-1'},
143
+ avatars: {driver: 'scoped', disk: 's3', prefix: 'users/avatars'},
144
+ assets: {driver: 'read-only', disk: 's3'},
145
+ }
146
+ ```
147
+
148
+ A scoped disk exposes only keys below its prefix. Mutating a read-only disk throws `ReadOnlyStorage`.
149
+
150
+ ## Testing
151
+
152
+ `fake` replaces a named disk with an isolated in-memory driver:
153
+
154
+ ```ts
155
+ const avatars = storage.fake('avatars')
156
+ await service.uploadAvatar(upload)
157
+ await avatars.assertExists('users/1.jpg')
158
+ await avatars.assertCount('users', 1, true)
159
+ ```
160
+
161
+ ## Custom Drivers
162
+
163
+ Register a driver factory with `extend`. The factory receives its disk configuration, name, and Storage manager and returns a
164
+ `StorageDriver`. Drivers implement backend translation only; shared application conveniences belong on `StorageDisk`.
165
+
166
+ ## Framework Filesystem
167
+
168
+ `Filesystem` is a separate local operating-system service used by Typetron to load configuration, controllers, entities, migrations,
169
+ seeders, static assets, and file cache data. It is available before `StorageConfig` is loaded, accepts real host paths, and keeps these
170
+ framework paths independent of the application's default object-storage disk.
171
+
172
+ `Filesystem` is framework-internal and is not the local-disk API intended for applications. Application code should use `Storage` or a
173
+ named disk such as `storage.disk('local')`. Its synchronous `files` method exists only because framework modules are currently discovered
174
+ synchronously during bootstrap; configured storage listings remain asynchronous and backend-neutral.
175
+
176
+ ## Image Data
177
+
178
+ The `fromBase64` helper decodes an image data URL:
179
+
180
+ ```ts
181
+ import { fromBase64 } from '@Typetron/Storage'
182
+
183
+ const image = fromBase64('data:image/png;base64,iVBORw0KGgo=')
184
+
185
+ await storage.put(`storage/avatar.${image.extension}`, image.content)
186
+ ```
187
+
188
+ Invalid image data throws `InvalidImage` with the message `The image has an invalid data format`.
@@ -0,0 +1,71 @@
1
+ # Support
2
+
3
+ - [Introduction](#introduction)
4
+ - [Array Helpers](#array-helpers)
5
+ - [String Helpers](#string-helpers)
6
+ - [Number Helpers](#number-helpers)
7
+ - [JSON Parsing](#json-parsing)
8
+ - [Shared Types](#shared-types)
9
+
10
+ ## Introduction
11
+
12
+ The Support module contains shared TypeScript types and lightweight helpers used throughout Typetron. Importing `@Typetron/Support` also
13
+ installs the framework's array, string, and math prototype extensions.
14
+
15
+ ## Array Helpers
16
+
17
+ Arrays gain collection-style operations for common filtering and transformation tasks:
18
+
19
+ ```ts
20
+ import '@Typetron/Support'
21
+
22
+ const users = [
23
+ {id: 1, name: 'John', active: true},
24
+ {id: 2, name: 'Jane', active: false},
25
+ ]
26
+
27
+ const names = users.where('active', true).pluck('name')
28
+ const byId = users.groupBy('id')
29
+ ```
30
+
31
+ Other helpers include `first`, `last`, `findWhere`, `whereIn`, `whereNotIn`, `sortBy`, `unique`, `sum`, `mapAsync`, and `forEachAsync`.
32
+ Methods such as `sortBy` and `remove` mutate the original array; filtering and mapping helpers return new arrays.
33
+
34
+ ## String Helpers
35
+
36
+ Strings may be capitalized or limited, and the `String` constructor can create random values:
37
+
38
+ ```ts
39
+ const title = 'typetron'.capitalize()
40
+ const excerpt = 'A longer description'.limit(10)
41
+ const token = String.randomAlphaNum(24)
42
+ ```
43
+
44
+ `limit` uses `...` by default and counts that ending inside the requested length.
45
+
46
+ ## Number Helpers
47
+
48
+ `Math.randomInt(min, max)` returns an integer including both boundaries:
49
+
50
+ ```ts
51
+ const dice = Math.randomInt(1, 6)
52
+ ```
53
+
54
+ ## JSON Parsing
55
+
56
+ `JSONSafeParse` returns `undefined` instead of throwing when input is empty or invalid:
57
+
58
+ ```ts
59
+ import { JSONSafeParse } from '@Typetron/Support/utils'
60
+
61
+ const settings = JSONSafeParse<{theme: string}>('{"theme":"dark"}')
62
+ const invalid = JSONSafeParse('{')
63
+ ```
64
+
65
+ ## Shared Types
66
+
67
+ The module exports reusable types including `Type`, `Constructor`, `Abstract`, `AnyFunction`, `KeysOfType`, `ChildKeys`, and `ChildObject`.
68
+ These types describe constructors and derive child-specific keys or objects without adding runtime behavior.
69
+
70
+ > Note: Prototype helpers are global after the Support module is imported. Check for an existing helper before adding another extension with
71
+ > the same name.