sh3-core 0.4.1 → 0.4.3

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.
@@ -28,6 +28,12 @@ export interface AppManifest {
28
28
  * user customization.
29
29
  */
30
30
  layoutVersion: number;
31
+ /**
32
+ * When true, the app is only visible on the shell home screen in
33
+ * admin mode. Server-side routes may still enforce their own auth
34
+ * independently. Defaults to false (visible to everyone).
35
+ */
36
+ admin?: boolean;
31
37
  }
32
38
  /**
33
39
  * Context object passed to `App.activate`. Provides app-scoped state zones
package/dist/build.js CHANGED
@@ -112,7 +112,7 @@ export function sh3Artifact(options = {}) {
112
112
  }
113
113
  },
114
114
  async closeBundle() {
115
- var _a;
115
+ var _a, _b, _c, _d;
116
116
  if (!entryFileName)
117
117
  return;
118
118
  const clientSrc = join(outDir, entryFileName);
@@ -122,7 +122,7 @@ export function sh3Artifact(options = {}) {
122
122
  try {
123
123
  source = readFileSync(clientSrc, 'utf-8');
124
124
  }
125
- catch (_b) {
125
+ catch (_e) {
126
126
  console.warn('[sh3-artifact] Could not read entry chunk:', clientSrc);
127
127
  return;
128
128
  }
@@ -148,8 +148,28 @@ export function sh3Artifact(options = {}) {
148
148
  copyFileSync(options.serverEntry, join(outDir, 'server.js'));
149
149
  hasServer = true;
150
150
  }
151
+ // --- Read fallback metadata from package.json ---
152
+ let pkgDescription;
153
+ let pkgAuthor;
154
+ try {
155
+ const pkg = JSON.parse(readFileSync('package.json', 'utf-8'));
156
+ pkgDescription = typeof pkg.description === 'string' ? pkg.description : undefined;
157
+ pkgAuthor = typeof pkg.author === 'string'
158
+ ? pkg.author
159
+ : typeof ((_a = pkg.author) === null || _a === void 0 ? void 0 : _a.name) === 'string' ? pkg.author.name : undefined;
160
+ }
161
+ catch ( /* no package.json or unreadable */_f) { /* no package.json or unreadable */ }
151
162
  // --- Write manifest.json ---
152
- const manifest = Object.assign(Object.assign({ id: id || 'unknown', type, label: label || id || 'unknown', version: version || '0.0.0', contractVersion: 1, client: 'client.js' }, (hasServer ? { server: 'server.js' } : {})), ((_a = options.manifest) !== null && _a !== void 0 ? _a : {}));
163
+ const overrides = (_b = options.manifest) !== null && _b !== void 0 ? _b : {};
164
+ const finalDescription = (_c = overrides.description) !== null && _c !== void 0 ? _c : pkgDescription;
165
+ const finalAuthor = (_d = overrides.author) !== null && _d !== void 0 ? _d : pkgAuthor;
166
+ if (!finalDescription) {
167
+ throw new Error('[sh3-artifact] Missing "description". Add it to package.json or pass it via sh3Artifact({ manifest: { description } }).');
168
+ }
169
+ if (!finalAuthor) {
170
+ throw new Error('[sh3-artifact] Missing "author". Add it to package.json or pass it via sh3Artifact({ manifest: { author } }).');
171
+ }
172
+ const manifest = Object.assign(Object.assign(Object.assign({ id: id || 'unknown', type, label: label || id || 'unknown', version: version || '0.0.0', contractVersion: 1, client: 'client.js' }, (hasServer ? { server: 'server.js' } : {})), { description: finalDescription, author: finalAuthor }), overrides);
153
173
  writeFileSync(join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2) + '\n');
154
174
  // --- Log summary ---
155
175
  const files = ['manifest.json', 'client.js'];
@@ -13,6 +13,7 @@ export const diagnosticApp = {
13
13
  version: '0.1.0',
14
14
  requiredShards: ['diagnostic'],
15
15
  layoutVersion: 1,
16
+ admin: true,
16
17
  },
17
18
  initialLayout: {
18
19
  type: 'tabs',
@@ -7,6 +7,5 @@ export { HttpDocumentBackend } from './documents/http-backend';
7
7
  export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
8
8
  export type { InstalledPackage, InstallResult, PackageMeta } from './registry/types';
9
9
  export { initAuth, elevate, deescalate } from './auth/index';
10
- export { adminAppIds } from './host';
11
10
  export { createShell } from './createShell';
12
11
  export type { ShellConfig } from './createShell';
@@ -12,6 +12,5 @@ export { HttpDocumentBackend } from './documents/http-backend';
12
12
  export { installPackage, uninstallPackage, listInstalledPackages, loadInstalledPackages, } from './registry/index';
13
13
  // Admin mode (host-only — elevate/deescalate drive the shell UI, initAuth runs at boot).
14
14
  export { initAuth, elevate, deescalate } from './auth/index';
15
- export { adminAppIds } from './host';
16
15
  // Shell boot factory.
17
16
  export { createShell } from './createShell';
package/dist/host.d.ts CHANGED
@@ -14,9 +14,4 @@ export interface BootstrapConfig {
14
14
  excludeApps?: string[];
15
15
  }
16
16
  export declare function bootstrap(config?: BootstrapConfig): Promise<void>;
17
- /**
18
- * Set of app IDs that require admin mode. Framework-internal — used by
19
- * the shell home to gate visibility. Not part of the app contract.
20
- */
21
- export declare const adminAppIds: ReadonlySet<string>;
22
17
  export { installPackage, listInstalledPackages } from './registry/installer';
package/dist/host.js CHANGED
@@ -77,9 +77,4 @@ export async function bootstrap(config) {
77
77
  await launchApp(lastId);
78
78
  }
79
79
  }
