@c9up/station 0.1.5

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 (73) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/ResourceRegistry.d.ts +18 -0
  4. package/dist/ResourceRegistry.d.ts.map +1 -0
  5. package/dist/ResourceRegistry.js +38 -0
  6. package/dist/ResourceRegistry.js.map +1 -0
  7. package/dist/StationProvider.d.ts +109 -0
  8. package/dist/StationProvider.d.ts.map +1 -0
  9. package/dist/StationProvider.js +1144 -0
  10. package/dist/StationProvider.js.map +1 -0
  11. package/dist/casing.d.ts +27 -0
  12. package/dist/casing.d.ts.map +1 -0
  13. package/dist/casing.js +75 -0
  14. package/dist/casing.js.map +1 -0
  15. package/dist/defineResource.d.ts +9 -0
  16. package/dist/defineResource.d.ts.map +1 -0
  17. package/dist/defineResource.js +84 -0
  18. package/dist/defineResource.js.map +1 -0
  19. package/dist/index.d.ts +7 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +6 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/services/main.d.ts +18 -0
  24. package/dist/services/main.d.ts.map +1 -0
  25. package/dist/services/main.js +31 -0
  26. package/dist/services/main.js.map +1 -0
  27. package/dist/types.d.ts +85 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +8 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/views/errors/404.d.ts +12 -0
  32. package/dist/views/errors/404.d.ts.map +1 -0
  33. package/dist/views/errors/404.js +19 -0
  34. package/dist/views/errors/404.js.map +1 -0
  35. package/dist/views/escape.d.ts +30 -0
  36. package/dist/views/escape.d.ts.map +1 -0
  37. package/dist/views/escape.js +34 -0
  38. package/dist/views/escape.js.map +1 -0
  39. package/dist/views/form.d.ts +34 -0
  40. package/dist/views/form.d.ts.map +1 -0
  41. package/dist/views/form.js +139 -0
  42. package/dist/views/form.js.map +1 -0
  43. package/dist/views/layout.d.ts +24 -0
  44. package/dist/views/layout.d.ts.map +1 -0
  45. package/dist/views/layout.js +85 -0
  46. package/dist/views/layout.js.map +1 -0
  47. package/dist/views/list.d.ts +22 -0
  48. package/dist/views/list.d.ts.map +1 -0
  49. package/dist/views/list.js +85 -0
  50. package/dist/views/list.js.map +1 -0
  51. package/dist/views/login.d.ts +25 -0
  52. package/dist/views/login.d.ts.map +1 -0
  53. package/dist/views/login.js +44 -0
  54. package/dist/views/login.js.map +1 -0
  55. package/dist/views/show.d.ts +17 -0
  56. package/dist/views/show.d.ts.map +1 -0
  57. package/dist/views/show.js +24 -0
  58. package/dist/views/show.js.map +1 -0
  59. package/package.json +63 -0
  60. package/src/ResourceRegistry.ts +49 -0
  61. package/src/StationProvider.ts +1579 -0
  62. package/src/casing.ts +86 -0
  63. package/src/defineResource.ts +126 -0
  64. package/src/index.ts +14 -0
  65. package/src/services/main.ts +39 -0
  66. package/src/types.ts +108 -0
  67. package/src/views/errors/404.ts +27 -0
  68. package/src/views/escape.ts +46 -0
  69. package/src/views/form.ts +191 -0
  70. package/src/views/layout.ts +90 -0
  71. package/src/views/list.ts +121 -0
  72. package/src/views/login.ts +65 -0
  73. package/src/views/show.ts +37 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 C9up
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # @c9up/station
2
+
3
+ Station is the admin-scaffolding module for the [Ream](https://ream.dev) framework: declare a resource for an Atlas entity in one file and Station owns the route/UI/audit/policy derivation. It centralises the resource catalogue (`defineResource`, `ResourceRegistry`) so every later concern reads from a single declaration. See [docs/en/modules/station.md](https://ream.dev/modules/station) for the full guide.
@@ -0,0 +1,18 @@
1
+ import type { Resource } from "./types.js";
2
+ /**
3
+ * Central catalogue of declared resources for a Station-powered admin surface.
4
+ *
5
+ * Plain data structure: no boot()/start() lifecycle here — provider wiring
6
+ * lands in story 54.7. Iteration order is registration order, which is what
7
+ * the admin sidebar wants.
8
+ */
9
+ export declare class ResourceRegistry {
10
+ #private;
11
+ register<T>(resource: Resource<T>): void;
12
+ get(name: string): Resource | undefined;
13
+ getOrThrow(name: string): Resource;
14
+ has(name: string): boolean;
15
+ count(): number;
16
+ all(): ReadonlyArray<Resource>;
17
+ }
18
+ //# sourceMappingURL=ResourceRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceRegistry.d.ts","sourceRoot":"","sources":["../src/ResourceRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;GAMG;AACH,qBAAa,gBAAgB;;IAG5B,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI;IAUxC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIvC,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ;IAUlC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B,KAAK,IAAI,MAAM;IAIf,GAAG,IAAI,aAAa,CAAC,QAAQ,CAAC;CAI9B"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Central catalogue of declared resources for a Station-powered admin surface.
3
+ *
4
+ * Plain data structure: no boot()/start() lifecycle here — provider wiring
5
+ * lands in story 54.7. Iteration order is registration order, which is what
6
+ * the admin sidebar wants.
7
+ */
8
+ export class ResourceRegistry {
9
+ #map = new Map();
10
+ register(resource) {
11
+ const existing = this.#map.get(resource.name);
12
+ if (existing !== undefined) {
13
+ throw new Error(`[station] ResourceRegistry: duplicate resource name '${resource.name}' (already registered for ${existing.entity.name})`);
14
+ }
15
+ this.#map.set(resource.name, resource);
16
+ }
17
+ get(name) {
18
+ return this.#map.get(name);
19
+ }
20
+ getOrThrow(name) {
21
+ const found = this.#map.get(name);
22
+ if (found === undefined) {
23
+ throw new Error(`[station] ResourceRegistry: no resource named '${name}'`);
24
+ }
25
+ return found;
26
+ }
27
+ has(name) {
28
+ return this.#map.has(name);
29
+ }
30
+ count() {
31
+ return this.#map.size;
32
+ }
33
+ all() {
34
+ const snapshot = [...this.#map.values()];
35
+ return Object.freeze(snapshot);
36
+ }
37
+ }
38
+ //# sourceMappingURL=ResourceRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ResourceRegistry.js","sourceRoot":"","sources":["../src/ResourceRegistry.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,gBAAgB;IACnB,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE5C,QAAQ,CAAI,QAAqB;QAChC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACd,wDAAwD,QAAQ,CAAC,IAAI,6BAA6B,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CACzH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,GAAG,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,UAAU,CAAC,IAAY;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACd,kDAAkD,IAAI,GAAG,CACzD,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACd,CAAC;IAED,GAAG,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,KAAK;QACJ,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IACvB,CAAC;IAED,GAAG;QACF,MAAM,QAAQ,GAAe,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACrD,OAAO,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChC,CAAC;CACD"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * StationProvider — Ream provider that wires Station's `ResourceRegistry`
3
+ * into the host container and mounts the list + show routes for every
4
+ * registered resource.
5
+ *
6
+ * Story 54.7 EXTENDS this provider with Warden integration (login
7
+ * surface + `/_assets/station/*` mount). The class shape and lifecycle
8
+ * stay; only `start()` grows.
9
+ *
10
+ * Mirror of `packages/aurora/src/AuroraProvider.ts` — register binds a
11
+ * singleton + sets the `services/main` proxy backing instance, start
12
+ * dynamically imports BOTH `@c9up/ream/services/router` AND `@c9up/atlas`
13
+ * inside try/catch so non-Ream hosts AND Station-without-Atlas consumers
14
+ * are silently tolerated. Once both modules resolve, the per-resource
15
+ * repository + column metadata is built ONCE (cached on the instance)
16
+ * and re-used by every request, then route registration runs OUTSIDE
17
+ * the catch — real bugs in route registration surface instead of being
18
+ * swallowed.
19
+ */
20
+ import type { ColumnMetadata } from "@c9up/atlas";
21
+ /**
22
+ * Duck-typed slice of the host's IoC container — Station MUST stay
23
+ * publishable without importing `@c9up/ream` directly (memory
24
+ * `project_package_extraction`). The Ream container fulfils this shape.
25
+ */
26
+ interface StationContainer {
27
+ singleton<T>(key: unknown, factory: () => T): void;
28
+ resolve<T>(key: unknown): T;
29
+ has(key: unknown): boolean;
30
+ }
31
+ interface StationConfigStore {
32
+ get<T>(key: string): T | undefined;
33
+ }
34
+ export interface StationAppContext {
35
+ container: StationContainer;
36
+ config: StationConfigStore;
37
+ }
38
+ /**
39
+ * Config block read from `app.config.get<StationConfig>('station')`.
40
+ * Every field is optional — the defaults match the 54.2 / 54.3 / 54.4
41
+ * conventions so an app can leave the config out entirely.
42
+ */
43
+ export interface StationConfig {
44
+ /**
45
+ * When true (default `true` if `@c9up/warden` is installed),
46
+ * Station mounts a login surface at `/admin/login` and gates every
47
+ * other `/admin/*` route behind `auth.verify(token)`. Setting this
48
+ * to `false` keeps the old open-by-default behaviour from 54.2.
49
+ */
50
+ requireAuth?: boolean;
51
+ /**
52
+ * Role required to pass the auth gate. When omitted, any
53
+ * authenticated user can access `/admin/*` (the per-action
54
+ * `<resource>.<action>` permission gate still applies).
55
+ */
56
+ requireRole?: string;
57
+ /**
58
+ * Where to redirect on a failed auth check. Defaults to
59
+ * `/admin/login`.
60
+ */
61
+ loginPath?: string;
62
+ /**
63
+ * Cookie name carrying the auth token. Defaults to `station_auth`
64
+ * to avoid colliding with app-level session cookies.
65
+ */
66
+ cookieName?: string;
67
+ }
68
+ /** @internal Reset module-level flags between tests. */
69
+ export declare function resetStationProviderFlags(): void;
70
+ /**
71
+ * Mass-assignment guard. Only accepts keys that are:
72
+ * 1. A declared `@Column` propertyKey on the resource entity, AND
73
+ * 2. Not the primary key (DB-managed), AND
74
+ * 3. Not a framework-managed timestamp. Two signals, both honoured:
75
+ * (a) the Lucid-style auto flag — `@column.dateTime({ autoCreate })` /
76
+ * `{ autoUpdate }` (passed in via `autoManaged`); this is the
77
+ * authoritative one and catches custom-named timestamp columns
78
+ * (e.g. `registeredAt`) the name list below would miss, AND
79
+ * (b) the conventional name fallback (created_at / updated_at /
80
+ * deleted_at) for plain `@Column` timestamps declared without the
81
+ * auto flag.
82
+ *
83
+ * The `_method` synthetic field from browser-form method-overrides is
84
+ * dropped automatically because it never matches a column propertyKey.
85
+ *
86
+ * Returning a fresh object — never the caller's reference — so a
87
+ * downstream mutation can't poison the audit snapshot.
88
+ */
89
+ /** @internal Exported for unit tests — the mass-assignment + checkbox coercion guard. */
90
+ export declare function filterWritableBody(body: Record<string, unknown>, columns: ReadonlyArray<ColumnMetadata>, pkColumn: string, autoManaged: ReadonlySet<string>): Record<string, unknown>;
91
+ export default class StationProvider {
92
+ #private;
93
+ protected app: StationAppContext;
94
+ constructor(app: StationAppContext);
95
+ register(): void;
96
+ boot(): Promise<void>;
97
+ start(): Promise<void>;
98
+ ready(): Promise<void>;
99
+ shutdown(): Promise<void>;
100
+ }
101
+ /**
102
+ * Node's ERR_MODULE_NOT_FOUND surfaces on an Error subclass with `code`.
103
+ * Exported for the 54.8 agnostic-peer-missing unit test, which can't
104
+ * realistically simulate the dynamic-import failure path inside vitest's
105
+ * mock graph.
106
+ */
107
+ export declare function isModuleNotFound(err: unknown): boolean;
108
+ export {};
109
+ //# sourceMappingURL=StationProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StationProvider.d.ts","sourceRoot":"","sources":["../src/StationProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAEX,cAAc,EAGd,MAAM,aAAa,CAAC;AAYrB;;;;GAIG;AACH,UAAU,gBAAgB;IACzB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACnD,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,GAAG,CAAC,CAAC;IAC5B,GAAG,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3B;AAED,UAAU,kBAAkB;IAC3B,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,iBAAiB;IACjC,SAAS,EAAE,gBAAgB,CAAC;IAC5B,MAAM,EAAE,kBAAkB,CAAC;CAC3B;AA2MD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC7B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAaD,wDAAwD;AACxD,wBAAgB,yBAAyB,IAAI,IAAI,CAMhD;AAaD;;;;;;;;;;;;;;;;;;GAkBG;AACH,yFAAyF;AACzF,wBAAgB,kBAAkB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,aAAa,CAAC,cAAc,CAAC,EACtC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,GAC9B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAmDD,MAAM,CAAC,OAAO,OAAO,eAAe;;IAgBvB,SAAS,CAAC,GAAG,EAAE,iBAAiB;gBAAtB,GAAG,EAAE,iBAAiB;IAE5C,QAAQ,IAAI,IAAI;IAWV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAMrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA0PtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAEtB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA+iB/B;AAwED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAItD"}