sh3-core 0.20.3 → 0.21.2

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.
@@ -113,29 +113,8 @@ function validatePackageVersion(data, path) {
113
113
  const obj = data;
114
114
  requireString(obj, 'version', path);
115
115
  requireString(obj, 'contractVersion', path);
116
- // Client bundle fields — optional individually, but if either is present both must be.
117
- const hasBundleUrl = 'bundleUrl' in obj && obj.bundleUrl !== undefined;
118
- const hasIntegrity = 'integrity' in obj && obj.integrity !== undefined;
119
- if (hasBundleUrl)
120
- requireString(obj, 'bundleUrl', path);
121
- if (hasIntegrity)
122
- requireString(obj, 'integrity', path);
123
- if (hasBundleUrl !== hasIntegrity) {
124
- throw new RegistryValidationError(path, 'bundleUrl and integrity must be provided together');
125
- }
126
- // Optional server bundle URL.
127
- const hasServerBundleUrl = 'serverBundleUrl' in obj && obj.serverBundleUrl !== undefined;
128
- if (hasServerBundleUrl) {
129
- requireString(obj, 'serverBundleUrl', path);
130
- }
131
- // Optional server bundle integrity hash — provisional, see ADR-015.
132
- if ('serverIntegrity' in obj && obj.serverIntegrity !== undefined) {
133
- requireString(obj, 'serverIntegrity', path);
134
- }
135
- // A version must ship at least one bundle.
136
- if (!hasBundleUrl && !hasServerBundleUrl) {
137
- throw new RegistryValidationError(path, 'expected at least one of bundleUrl+integrity or serverBundleUrl');
138
- }
116
+ requireString(obj, 'archiveUrl', path);
117
+ requireString(obj, 'integrity', path);
139
118
  let requires;
140
119
  if (obj['requires'] !== undefined) {
141
120
  if (!Array.isArray(obj['requires'])) {
@@ -146,10 +125,8 @@ function validatePackageVersion(data, path) {
146
125
  return {
147
126
  version: obj['version'],
148
127
  contractVersion: obj['contractVersion'],
149
- bundleUrl: typeof obj['bundleUrl'] === 'string' ? obj['bundleUrl'] : undefined,
150
- integrity: typeof obj['integrity'] === 'string' ? obj['integrity'] : undefined,
151
- serverBundleUrl: typeof obj['serverBundleUrl'] === 'string' ? obj['serverBundleUrl'] : undefined,
152
- serverIntegrity: typeof obj['serverIntegrity'] === 'string' ? obj['serverIntegrity'] : undefined,
128
+ archiveUrl: obj['archiveUrl'],
129
+ integrity: obj['integrity'],
153
130
  requires,
154
131
  };
155
132
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateRegistryIndex, RegistryValidationError } from './schema.js';
3
+ const VALID_VERSION = {
4
+ version: '1.0.0',
5
+ contractVersion: '1',
6
+ archiveUrl: 'https://example.com/pkg-1.0.0.sh3pkg',
7
+ integrity: 'sha384-abc123',
8
+ };
9
+ const VALID_ENTRY = {
10
+ id: 'my-shard',
11
+ type: 'shard',
12
+ label: 'My Shard',
13
+ description: 'A test shard',
14
+ author: { name: 'Test Author' },
15
+ versions: [VALID_VERSION],
16
+ };
17
+ const VALID_INDEX = { version: 1, packages: [VALID_ENTRY] };
18
+ describe('validateRegistryIndex', () => {
19
+ it('accepts a valid index with archiveUrl and integrity', () => {
20
+ const result = validateRegistryIndex(VALID_INDEX);
21
+ expect(result.packages[0].versions[0].archiveUrl).toBe('https://example.com/pkg-1.0.0.sh3pkg');
22
+ expect(result.packages[0].versions[0].integrity).toBe('sha384-abc123');
23
+ });
24
+ it('rejects a version missing archiveUrl', () => {
25
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [{ version: '1.0.0', contractVersion: '1', integrity: 'sha384-abc' }] })] });
26
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
27
+ });
28
+ it('rejects a version missing integrity', () => {
29
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [{ version: '1.0.0', contractVersion: '1', archiveUrl: 'https://x.com/a.sh3pkg' }] })] });
30
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
31
+ });
32
+ it('accepts optional requires array', () => {
33
+ const withRequires = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [Object.assign(Object.assign({}, VALID_VERSION), { requires: [{ id: 'dep-shard', versionRange: '^1.0.0' }] })] })] });
34
+ const result = validateRegistryIndex(withRequires);
35
+ expect(result.packages[0].versions[0].requires[0].id).toBe('dep-shard');
36
+ });
37
+ it('rejects a version with requires that has an invalid entry', () => {
38
+ const bad = Object.assign(Object.assign({}, VALID_INDEX), { packages: [Object.assign(Object.assign({}, VALID_ENTRY), { versions: [Object.assign(Object.assign({}, VALID_VERSION), { requires: [{ id: 'dep' }] })] })] });
39
+ expect(() => validateRegistryIndex(bad)).toThrow(RegistryValidationError);
40
+ });
41
+ });
@@ -81,9 +81,9 @@ export interface PackageEntry {
81
81
  /**
82
82
  * A specific published version of a package.
83
83
  *
84
- * Each version ships a self-contained pre-built ESM bundle at `bundleUrl`.
85
- * The `integrity` field is an SRI hash (sha384 recommended) used to verify
86
- * the download before execution.
84
+ * Each version ships a `.sh3pkg` ZIP archive at `archiveUrl`.
85
+ * The archive contains `manifest.json` and at least one of `client.js` / `server.js`.
86
+ * The `integrity` SRI hash verifies the download before execution.
87
87
  */
88
88
  export interface PackageVersion {
89
89
  /**
@@ -98,36 +98,17 @@ export interface PackageVersion {
98
98
  */
99
99
  contractVersion: string;
100
100
  /**
101
- * Absolute or registry-relative URL to the pre-built ESM bundle.
102
- * The client fetches this URL and verifies the download against `integrity`
103
- * before executing. Optional for server-only packages that ship only a
104
- * `serverBundleUrl` — in that case `integrity` must also be omitted.
101
+ * URL to the `.sh3pkg` ZIP archive for this version.
102
+ * Absolute or registry-relative. The archive contains `manifest.json`
103
+ * plus at least one of `client.js` / `server.js`.
105
104
  */
106
- bundleUrl?: string;
105
+ archiveUrl: string;
107
106
  /**
108
- * SRI integrity hash for the bundle file.
107
+ * SRI integrity hash of the archive ZIP.
109
108
  * Format: `"<algorithm>-<base64digest>"` (e.g. `"sha384-abc123..."`).
110
109
  * Algorithms: sha256, sha384 (recommended), sha512.
111
- * See: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
112
- * Omitted when the package has no client bundle.
113
110
  */
114
- integrity?: string;
115
- /**
116
- * Optional URL to the server-side bundle for shards that have a backend
117
- * component. Same resolution rules as `bundleUrl` (absolute or registry-
118
- * relative). Only present when the shard declares `serverBundle` in its
119
- * manifest.
120
- */
121
- serverBundleUrl?: string;
122
- /**
123
- * SRI integrity hash for the server bundle. Same format as `integrity`.
124
- * Optional in contract v1 for back-compat with registries that predate
125
- * server bundles. Will become required when the formal registry spec
126
- * lands (see ADR-015 proposal). When absent, the client skips the SRI
127
- * check at download time and logs a warning — the unverified bundle is
128
- * still installed.
129
- */
130
- serverIntegrity?: string;
111
+ integrity: string;
131
112
  /**
132
113
  * Other shards that must be installed and active before this package
133
114
  * can be loaded. Optional — omit if the package has no dependencies.
@@ -260,9 +241,7 @@ export interface PackageMeta {
260
241
  */
261
242
  sourceRegistry: string;
262
243
  /**
263
- * SRI hash to verify the downloaded bundle against before executing.
264
- * Must match `PackageVersion.integrity`. Omitted for server-only packages
265
- * that ship no client bundle.
244
+ * SRI hash of the archive this package was installed from. Kept for audit purposes.
266
245
  */
267
246
  integrity?: string;
268
247
  /**
@@ -270,14 +249,10 @@ export interface PackageMeta {
270
249
  * Undefined if no dependencies.
271
250
  */
272
251
  requires?: RequiredDependency[];
273
- /**
274
- * SRI hash for the server bundle. Only present when the package has a
275
- * server component.
276
- */
277
- serverIntegrity?: string;
278
- /**
279
- * Whether this package includes a server bundle that needs to be pushed
280
- * to the server after client-side installation.
281
- */
282
- hasServerBundle?: boolean;
252
+ }
253
+ /** Payload sent to the server to trigger a server-side install. */
254
+ export interface RemoteInstallRequest {
255
+ registryUrl: string;
256
+ packageId: string;
257
+ version: string;
283
258
  }
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export declare const VERSION = "0.20.3";
2
+ export declare const VERSION = "0.21.2";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export const VERSION = '0.20.3';
2
+ export const VERSION = '0.21.2';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.20.3",
3
+ "version": "0.21.2",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"
@@ -39,7 +39,8 @@
39
39
  "test:watch": "vitest"
40
40
  },
41
41
  "dependencies": {
42
- "esm-env": "^1.1.0"
42
+ "esm-env": "^1.1.0",
43
+ "fflate": "^0.8.3"
43
44
  },
44
45
  "peerDependencies": {
45
46
  "svelte": "^5.0.0",