80
- /**
81
- * Set of app IDs that require admin mode. Framework-internal — used by
82
- * the shell home to gate visibility. Not part of the app contract.
83
- */
84
- export const adminAppIds = new Set(['sh3-store-app', 'diagnostic-app']);
85
80
  export { installPackage, listInstalledPackages } from './registry/installer';
@@ -82,9 +82,8 @@ export async function fetchRegistries(urls) {
82
82
  */
83
83
  export async function fetchBundle(version, sourceRegistry) {
84
84
  let url = version.bundleUrl;
85
- if (sourceRegistry && url.startsWith('/')) {
86
- const base = new URL(sourceRegistry);
87
- url = `${base.origin}${url}`;
85
+ if (sourceRegistry && !/^https?:\/\//i.test(url)) {
86
+ url = new URL(url, sourceRegistry).href;
88
87
  }
89
88
  const response = await fetch(url);
90
89
  if (!response.ok) {
@@ -71,7 +71,7 @@ function validatePackageEntry(data, path) {
71
71
  }
72
72
  const obj = data;
73
73
  requireString(obj, 'id', path);
74
- requireOneOf(obj, 'type', ['shard', 'app'], path);
74
+ requireOneOf(obj, 'type', ['shard', 'app', 'combo'], path);
75
75
  requireString(obj, 'label', path);
76
76
  requireString(obj, 'description', path);
77
77
  // author: { name: string }
@@ -47,7 +47,7 @@ export interface PackageEntry {
47
47
  * Whether this package is a shard (module that contributes views/commands)
48
48
  * or an app (composition document that wires shards into a layout).
49
49
  */
50
- type: 'shard' | 'app';
50
+ type: 'shard' | 'app' | 'combo';
51
51
  /**
52
52
  * Human-readable display name shown in the store UI.
53
53
  * Should be short (under 40 characters).
@@ -161,7 +161,7 @@ export interface InstalledPackage {
161
161
  /**
162
162
  * Whether this is a shard or an app.
163
163
  */
164
- type: 'shard' | 'app';
164
+ type: 'shard' | 'app' | 'combo';
165
165
  /**
166
166
  * The version that was installed.
167
167
  */
@@ -226,7 +226,7 @@ export interface PackageMeta {
226
226
  /**
227
227
  * Whether this is a shard or an app.
228
228
  */
229
- type: 'shard' | 'app';
229
+ type: 'shard' | 'app' | 'combo';
230
230
  /**
231
231
  * The version being installed. Matches `PackageVersion.version`.
232
232
  */
@@ -8,11 +8,10 @@
8
8
 
9
9
  import { listRegisteredApps, launchApp, isAdmin } from '../api';
10
10
  import { elevate, deescalate } from '../auth/index';
11
- import { adminAppIds } from '../host';
12
11
 
13
12
  const apps = $derived(listRegisteredApps());
14
- const userApps = $derived(apps.filter(m => !adminAppIds.has(m.id)));
15
- const adminApps = $derived(apps.filter(m => adminAppIds.has(m.id)));
13
+ const userApps = $derived(apps.filter(m => !m.admin));
14
+ const adminApps = $derived(apps.filter(m => m.admin));
16
15
  const elevated = $derived(isAdmin());
17
16
 
18
17
  let keyInput = $state('');
@@ -4,8 +4,7 @@
4
4
  * packages.
5
5
  *
6
6
  * Framework-shipped: registered in host.ts during bootstrap.
7
- * Admin-gated: the shell home checks adminAppIds to gate visibility;
8
- * the AppManifest itself carries no admin flag (ADR-011).
7
+ * Admin-gated via manifest flag (ADR-011).
9
8
  */
10
9
  import type { App } from '../apps/types';
11
10
  export declare const storeApp: App;
@@ -4,8 +4,7 @@
4
4
  * packages.
5
5
  *
6
6
  * Framework-shipped: registered in host.ts during bootstrap.
7
- * Admin-gated: the shell home checks adminAppIds to gate visibility;
8
- * the AppManifest itself carries no admin flag (ADR-011).
7
+ * Admin-gated via manifest flag (ADR-011).
9
8
  */
10
9
  export const storeApp = {
11
10
  manifest: {
@@ -14,6 +13,7 @@ export const storeApp = {
14
13
  version: '0.1.0',
15
14
  requiredShards: ['sh3-store'],
16
15
  layoutVersion: 1,
16
+ admin: true,
17
17
  },
18
18
  initialLayout: {
19
19
  type: 'tabs',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"