sh3-server 0.10.2 → 0.10.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.
- package/app/assets/index-BOrfojaa.css +1 -0
- package/app/assets/index-C0N0oGGx.js +18 -0
- package/app/assets/index-C0N0oGGx.js.map +1 -0
- package/app/index.html +2 -2
- package/dist/doc-store/store.d.ts +1 -0
- package/dist/doc-store/store.js +15 -0
- package/dist/routes/docs.js +21 -0
- package/dist/shard-router.js +29 -2
- package/package.json +1 -1
- package/app/assets/index-Boy8llCh.js +0 -17
- package/app/assets/index-Boy8llCh.js.map +0 -1
- package/app/assets/index-TUefqqjg.css +0 -1
package/app/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>SH3</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-C0N0oGGx.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BOrfojaa.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="app"></div>
|
|
@@ -67,5 +67,6 @@ export declare class TenantDocStore {
|
|
|
67
67
|
applyFromPeer(tenant: string, input: ApplyFromPeerInput): Promise<ApplyResult>;
|
|
68
68
|
listConflicts(tenant: string): Promise<ConflictRef[]>;
|
|
69
69
|
readConflict(tenant: string, shardId: string, path: string): Promise<ConflictFile | null>;
|
|
70
|
+
readBranchContent(tenant: string, shardId: string, path: string, origin: string): Promise<string | null>;
|
|
70
71
|
resolveConflict(tenant: string, shardId: string, path: string, choice: 'local' | string | Buffer): Promise<void>;
|
|
71
72
|
}
|
package/dist/doc-store/store.js
CHANGED
|
@@ -306,6 +306,21 @@ export class TenantDocStore {
|
|
|
306
306
|
async readConflict(tenant, shardId, path) {
|
|
307
307
|
return this.#conflicts.read(tenant, shardId, path);
|
|
308
308
|
}
|
|
309
|
+
async readBranchContent(tenant, shardId, path, origin) {
|
|
310
|
+
const cf = await this.#conflicts.read(tenant, shardId, path);
|
|
311
|
+
if (!cf)
|
|
312
|
+
return null;
|
|
313
|
+
const branch = cf.branches.find((b) => b.origin === origin);
|
|
314
|
+
if (!branch)
|
|
315
|
+
return null;
|
|
316
|
+
const content = branch.content;
|
|
317
|
+
if (typeof content === 'string')
|
|
318
|
+
return content;
|
|
319
|
+
if (content && typeof content.toString === 'function') {
|
|
320
|
+
return content.toString('utf-8');
|
|
321
|
+
}
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
309
324
|
async resolveConflict(tenant, shardId, path, choice) {
|
|
310
325
|
const cf = await this.#conflicts.read(tenant, shardId, path);
|
|
311
326
|
if (!cf)
|
package/dist/routes/docs.js
CHANGED
|
@@ -44,6 +44,27 @@ export function createDocsRouter(store, settings) {
|
|
|
44
44
|
return c.notFound();
|
|
45
45
|
return c.json(await store.list(tenant, shard));
|
|
46
46
|
});
|
|
47
|
+
// Branch content read — conflict-branch by origin. Registered before the
|
|
48
|
+
// generic read handler so the `/branch` suffix isn't swallowed as a path.
|
|
49
|
+
router.get('/:tenant/:shard/*', async (c, next) => {
|
|
50
|
+
const { tenant, shard } = c.req.param();
|
|
51
|
+
const prefix = `/api/docs/${tenant}/${shard}/`;
|
|
52
|
+
const rawPath = c.req.path.replace(prefix, '');
|
|
53
|
+
if (!rawPath.endsWith('/branch'))
|
|
54
|
+
return next();
|
|
55
|
+
if (isReservedShardId(shard))
|
|
56
|
+
return c.notFound();
|
|
57
|
+
const filePath = rawPath.replace(/\/branch$/, '');
|
|
58
|
+
if (!filePath)
|
|
59
|
+
return c.json({ error: 'Missing file path' }, 400);
|
|
60
|
+
const origin = c.req.query('origin');
|
|
61
|
+
if (!origin)
|
|
62
|
+
return c.json({ error: 'Missing origin query param' }, 400);
|
|
63
|
+
const content = await store.readBranchContent(tenant, shard, filePath, origin);
|
|
64
|
+
if (content === null)
|
|
65
|
+
return c.notFound();
|
|
66
|
+
return c.text(content);
|
|
67
|
+
});
|
|
47
68
|
// Read
|
|
48
69
|
router.get('/:tenant/:shard/*', async (c) => {
|
|
49
70
|
const { tenant, shard } = c.req.param();
|
package/dist/shard-router.js
CHANGED
|
@@ -72,6 +72,24 @@ function makeTenantDocumentAPI(store, tenant, callingShardId, permissions) {
|
|
|
72
72
|
resolveConflict: (shardId, path, choice) => store.resolveConflict(tenant, shardId, path, choice),
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Bootstrap middleware installed on every shard sub-app before its routes
|
|
77
|
+
* are wired. Copies `session` / `caller` from `c.env` (forwarded by the
|
|
78
|
+
* outer handler) into the sub-app's context variables, so scope guards
|
|
79
|
+
* reading `c.get('caller')` see the same identity the outer cascade
|
|
80
|
+
* resolved. Without this, the sub-app's fresh context is empty and
|
|
81
|
+
* `adminOnly` / `scopeRequired` / `tenantRequired` fail regardless of role.
|
|
82
|
+
*/
|
|
83
|
+
function contextBootstrap() {
|
|
84
|
+
return async (c, next) => {
|
|
85
|
+
const env = c.env;
|
|
86
|
+
if (env?.session !== undefined)
|
|
87
|
+
c.set('session', env.session);
|
|
88
|
+
if (env?.caller !== undefined)
|
|
89
|
+
c.set('caller', env.caller);
|
|
90
|
+
await next();
|
|
91
|
+
};
|
|
92
|
+
}
|
|
75
93
|
/**
|
|
76
94
|
* Dynamic shard route manager. Holds a Map of shard Hono sub-apps
|
|
77
95
|
* and delegates requests from a single wildcard route.
|
|
@@ -90,6 +108,7 @@ export class ShardRouter {
|
|
|
90
108
|
throw new Error(`${shardId}/server.js invalid export shape — expected { id, routes }`);
|
|
91
109
|
}
|
|
92
110
|
const router = new Hono();
|
|
111
|
+
router.use('*', contextBootstrap());
|
|
93
112
|
await shard.routes(router, this.#buildContext(shard.id, ctx));
|
|
94
113
|
mkdirSync(join(ctx.pkgDir, 'data'), { recursive: true });
|
|
95
114
|
this.shards.set(shardId, { app: router, shard });
|
|
@@ -105,6 +124,7 @@ export class ShardRouter {
|
|
|
105
124
|
throw new Error(`${shardId} static mount — expected { id, routes }, got ${typeof mod}`);
|
|
106
125
|
}
|
|
107
126
|
const router = new Hono();
|
|
127
|
+
router.use('*', contextBootstrap());
|
|
108
128
|
await mod.routes(router, this.#buildContext(mod.id, ctx));
|
|
109
129
|
mkdirSync(join(ctx.pkgDir, 'data'), { recursive: true });
|
|
110
130
|
this.shards.set(shardId, { app: router, shard: mod });
|
|
@@ -193,8 +213,15 @@ export class ShardRouter {
|
|
|
193
213
|
// @ts-expect-error duplex needed for streaming bodies
|
|
194
214
|
duplex: 'half',
|
|
195
215
|
});
|
|
196
|
-
// Forward session
|
|
197
|
-
|
|
216
|
+
// Forward session and caller from the outer sessionAuth + resolveCaller
|
|
217
|
+
// cascade. `contextBootstrap` inside the sub-app copies these into
|
|
218
|
+
// `c.var` so scope guards (adminOnly / scopeRequired / tenantRequired)
|
|
219
|
+
// resolve against the same identity as routes mounted on the main tree.
|
|
220
|
+
const env = {
|
|
221
|
+
...c.env,
|
|
222
|
+
session: c.get('session') ?? null,
|
|
223
|
+
caller: c.get('caller') ?? null,
|
|
224
|
+
};
|
|
198
225
|
return await entry.app.fetch(strippedRequest, env);
|
|
199
226
|
}
|
|
200
227
|
catch (err) {
|