@graview/ship 0.1.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.
Files changed (87) hide show
  1. package/LICENSE +96 -0
  2. package/README.md +95 -0
  3. package/dist/browser-adapter.d.ts +84 -0
  4. package/dist/browser-adapter.d.ts.map +1 -0
  5. package/dist/browser-adapter.js +111 -0
  6. package/dist/browser-adapter.js.map +1 -0
  7. package/dist/browser.d.ts +26 -0
  8. package/dist/browser.d.ts.map +1 -0
  9. package/dist/browser.js +16 -0
  10. package/dist/browser.js.map +1 -0
  11. package/dist/cli.d.ts +48 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +170 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/dev.d.ts +148 -0
  16. package/dist/dev.d.ts.map +1 -0
  17. package/dist/dev.js +286 -0
  18. package/dist/dev.js.map +1 -0
  19. package/dist/door.d.ts +22 -0
  20. package/dist/door.d.ts.map +1 -0
  21. package/dist/door.js +43 -0
  22. package/dist/door.js.map +1 -0
  23. package/dist/export.d.ts +25 -0
  24. package/dist/export.d.ts.map +1 -0
  25. package/dist/export.js +30 -0
  26. package/dist/export.js.map +1 -0
  27. package/dist/file-adapter.d.ts +28 -0
  28. package/dist/file-adapter.d.ts.map +1 -0
  29. package/dist/file-adapter.js +50 -0
  30. package/dist/file-adapter.js.map +1 -0
  31. package/dist/health.d.ts +19 -0
  32. package/dist/health.d.ts.map +1 -0
  33. package/dist/health.js +16 -0
  34. package/dist/health.js.map +1 -0
  35. package/dist/index.d.ts +29 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +15 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/migrations.d.ts +31 -0
  40. package/dist/migrations.d.ts.map +1 -0
  41. package/dist/migrations.js +59 -0
  42. package/dist/migrations.js.map +1 -0
  43. package/dist/open-store.d.ts +57 -0
  44. package/dist/open-store.d.ts.map +1 -0
  45. package/dist/open-store.js +103 -0
  46. package/dist/open-store.js.map +1 -0
  47. package/dist/photos.d.ts +89 -0
  48. package/dist/photos.d.ts.map +1 -0
  49. package/dist/photos.js +102 -0
  50. package/dist/photos.js.map +1 -0
  51. package/dist/presence.d.ts +33 -0
  52. package/dist/presence.d.ts.map +1 -0
  53. package/dist/presence.js +99 -0
  54. package/dist/presence.js.map +1 -0
  55. package/dist/remote.d.ts +73 -0
  56. package/dist/remote.d.ts.map +1 -0
  57. package/dist/remote.js +271 -0
  58. package/dist/remote.js.map +1 -0
  59. package/dist/serve.d.ts +121 -0
  60. package/dist/serve.d.ts.map +1 -0
  61. package/dist/serve.js +249 -0
  62. package/dist/serve.js.map +1 -0
  63. package/dist/snapshot.d.ts +20 -0
  64. package/dist/snapshot.d.ts.map +1 -0
  65. package/dist/snapshot.js +41 -0
  66. package/dist/snapshot.js.map +1 -0
  67. package/dist/source-edit.d.ts +52 -0
  68. package/dist/source-edit.d.ts.map +1 -0
  69. package/dist/source-edit.js +455 -0
  70. package/dist/source-edit.js.map +1 -0
  71. package/dist/steps.d.ts +98 -0
  72. package/dist/steps.d.ts.map +1 -0
  73. package/dist/steps.js +189 -0
  74. package/dist/steps.js.map +1 -0
  75. package/dist/studio-door.d.ts +36 -0
  76. package/dist/studio-door.d.ts.map +1 -0
  77. package/dist/studio-door.js +102 -0
  78. package/dist/studio-door.js.map +1 -0
  79. package/dist/sync-seed.d.ts +61 -0
  80. package/dist/sync-seed.d.ts.map +1 -0
  81. package/dist/sync-seed.js +95 -0
  82. package/dist/sync-seed.js.map +1 -0
  83. package/dist/typecheck.d.ts +15 -0
  84. package/dist/typecheck.d.ts.map +1 -0
  85. package/dist/typecheck.js +59 -0
  86. package/dist/typecheck.js.map +1 -0
  87. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,96 @@
