@jolly-pixel/asset-server 1.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.
- package/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/catalog/CatalogExtension.d.ts +37 -0
- package/dist/catalog/CatalogExtension.d.ts.map +1 -0
- package/dist/catalog/CatalogExtension.js +82 -0
- package/dist/catalog/CatalogIdentitySidecar.d.ts +52 -0
- package/dist/catalog/CatalogIdentitySidecar.d.ts.map +1 -0
- package/dist/catalog/CatalogIdentitySidecar.js +132 -0
- package/dist/catalog/CatalogProjection.d.ts +32 -0
- package/dist/catalog/CatalogProjection.d.ts.map +1 -0
- package/dist/catalog/CatalogProjection.js +109 -0
- package/dist/catalog/httpHandler.d.ts +13 -0
- package/dist/catalog/httpHandler.d.ts.map +1 -0
- package/dist/catalog/httpHandler.js +27 -0
- package/dist/catalog/index.d.ts +9 -0
- package/dist/catalog/index.d.ts.map +1 -0
- package/dist/catalog/index.js +4 -0
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +13 -0
- package/dist/createAssetBackend.d.ts +79 -0
- package/dist/createAssetBackend.d.ts.map +1 -0
- package/dist/createAssetBackend.js +146 -0
- package/dist/events/AssetEvents.d.ts +54 -0
- package/dist/events/AssetEvents.d.ts.map +1 -0
- package/dist/events/AssetEvents.js +102 -0
- package/dist/events/AssetEvents.schema.d.ts +111 -0
- package/dist/events/AssetEvents.schema.d.ts.map +1 -0
- package/dist/events/AssetEvents.schema.js +75 -0
- package/dist/events/index.d.ts +3 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/kinds/AssetKindHandler.d.ts +38 -0
- package/dist/kinds/AssetKindHandler.d.ts.map +1 -0
- package/dist/kinds/AssetKindHandler.js +1 -0
- package/dist/kinds/AssetKindRegistry.d.ts +14 -0
- package/dist/kinds/AssetKindRegistry.d.ts.map +1 -0
- package/dist/kinds/AssetKindRegistry.js +52 -0
- package/dist/kinds/errors/UnknownAssetKindError.d.ts +5 -0
- package/dist/kinds/errors/UnknownAssetKindError.d.ts.map +1 -0
- package/dist/kinds/errors/UnknownAssetKindError.js +8 -0
- package/dist/kinds/errors/index.d.ts +2 -0
- package/dist/kinds/errors/index.d.ts.map +1 -0
- package/dist/kinds/errors/index.js +1 -0
- package/dist/kinds/handlers/binary.d.ts +10 -0
- package/dist/kinds/handlers/binary.d.ts.map +1 -0
- package/dist/kinds/handlers/binary.js +31 -0
- package/dist/kinds/handlers/index.d.ts +5 -0
- package/dist/kinds/handlers/index.d.ts.map +1 -0
- package/dist/kinds/handlers/index.js +2 -0
- package/dist/kinds/handlers/texture.d.ts +12 -0
- package/dist/kinds/handlers/texture.d.ts.map +1 -0
- package/dist/kinds/handlers/texture.js +27 -0
- package/dist/kinds/index.d.ts +5 -0
- package/dist/kinds/index.d.ts.map +1 -0
- package/dist/kinds/index.js +3 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +9 -0
- package/dist/plugins/vite.d.ts +36 -0
- package/dist/plugins/vite.d.ts.map +1 -0
- package/dist/plugins/vite.js +60 -0
- package/dist/rooms/AssetRoomExtension.d.ts +29 -0
- package/dist/rooms/AssetRoomExtension.d.ts.map +1 -0
- package/dist/rooms/AssetRoomExtension.js +50 -0
- package/dist/rooms/index.d.ts +5 -0
- package/dist/rooms/index.d.ts.map +1 -0
- package/dist/rooms/index.js +2 -0
- package/dist/rooms/registerAssetRooms.d.ts +21 -0
- package/dist/rooms/registerAssetRooms.d.ts.map +1 -0
- package/dist/rooms/registerAssetRooms.js +71 -0
- package/dist/static/contentTypes.d.ts +6 -0
- package/dist/static/contentTypes.d.ts.map +1 -0
- package/dist/static/contentTypes.js +27 -0
- package/dist/static/httpHandler.d.ts +24 -0
- package/dist/static/httpHandler.d.ts.map +1 -0
- package/dist/static/httpHandler.js +105 -0
- package/dist/static/index.d.ts +4 -0
- package/dist/static/index.d.ts.map +1 -0
- package/dist/static/index.js +2 -0
- package/dist/sync/AssetProjector.d.ts +33 -0
- package/dist/sync/AssetProjector.d.ts.map +1 -0
- package/dist/sync/AssetProjector.js +186 -0
- package/dist/sync/AssetStateStore.d.ts +28 -0
- package/dist/sync/AssetStateStore.d.ts.map +1 -0
- package/dist/sync/AssetStateStore.js +86 -0
- package/dist/sync/AssetWriter.d.ts +54 -0
- package/dist/sync/AssetWriter.d.ts.map +1 -0
- package/dist/sync/AssetWriter.js +149 -0
- package/dist/sync/ProjectionState.d.ts +33 -0
- package/dist/sync/ProjectionState.d.ts.map +1 -0
- package/dist/sync/ProjectionState.js +129 -0
- package/dist/sync/Reconciler.d.ts +36 -0
- package/dist/sync/Reconciler.d.ts.map +1 -0
- package/dist/sync/Reconciler.js +182 -0
- package/dist/sync/ReconciliationWatcher.d.ts +28 -0
- package/dist/sync/ReconciliationWatcher.d.ts.map +1 -0
- package/dist/sync/ReconciliationWatcher.js +86 -0
- package/dist/sync/SnapshotScheduler.d.ts +29 -0
- package/dist/sync/SnapshotScheduler.d.ts.map +1 -0
- package/dist/sync/SnapshotScheduler.js +136 -0
- package/dist/sync/foldProjection.d.ts +12 -0
- package/dist/sync/foldProjection.d.ts.map +1 -0
- package/dist/sync/foldProjection.js +32 -0
- package/dist/sync/index.d.ts +19 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +9 -0
- package/dist/sync/matchRenames.d.ts +45 -0
- package/dist/sync/matchRenames.d.ts.map +1 -0
- package/dist/sync/matchRenames.js +85 -0
- package/dist/utils/TaskChain.d.ts +9 -0
- package/dist/utils/TaskChain.d.ts.map +1 -0
- package/dist/utils/TaskChain.js +14 -0
- package/dist/utils/contentHash.d.ts +2 -0
- package/dist/utils/contentHash.d.ts.map +1 -0
- package/dist/utils/contentHash.js +7 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +2 -0
- package/dist/workspace/createAssetWorkspace.d.ts +71 -0
- package/dist/workspace/createAssetWorkspace.d.ts.map +1 -0
- package/dist/workspace/createAssetWorkspace.js +72 -0
- package/dist/workspace/index.d.ts +5 -0
- package/dist/workspace/index.d.ts.map +1 -0
- package/dist/workspace/index.js +2 -0
- package/dist/workspace/seedAssetSource.d.ts +5 -0
- package/dist/workspace/seedAssetSource.d.ts.map +1 -0
- package/dist/workspace/seedAssetSource.js +20 -0
- package/docs/AssetBackend.md +73 -0
- package/docs/AssetKinds.md +204 -0
- package/docs/AssetWriter.md +62 -0
- package/docs/Catalog.md +71 -0
- package/docs/Rooms.md +70 -0
- package/docs/Sync.md +146 -0
- package/docs/Workspace.md +172 -0
- package/package.json +83 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# Workspace
|
|
2
|
+
|
|
3
|
+
`createAssetWorkspace` assembles what a host needs to edit an asset
|
|
4
|
+
workspace live: a source, an event log, the back-end, and the network server
|
|
5
|
+
its rooms are attached to.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
createAssetWorkspace(options: AssetWorkspaceOptions): Promise<AssetWorkspace>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
await using workspace = await createAssetWorkspace({
|
|
13
|
+
root: "./assets",
|
|
14
|
+
handlers: [textureAssetHandler()],
|
|
15
|
+
seed: {
|
|
16
|
+
"textures/block.png": () => defaultTextureBytes()
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Options
|
|
22
|
+
|
|
23
|
+
| Option | Default | Description |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `root` | required | Filesystem root, behind the default source and event log. |
|
|
26
|
+
| `handlers` | `[]` | Asset kind handlers. Unmatched paths use `binary`. |
|
|
27
|
+
| `seed` | none | Starter documents, written only where the workspace holds no file. |
|
|
28
|
+
| `source` | `FilesystemAssetSource(root)` | Physical storage. |
|
|
29
|
+
| `eventStore` | sqlite in the state directory | Event log. |
|
|
30
|
+
| `server` | a new `Server` | Network server hosting the rooms. |
|
|
31
|
+
| `extensions` | `[]` | Extensions registered before the asset rooms attach. |
|
|
32
|
+
| `rights` | none | Rights map for the server it builds. |
|
|
33
|
+
| `roomGraceMs` | server default | Grace period before an empty room is evicted. |
|
|
34
|
+
| `compactOnOpen` | `true` | Drop the events superseded by each asset's newest checkpoint. |
|
|
35
|
+
| `logger` | silent | A `loglayer` logger. Left unset, the server keeps its own. |
|
|
36
|
+
| `backend` | `{}` | Extra [`AssetBackend`](./AssetBackend.md) options. |
|
|
37
|
+
|
|
38
|
+
The workspace owns only what it creates: a source, event store or server
|
|
39
|
+
passed in is left open by `close()`.
|
|
40
|
+
|
|
41
|
+
Seeding runs before the back-end starts, so the first reconciliation catalogs
|
|
42
|
+
the starter documents.
|
|
43
|
+
|
|
44
|
+
## Compaction
|
|
45
|
+
|
|
46
|
+
Every `asset.created` and `asset.updated` event carries the whole document
|
|
47
|
+
inline, so a log that keeps them all grows with each edit and startup slows
|
|
48
|
+
with it. Opening a workspace therefore compacts the log first, dropping the
|
|
49
|
+
events stored before each asset's newest `asset.created`, `asset.updated` or
|
|
50
|
+
`asset.deleted`. Nothing reads below that point: both projections load from
|
|
51
|
+
it, and so does state replay.
|
|
52
|
+
|
|
53
|
+
> [!WARNING]
|
|
54
|
+
> Compaction is destructive and irreversible. It discards the editing history
|
|
55
|
+
> in exchange for a log that stops growing without bound. Pass
|
|
56
|
+
> `compactOnOpen: false` to keep it.
|
|
57
|
+
|
|
58
|
+
It runs before the back-end, so the projections never read the superseded
|
|
59
|
+
events and the reclaimed file is the one the back-end opens. See
|
|
60
|
+
[`EventStore compaction`](../../event-store/docs/EventStore.md#compaction).
|
|
61
|
+
|
|
62
|
+
## Event log
|
|
63
|
+
|
|
64
|
+
Without an `eventStore` option the workspace opens a sqlite log at
|
|
65
|
+
`.jollypixel/events.db` under the root, and the backend creates the directories
|
|
66
|
+
it needs. Pass `eventStore` to bring your own, in which case `close()` leaves
|
|
67
|
+
it open.
|
|
68
|
+
|
|
69
|
+
## Seeding
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
seedAssetSource(source: AssetSource, seed: AssetSeedMap): Promise<string[]>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Writes each starter document whose path the source does not hold, and returns
|
|
76
|
+
the paths written. An existing file is never overwritten and its factory is
|
|
77
|
+
never called: once the workspace exists it is the source of truth.
|
|
78
|
+
|
|
79
|
+
## Serving the workspace
|
|
80
|
+
|
|
81
|
+
The catalog hands the browser workspace-relative `source` paths, which have to
|
|
82
|
+
resolve to something.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { createAssetStaticHandler } from "@jolly-pixel/asset-server/static";
|
|
86
|
+
|
|
87
|
+
const handler = createAssetStaticHandler({
|
|
88
|
+
source: workspace.source,
|
|
89
|
+
kinds: workspace.backend.kinds
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Requests outside the prefix are passed to `next()`. `GET` and `HEAD` answer
|
|
94
|
+
`200` from the source, other methods `405`. Reads go through the
|
|
95
|
+
`AssetSource`, so an in-memory workspace is servable too.
|
|
96
|
+
|
|
97
|
+
The request target is stripped of its query and fragment, decoded once, then
|
|
98
|
+
validated by
|
|
99
|
+
[`safeAssetPath`](../../asset-source/docs/AssetSource.md#paths), so the source
|
|
100
|
+
only ever sees a root-relative POSIX path. The rejection decides the status:
|
|
101
|
+
|
|
102
|
+
| Case | Status |
|
|
103
|
+
|---|---|
|
|
104
|
+
| Absolute, drive-qualified or `..` path, source refusing the path | `403` |
|
|
105
|
+
| Malformed escape sequence, control character in the path | `400` |
|
|
106
|
+
| Missing file, directory target, state directory, path the source ignores | `404` |
|
|
107
|
+
|
|
108
|
+
The state directory is matched case-insensitively, because a
|
|
109
|
+
case-insensitive filesystem answers `.JOLLYPIXEL/state.json` from
|
|
110
|
+
`.jollypixel/`. Paths a source hides through `isIgnored` (`.git/`,
|
|
111
|
+
`node_modules/`, `dist/` by default) answer `404` as well, so the handler
|
|
112
|
+
never serves what listing and reconciliation deliberately skip.
|
|
113
|
+
|
|
114
|
+
| Option | Default | Description |
|
|
115
|
+
|---|---|---|
|
|
116
|
+
| `source` | required | Workspace the bytes are read from. |
|
|
117
|
+
| `prefix` | `/assets/` | URL prefix, with a trailing slash added when missing. |
|
|
118
|
+
| `kinds` | none | Registry contributing content types per claimed extension. |
|
|
119
|
+
| `contentTypes` | none | Extension-to-content-type entries, winning over the kinds. |
|
|
120
|
+
|
|
121
|
+
Content types come from [what each kind declares](./AssetKinds.md#content-types)
|
|
122
|
+
merged over a small default table; anything unmatched is served as
|
|
123
|
+
`application/octet-stream`.
|
|
124
|
+
|
|
125
|
+
`@jolly-pixel/asset` exports `ASSET_URL_PREFIX`, `CATALOG_URL_PATH` and
|
|
126
|
+
`assetSourceUrl(source)` so the browser builds the same URLs without repeating
|
|
127
|
+
the routes.
|
|
128
|
+
|
|
129
|
+
## Vite plugin
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import {
|
|
133
|
+
createAssetWorkspacePlugin
|
|
134
|
+
} from "@jolly-pixel/asset-server/plugins/vite.ts";
|
|
135
|
+
|
|
136
|
+
export default defineConfig({
|
|
137
|
+
plugins: [
|
|
138
|
+
createAssetWorkspacePlugin({
|
|
139
|
+
root: path.join(import.meta.dirname, "assets"),
|
|
140
|
+
handlers: [textureAssetHandler()]
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
One plugin mounts the whole workspace on the dev server: the catalog route,
|
|
147
|
+
static delivery and the WebSocket the asset rooms are edited through. It
|
|
148
|
+
accepts every `createAssetWorkspace` option plus:
|
|
149
|
+
|
|
150
|
+
| Option | Default | Description |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `catalogPath` | `/__jollypixel/catalog` | Catalog route. |
|
|
153
|
+
| `prefix` | `/assets/` | URL prefix the workspace is served under. |
|
|
154
|
+
| `socketPath` | `/ws-sync` | WebSocket upgrade path, kept apart from Vite HMR. |
|
|
155
|
+
| `onReady` | none | Receives the workspace once the back-end is up. |
|
|
156
|
+
|
|
157
|
+
Everything is built inside `configureServer`, so a production build never
|
|
158
|
+
opens the event log. `closeBundle` closes the workspace.
|
|
159
|
+
|
|
160
|
+
Use `onReady`, or pass your own `server`, when the dev server also hosts rooms
|
|
161
|
+
of its own:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
createAssetWorkspacePlugin({
|
|
165
|
+
root,
|
|
166
|
+
extensions: [new MyDemoRoom()],
|
|
167
|
+
onReady: ({ backend }) => reportCatalogSize(backend.catalog.size)
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
`createAssetCatalogPlugin` and `createAssetStaticPlugin` remain available for
|
|
172
|
+
a host wiring the pieces itself.
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jolly-pixel/asset-server",
|
|
3
|
+
"description": "Back-end asset orchestration: event-sourced projection, reconciliation and catalog delivery",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"imports": {
|
|
7
|
+
"#src/*": "./src/*"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./sync": {
|
|
15
|
+
"types": "./dist/sync/index.d.ts",
|
|
16
|
+
"default": "./dist/sync/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./catalog": {
|
|
19
|
+
"types": "./dist/catalog/index.d.ts",
|
|
20
|
+
"default": "./dist/catalog/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./rooms": {
|
|
23
|
+
"types": "./dist/rooms/index.d.ts",
|
|
24
|
+
"default": "./dist/rooms/index.js"
|
|
25
|
+
},
|
|
26
|
+
"./plugins/*.ts": "./dist/plugins/*.js",
|
|
27
|
+
"./static": {
|
|
28
|
+
"types": "./dist/static/index.d.ts",
|
|
29
|
+
"default": "./dist/static/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./workspace": {
|
|
32
|
+
"types": "./dist/workspace/index.d.ts",
|
|
33
|
+
"default": "./dist/workspace/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"prepublish": "rimraf ./dist && tsc -b",
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"typecheck": "tsc --noEmit -p test/tsconfig.json",
|
|
40
|
+
"test-only": "node --test \"test/**/*.spec.ts\"",
|
|
41
|
+
"test-types": "tstyche --tsconfig ./test/tsconfig.json",
|
|
42
|
+
"test": "npm run typecheck && c8 -r html npm run test-only && npm run test-types",
|
|
43
|
+
"lint": "eslint src test"
|
|
44
|
+
},
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"registry": "https://registry.npmjs.org",
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+https://github.com/JollyPixel/editor.git",
|
|
52
|
+
"directory": "packages/asset-server"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [],
|
|
55
|
+
"files": [
|
|
56
|
+
"dist",
|
|
57
|
+
"docs"
|
|
58
|
+
],
|
|
59
|
+
"directories": {
|
|
60
|
+
"doc": "docs",
|
|
61
|
+
"test": "test"
|
|
62
|
+
},
|
|
63
|
+
"author": "GENTILHOMME Thomas <gentilhomme.thomas@gmail.com>",
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@jolly-pixel/asset": "1.0.0",
|
|
67
|
+
"@jolly-pixel/asset-source": "1.0.0",
|
|
68
|
+
"@jolly-pixel/event-store": "2.0.0",
|
|
69
|
+
"@jolly-pixel/network": "1.1.0",
|
|
70
|
+
"@openally/emitt": "1.0.1",
|
|
71
|
+
"@openally/result": "3.1.0",
|
|
72
|
+
"ata-validator": "1.13.2",
|
|
73
|
+
"loglayer": "9.4.0"
|
|
74
|
+
},
|
|
75
|
+
"peerDependencies": {
|
|
76
|
+
"vite": "^8.0.0"
|
|
77
|
+
},
|
|
78
|
+
"peerDependenciesMeta": {
|
|
79
|
+
"vite": {
|
|
80
|
+
"optional": true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|