1
+ Elastic License 2.0
2
+
3
+ ## Acceptance
4
+
5
+ By using the software, you agree to all of the terms and conditions below.
6
+
7
+ ## Copyright License
8
+
9
+ The licensor grants you a non-exclusive, royalty-free, worldwide,
10
+ non-sublicensable, non-transferable license to use, copy, distribute, make
11
+ available, and prepare derivative works of the software, in each case subject
12
+ to the limitations and conditions below.
13
+
14
+ ## Limitations
15
+
16
+ You may not provide the software to third parties as a hosted or managed
17
+ service, where the service provides users with access to any substantial set
18
+ of the features or functionality of the software.
19
+
20
+ You may not move, change, disable, or circumvent the license key
21
+ functionality in the software, and you may not remove or obscure any
22
+ functionality in the software that is protected by the license key.
23
+
24
+ You may not alter, remove, or obscure any licensing, copyright, or other
25
+ notices of the licensor in the software. Any use of the licensor's trademarks
26
+ is subject to applicable law.
27
+
28
+ ## Patents
29
+
30
+ The licensor grants you a license, under any patent claims the licensor can
31
+ license, or becomes able to license, to make, have made, use, sell, offer for
32
+ sale, import and have imported the software, in each case subject to the
33
+ limitations and conditions in this license. This license does not cover any
34
+ patent claims that you cause to be infringed by modifications or additions to
35
+ the software. If you or your company make any written claim that the software
36
+ infringes or contributes to infringement of any patent, your patent license
37
+ for the software granted under these terms ends immediately. If your company
38
+ makes such a claim, your patent license ends immediately for work on behalf
39
+ of your company.
40
+
41
+ ## Notices
42
+
43
+ You must ensure that anyone who gets a copy of any part of the software from
44
+ you also gets a copy of these terms.
45
+
46
+ If you modify the software, you must include in any modified copies of the
47
+ software prominent notices stating that you have modified the software.
48
+
49
+ ## No Other Rights
50
+
51
+ These terms do not imply any licenses other than those expressly granted in
52
+ these terms.
53
+
54
+ ## Termination
55
+
56
+ If you use the software in violation of these terms, such use is not
57
+ licensed, and your licenses will automatically terminate. If the licensor
58
+ provides you with a notice of your violation, and you cease all violation of
59
+ this license no later than 30 days after you receive that notice, your
60
+ licenses will be reinstated retroactively. However, if you violate these
61
+ terms after such reinstatement, any additional violation of these terms will
62
+ cause your licenses to terminate automatically and permanently.
63
+
64
+ ## No Liability
65
+
66
+ *As far as the law allows, the software comes as is, without any warranty or
67
+ condition, and the licensor will not be liable to you for any damages arising
68
+ out of these terms or the use or nature of the software, under any kind of
69
+ legal claim.*
70
+
71
+ ## Definitions
72
+
73
+ The **licensor** is the entity offering these terms, and the **software** is
74
+ the software the licensor makes available under these terms, including any
75
+ portion of it.
76
+
77
+ **you** refers to the individual or entity agreeing to these terms.
78
+
79
+ **your company** is any legal entity, sole proprietorship, or other kind of
80
+ organization that you work for, plus all organizations that have control
81
+ over, are under the control of, or are under common control with that
82
+ organization. **control** means ownership of substantially all the assets of
83
+ an entity, or the power to direct its management and policies by vote,
84
+ contract, or otherwise. Control can be direct or indirect.
85
+
86
+ **your licenses** are all the licenses granted to you for the software under
87
+ these terms.
88
+
89
+ **use** means anything you do with the software requiring one of your
90
+ licenses.
91
+
92
+ **trademark** means trademarks, service marks, and similar rights.
93
+
94
+ ---
95
+
96
+ Copyright 2025-2026 En Dash Consulting
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @graview/ship
2
+
3
+ What **every** deployment of a Graview app needs, hosted or self-hosted: one `defineApp`
4
+ declaration plus one persistence adapter is a running deployment.
5
+
6
+ - **`openStore({ app, adapter })`** — load what was stored, migrate it forward, fold it into
7
+ a live `Store`, and keep the adapter current (every diff appends its operations and
8
+ rewrites the snapshot).
9
+ - **`createFileAdapter(root)`** — persistence a person can read: `snapshot.json`,
10
+ append-only `log.jsonl`, `meta.json` with the stored schema version. The core's sqlite
11
+ adapter is the scale answer; this is the "where is my data" answer.
12
+ - **`createBrowserAdapter()`** — the same three things in `localStorage`, for an app that
13
+ runs in the page with no server: the sample apps remember their edits through it,
14
+ attributed and undoable, migrations included. Import from `@graview/ship/browser` in a
15
+ bundle (the root entry carries the file adapter's `node:fs`). Conventions the helpers
16
+ read: `?fresh=1` returns to the seed (`openStore({ fresh })`), and a driven browser
17
+ starts fresh unless it says `?remember=1` — `browserStartsFresh()`, `forgetFreshParam()`.
18
+ - **The store reopens with its history.** `openStore` hands the persisted log to the
19
+ store alongside the snapshot, so what was done in an earlier session is still in the
20
+ activity and still undoable — persistence is the op log, not a cache of the graph.
21
+ - **Op-log-native migrations** — a declaration carries `version` and `migrations`
22
+ (`{ from, to, title, apply(snapshot) → primitives }`). Running one appends ordinary
23
+ operations: authored `system · ship:migration`, stating intent, carrying their inverse.
24
+ `graview check` refuses a chain with gaps or multi-version jumps before deploy time.
25
+ - **`exportBundle` / `assertBundle`** — the anti-lock-in shape: graph + attributed history +
26
+ version in one JSON bundle, re-importable into any deployment of the same declaration.
27
+ - **`health(store)`** — coherence, not liveness: dangling edges, standing, sizes.
28
+ - **`serveStore` / `openRemote`** — the store behind HTTP, and the other end of the wire. A
29
+ client sends CALLS, never primitives; the server applies them through an ordinary `Store`
30
+ under the seat the request carries, so the policy refuses on the server exactly what it
31
+ refuses in a browser. `graview serve <entry>` is the command.
32
+ - **Content moves as steps.** The step DSL (`stepsMigration`) has five content steps beside
33
+ the schema ones — `put-node`, `patch-node`, `drop-node`, `put-edge`, `drop-edge` — each
34
+ judged against the stored graph when it runs, so a default that is already there is not
35
+ put twice. `seedSteps(seed, live)` diffs a bootstrap seed against a live snapshot into
36
+ them; `applySteps(store, steps)` lands them as one logged, undoable operation authored
37
+ `system · ship:sync-seed`. `graview sync-seed <entry> --seed <file> --data <dir> [--apply]
38
+ [--prune]` is the command.
39
+
40
+ ## The seed is read once
41
+
42
+ `seed` is what `openStore` puts in an EMPTY store — the first install, and never again.
43
+ A store that has been opened before keeps what it has, whatever the seed file now says;
44
+ that is the whole reason a person's edits survive a deploy. So a change to the default
45
+ content is not a reason to delete the store (`fresh` is a demo's way back to the example,
46
+ not a redesign tool): it is `graview sync-seed`, which says what would change and lands
47
+ only that, under undo.
48
+
49
+ ## The wire
50
+
51
+ Every route `serveStore` answers is `WIRE`, exported from the package and pinned by a test,
52
+ so a host in front of it knows what it must keep answering for `openRemote`, `graview mcp
53
+ --remote-url` and `graview apply --remote-url` to work unchanged:
54
+
55
+ | Method | Path | Says |
56
+ |---|---|---|
57
+ | GET | `/graview/state` | the graph, the log and the stored version |
58
+ | POST | `/graview/ops` | calls in, the ops they produced out — or `undo`, batches to take back; 409 with the policy's sentence when refused |
59
+ | GET | `/graview/since?seq=N` | the ops appended after N — everyone else's |
60
+ | GET | `/graview/health` | ship's own report, plus where the data is |
61
+ | GET | `/graview/export` | the whole store as one bundle, the way out |
62
+ | POST | `/graview/here` | say where you are; answers with who else is, and the ops since `seq` |
63
+ | GET | `/graview/who` | who is here right now |
64
+ | POST | `/graview/leave` | say you have gone |
65
+
66
+ A request carries its seat in `x-graview-seat` and `x-graview-roles` (`SEAT_HEADERS`) by
67
+ default; `serveStore({ seatOf })` is where a host reads its own credential instead. Whatever
68
+ a host asks for rides along: `openRemote({ headers })` sends them with every request, and
69
+ the framework never reads them. `openRemote(...).settled()` resolves once every call sent
70
+ so far has been answered — a browser never waits for it; a host that must report the
71
+ server's verdict before it exits does.
72
+
73
+ ## The hosted-store contract
74
+
75
+ A third party stands up their own host — a file store or SQLite, `graview serve`,
76
+ `graview mcp` — without forking anything here. Graview Cloud is the polished multi-tenant
77
+ host of the same API, and it consumes this package the way any customer would.
78
+
79
+ | Concern | In the framework | In a host (Graview Cloud, or yours) |
80
+ |---|---|---|
81
+ | Op log + snapshot + migrate on open | `openStore` | runs it on the server, per deployment |
82
+ | The wire | `serveStore`, `openRemote`, `WIRE` | production TLS, a gateway URL |
83
+ | Who is asking | `Principal` on every `apply`; `seatOf` reads the request | maps users, keys and agents to principals; tenancy; quotas |
84
+ | Seed | read once, on an empty store | the same |
85
+ | Default content moving | `seedSteps`, `applySteps`, `graview sync-seed` | when to run it, and for whom |
86
+ | An agent attaching | `graview mcp`, `graview apply`, `--remote-url`, `--header` | issuing the keys those headers carry |
87
+ | Schema moving | `version`, `migrations[]`, `graview check` | fleet upgrades |
88
+
89
+ ## The boundary
90
+
91
+ Anything **one** deployment needs lives here. Anything only the **operator of many**
92
+ deployments needs — tenancy, provisioning, deploy-to-URL, billing, fleet upgrades, the
93
+ builder UX — lives in the `graview-cloud` repo, which consumes this package the way any
94
+ customer would. If Cloud ever needs a private hook into the framework, that hook is a
95
+ missing public seam to fix here first.
@@ -0,0 +1,84 @@
1
+ import type { PersistenceAdapter } from "@graview/core";
2
+ /**
3
+ * The slice of `Storage` this adapter needs — `localStorage` in a browser,
4
+ * a `Map` dressed as one in a test or a node rehearsal. Declared here rather
5
+ * than as the DOM's own type so the package needs no DOM lib to build.
6
+ */
7
+ export interface StorageLike {
8
+ getItem(key: string): string | null;
9
+ setItem(key: string, value: string): void;
10
+ removeItem(key: string): void;
11
+ }
12
+ export interface BrowserAdapter extends PersistenceAdapter<string> {
13
+ loadMeta(scope: string): {
14
+ version: number;
15
+ } | null;
16
+ saveMeta(scope: string, meta: {
17
+ version: number;
18
+ }): void;
19
+ /** The storage keys this scope occupies — for a person or a test to look. */
20
+ keysFor(scope: string): {
21
+ snapshot: string;
22
+ log: string;
23
+ meta: string;
24
+ };
25
+ }
26
+ export interface BrowserAdapterOptions {
27
+ /** Defaults to the page's `localStorage`. */
28
+ readonly storage?: StorageLike;
29
+ /** Key prefix, so two apps on one origin do not read each other's graph. */
30
+ readonly prefix?: string;
31
+ }
32
+ /**
33
+ * Persistence in the browser itself: the SAME three things the file adapter
34
+ * writes — the snapshot, the append-only log, the stored schema version —
35
+ * as three `localStorage` entries per scope. It slots into `openStore`
36
+ * unchanged, migrations included, so a sample app that remembers is the
37
+ * same lifecycle as a deployment that does, minus the server.
38
+ *
39
+ * `localStorage` is synchronous and small (a few megabytes an origin), which
40
+ * is exactly right for a demo or a single person's graph and exactly wrong
41
+ * for a shared deployment — that is what the file and sqlite adapters are
42
+ * for. If a log outgrows it the honest next step is IndexedDB behind this
43
+ * same interface, not a bigger string.
44
+ *
45
+ * A write that fails (quota, a locked-down browser) THROWS, and `openStore`
46
+ * reports it: an app that believes it remembered and did not is the worst
47
+ * quiet state, so nothing here swallows an error.
48
+ */
49
+ export declare function createBrowserAdapter(options?: BrowserAdapterOptions): BrowserAdapter;
50
+ /**
51
+ * Whether THIS load should start from the seed rather than from what the
52
+ * browser remembers.
53
+ *
54
+ * `?fresh=1` asks for it outright — the address a "start fresh" control
55
+ * navigates to. Beyond that, a DRIVEN browser starts fresh unless it says
56
+ * `?remember=1`: a harness is a specification of the example, and a
57
+ * harness whose second `goto` inherited its first one's edits would be
58
+ * testing its own residue. A person's browser is never driven, so a
59
+ * person always gets what they left.
60
+ */
61
+ export declare function browserStartsFresh(location?: {
62
+ readonly search: string;
63
+ }, navigator?: {
64
+ readonly webdriver?: boolean;
65
+ }): boolean;
66
+ /**
67
+ * Drops `?fresh=1` from the address once it has been honoured, so the seed
68
+ * is the FIRST load rather than every load: a reload after starting fresh
69
+ * must keep what was done since, or "start fresh" is really "stop
70
+ * remembering".
71
+ */
72
+ export declare function forgetFreshParam(win?: {
73
+ readonly location: {
74
+ readonly href: string;
75
+ };
76
+ readonly history: {
77
+ replaceState(data: unknown, unused: string, url: string): void;
78
+ };
79
+ }): void;
80
+ /** The address a "start fresh" control goes to: here, from the seed. */
81
+ export declare function freshHref(location?: {
82
+ readonly href: string;
83
+ }): string;
84
+ //# sourceMappingURL=browser-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-adapter.d.ts","sourceRoot":"","sources":["../src/browser-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAa,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,MAAM,WAAW,cAAe,SAAQ,kBAAkB,CAAC,MAAM,CAAC;IAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,6EAA6E;IAC7E,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACzE;AAED,MAAM,WAAW,qBAAqB;IACpC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAC/B,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CA4CxF;AAaD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,GAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,EACX,SAAS,GAAE;IAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAE9B,GACV,OAAO,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE;IACH,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE;QAAE,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAChE,GACtB,IAAI,CAKN;AAED,wEAAwE;AACxE,wBAAgB,SAAS,CACvB,QAAQ,GAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,GACV,MAAM,CAIR"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Persistence in the browser itself: the SAME three things the file adapter
3
+ * writes — the snapshot, the append-only log, the stored schema version —
4
+ * as three `localStorage` entries per scope. It slots into `openStore`
5
+ * unchanged, migrations included, so a sample app that remembers is the
6
+ * same lifecycle as a deployment that does, minus the server.
7
+ *
8
+ * `localStorage` is synchronous and small (a few megabytes an origin), which
9
+ * is exactly right for a demo or a single person's graph and exactly wrong
10
+ * for a shared deployment — that is what the file and sqlite adapters are
11
+ * for. If a log outgrows it the honest next step is IndexedDB behind this
12
+ * same interface, not a bigger string.
13
+ *
14
+ * A write that fails (quota, a locked-down browser) THROWS, and `openStore`
15
+ * reports it: an app that believes it remembered and did not is the worst
16
+ * quiet state, so nothing here swallows an error.
17
+ */
18
+ export function createBrowserAdapter(options = {}) {
19
+ const prefix = options.prefix ?? "graview";
20
+ const storage = options.storage ?? pageStorage();
21
+ const keysFor = (scope) => ({
22
+ snapshot: `${prefix}:${scope}:snapshot`,
23
+ log: `${prefix}:${scope}:log`,
24
+ meta: `${prefix}:${scope}:meta`,
25
+ });
26
+ const read = (key) => {
27
+ const raw = storage.getItem(key);
28
+ return raw === null ? null : JSON.parse(raw);
29
+ };
30
+ const write = (key, value) => storage.setItem(key, JSON.stringify(value));
31
+ return {
32
+ name: "browser",
33
+ keysFor,
34
+ async load(scope) {
35
+ return read(keysFor(scope).snapshot);
36
+ },
37
+ async save(scope, snapshot) {
38
+ write(keysFor(scope).snapshot, snapshot);
39
+ },
40
+ async delete(scope) {
41
+ const keys = keysFor(scope);
42
+ storage.removeItem(keys.snapshot);
43
+ storage.removeItem(keys.log);
44
+ storage.removeItem(keys.meta);
45
+ },
46
+ async loadLog(scope) {
47
+ return read(keysFor(scope).log) ?? [];
48
+ },
49
+ async appendOps(scope, ops) {
50
+ if (ops.length === 0)
51
+ return;
52
+ const key = keysFor(scope).log;
53
+ write(key, [...(read(key) ?? []), ...ops]);
54
+ },
55
+ loadMeta(scope) {
56
+ return read(keysFor(scope).meta);
57
+ },
58
+ saveMeta(scope, meta) {
59
+ write(keysFor(scope).meta, meta);
60
+ },
61
+ };
62
+ }
63
+ function pageStorage() {
64
+ const found = globalThis.localStorage;
65
+ if (!found) {
66
+ throw new Error("createBrowserAdapter: no localStorage here — pass `storage` (any object with " +
67
+ "getItem/setItem/removeItem) when running outside a browser.");
68
+ }
69
+ return found;
70
+ }
71
+ /**
72
+ * Whether THIS load should start from the seed rather than from what the
73
+ * browser remembers.
74
+ *
75
+ * `?fresh=1` asks for it outright — the address a "start fresh" control
76
+ * navigates to. Beyond that, a DRIVEN browser starts fresh unless it says
77
+ * `?remember=1`: a harness is a specification of the example, and a
78
+ * harness whose second `goto` inherited its first one's edits would be
79
+ * testing its own residue. A person's browser is never driven, so a
80
+ * person always gets what they left.
81
+ */
82
+ export function browserStartsFresh(location = globalThis
83
+ .location, navigator = globalThis.navigator) {
84
+ const params = new URLSearchParams(location.search);
85
+ if (params.get("fresh") === "1")
86
+ return true;
87
+ if (params.get("remember") === "1")
88
+ return false;
89
+ return navigator?.webdriver === true;
90
+ }
91
+ /**
92
+ * Drops `?fresh=1` from the address once it has been honoured, so the seed
93
+ * is the FIRST load rather than every load: a reload after starting fresh
94
+ * must keep what was done since, or "start fresh" is really "stop
95
+ * remembering".
96
+ */
97
+ export function forgetFreshParam(win = globalThis) {
98
+ const url = new URL(win.location.href);
99
+ if (url.searchParams.get("fresh") !== "1")
100
+ return;
101
+ url.searchParams.delete("fresh");
102
+ win.history.replaceState(null, "", url.toString());
103
+ }
104
+ /** The address a "start fresh" control goes to: here, from the seed. */
105
+ export function freshHref(location = globalThis
106
+ .location) {
107
+ const url = new URL(location.href);
108
+ url.searchParams.set("fresh", "1");
109
+ return url.toString();
110
+ }
111
+ //# sourceMappingURL=browser-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-adapter.js","sourceRoot":"","sources":["../src/browser-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The browser entry: everything in `@graview/ship` that does not need a
3
+ * filesystem. An app bundled for the page imports from here, so a bundler
4
+ * never meets `node:fs` through the file adapter.
5
+ */
6
+ export { browserStartsFresh, createBrowserAdapter, forgetFreshParam, freshHref, } from "./browser-adapter.js";
7
+ export type { BrowserAdapter, BrowserAdapterOptions, StorageLike } from "./browser-adapter.js";
8
+ export { migrateSnapshot, pendingMigrations } from "./migrations.js";
9
+ export { primitivesFor, sayStep, stepsMigration } from "./steps.js";
10
+ export type { MigrationStep } from "./steps.js";
11
+ export type { MigrationRun } from "./migrations.js";
12
+ export { openStore } from "./open-store.js";
13
+ export type { OpenStoreOptions, OpenedStore } from "./open-store.js";
14
+ export { applyToSnapshot } from "./snapshot.js";
15
+ export type { GraphSnapshot } from "./snapshot.js";
16
+ export { assertBundle, exportBundle } from "./export.js";
17
+ export type { AppBundle } from "./export.js";
18
+ export { health } from "./health.js";
19
+ export type { HealthReport } from "./health.js";
20
+ export { openRemote } from "./remote.js";
21
+ export { createBroadcastPresence, presenceChannelName } from "./presence.js";
22
+ export type { BroadcastPresenceOptions, ChannelLike } from "./presence.js";
23
+ export type { RemoteOptions, RemoteStore } from "./remote.js";
24
+ export { assertPhotoFits, photosUsed, storageBytes, PhotoTooLarge, PHOTO_BUDGET_BYTES, PHOTO_MAX_BYTES, } from "./photos.js";
25
+ export type { PhotoBudget, PhotoField } from "./photos.js";
26
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,GACV,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,cAAc,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,YAAY,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAC7E,YAAY,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC3E,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The browser entry: everything in `@graview/ship` that does not need a
3
+ * filesystem. An app bundled for the page imports from here, so a bundler
4
+ * never meets `node:fs` through the file adapter.
5
+ */
6
+ export { browserStartsFresh, createBrowserAdapter, forgetFreshParam, freshHref, } from "./browser-adapter.js";
7
+ export { migrateSnapshot, pendingMigrations } from "./migrations.js";
8
+ export { primitivesFor, sayStep, stepsMigration } from "./steps.js";
9
+ export { openStore } from "./open-store.js";
10
+ export { applyToSnapshot } from "./snapshot.js";
11
+ export { assertBundle, exportBundle } from "./export.js";
12
+ export { health } from "./health.js";
13
+ export { openRemote } from "./remote.js";
14
+ export { createBroadcastPresence, presenceChannelName } from "./presence.js";
15
+ export { assertPhotoFits, photosUsed, storageBytes, PhotoTooLarge, PHOTO_BUDGET_BYTES, PHOTO_MAX_BYTES, } from "./photos.js";
16
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACL,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,SAAS,GACV,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAG7E,OAAO,EACL,eAAe,EACf,UAAU,EACV,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,eAAe,GAChB,MAAM,aAAa,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,48 @@
1
+ import type { PersistenceAdapter } from "@graview/core";
2
+ import type { GraphSnapshot } from "./snapshot.js";
3
+ /**
4
+ * `graview serve` — the store behind HTTP, and the data in a folder you can
5
+ * open.
6
+ *
7
+ * The file adapter is the default because the point of this command is the
8
+ * answer to "where is my data": a directory holding `snapshot.json`, a
9
+ * `log.jsonl` with one operation per line that a person can `grep`, and a
10
+ * `meta.json` carrying the stored version. `--sqlite` swaps the adapter and
11
+ * changes nothing else, which is the actual claim about adapters.
12
+ */
13
+ export declare const SERVE_USAGE = " graview serve <entry> [--data <dir>] [--port <n>] [--seed <file>] [--sqlite <file>]\n Serves the app's store over HTTP. The op log is the wire: a client\n sends calls, the store judges them under the caller's own seat, and\n the ops come back. Data lives in <dir> (default ./data) as readable\n JSON, or in a SQLite file with --sqlite.\n\n graview sync-seed <entry> --seed <file> [--data <dir> | --sqlite <file>]\n [--apply] [--prune] [--json]\n Diffs the bootstrap seed against the live store and says, as content\n steps, what would bring the store in step with it: records put,\n fields patched, ties made. Prints and exits by default; --apply lands\n the steps as one logged, undoable operation; --prune also drops what\n the seed no longer has. The seed itself is only ever read at first\n install \u2014 this is how default content moves afterwards, in place of\n deleting the store.\n";
14
+ /** The same store, whichever command asked: parsed from the flags every store command shares. */
15
+ export declare const STORE_FLAGS = "--data <dir> (default ./data) | --sqlite <file>, and --seed <file> for a first install";
16
+ export declare function flag(argv: readonly string[], name: string): string | undefined;
17
+ export interface StoreBackend {
18
+ readonly adapter: PersistenceAdapter<string> & {
19
+ loadMeta?(scope: string): {
20
+ version: number;
21
+ } | null;
22
+ saveMeta?(scope: string, meta: {
23
+ version: number;
24
+ }): void;
25
+ };
26
+ /** Where the data is, in words a person can open. */
27
+ readonly where: string;
28
+ readonly seed?: GraphSnapshot;
29
+ }
30
+ /**
31
+ * THE BACKEND FLAGS, PARSED ONCE. `serve`, `sync-seed`, `mcp` and `apply`
32
+ * all take the same store: a folder of readable JSON, or a SQLite file, and
33
+ * a seed for a first install. One parser, so the four commands cannot come
34
+ * to mean different things by the same words.
35
+ */
36
+ export declare function backendFrom(argv: readonly string[], cwd?: string): Promise<StoreBackend>;
37
+ export declare function serve(argv: readonly string[]): Promise<number>;
38
+ /**
39
+ * `graview sync-seed` — default content moves without a wipe.
40
+ *
41
+ * Read-only unless `--apply` is said, because the steps are the product: a
42
+ * person or an agent reads what WOULD change and decides. Applying lands
43
+ * them through `applySteps`, which is `receive` on the open store — the
44
+ * adapter hears it like any change, the log carries it with its inverse,
45
+ * and undo is the ordinary undo.
46
+ */
47
+ export declare function syncSeed(argv: readonly string[]): Promise<number>;
48
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAc,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAIpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW,q9BAevB,CAAC;AAEF,iGAAiG;AACjG,eAAO,MAAM,WAAW,2FAA2F,CAAC;AAEpH,wBAAgB,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI9E;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,kBAAkB,CAAC,MAAM,CAAC,GAAG;QAC7C,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QACrD,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;KAC3D,CAAC;IACF,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,SAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAUrG;AAoBD,wBAAsB,KAAK,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBpE;AAED;;;;;;;;GAQG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAkDvE"}