@markii/pack 0.12.1 → 0.14.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 (2) hide show
  1. package/dist/archive.js +50 -0
  2. package/package.json +2 -2
package/dist/archive.js CHANGED
@@ -38,6 +38,39 @@ const MANIFEST_ENTRY = 'pack.json';
38
38
  const SCRIPT_ENTRY = 'webview.js';
39
39
  const STYLESHEET_ENTRY = 'webview.css';
40
40
  const SCRIPTS_DIR_PREFIX = 'scripts/';
41
+ /** macOS's zip metadata folder — never a real pack file, and never counted toward "is this archive a single wrapped folder" detection below (batch 7 #61). */
42
+ const MACOSX_PREFIX = '__MACOSX/';
43
+ /** Every entry in `paths` that isn't macOS's `__MACOSX` metadata folder or its bare directory entry. */
44
+ function significantEntries(paths) {
45
+ return paths.filter((entry) => entry !== '__MACOSX' && !entry.startsWith(MACOSX_PREFIX));
46
+ }
47
+ /**
48
+ * When every significant entry (ignoring `__MACOSX`) sits under the SAME
49
+ * single top-level directory — the shape produced by zipping a folder
50
+ * rather than its contents — returns that directory's bare name (no
51
+ * trailing slash). Returns `undefined` when there is no such common
52
+ * folder (including: no significant entries at all, or at least one
53
+ * significant entry already sits at the archive root).
54
+ */
55
+ function singleWrappingFolder(paths) {
56
+ const relevant = significantEntries(paths);
57
+ if (relevant.length === 0)
58
+ return undefined;
59
+ let folder;
60
+ for (const entry of relevant) {
61
+ const slash = entry.indexOf('/');
62
+ if (slash === -1)
63
+ return undefined; // already at the archive root
64
+ const top = entry.slice(0, slash);
65
+ if (folder === undefined) {
66
+ folder = top;
67
+ }
68
+ else if (folder !== top) {
69
+ return undefined;
70
+ }
71
+ }
72
+ return folder;
73
+ }
41
74
  function isScriptModulePath(path) {
42
75
  return (path.startsWith(SCRIPTS_DIR_PREFIX) &&
43
76
  path.length > SCRIPTS_DIR_PREFIX.length);
@@ -85,6 +118,23 @@ export async function openPackArchive(bytes, options = {}) {
85
118
  }
86
119
  const paths = await storage.list();
87
120
  if (!paths.includes(MANIFEST_ENTRY)) {
121
+ // Before reporting a bare "missing" (which reads as "there is no
122
+ // pack.json anywhere", and sends a user who zipped a FOLDER instead of
123
+ // its CONTENTS looking for a file that plainly exists one level down),
124
+ // check whether this archive is exactly that shape: every entry
125
+ // wrapped under one common top-level folder (batch 7 #61).
126
+ const wrappingFolder = singleWrappingFolder(paths);
127
+ if (wrappingFolder !== undefined &&
128
+ paths.includes(`${wrappingFolder}/${MANIFEST_ENTRY}`)) {
129
+ return {
130
+ ok: false,
131
+ error: {
132
+ kind: 'missing-entry',
133
+ entry: MANIFEST_ENTRY,
134
+ message: `pack archive rejected: "${MANIFEST_ENTRY}" is inside the folder "${wrappingFolder}/", not at the archive root. Zip the pack's files themselves, not the folder containing them.`,
135
+ },
136
+ };
137
+ }
88
138
  return {
89
139
  ok: false,
90
140
  error: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markii/pack",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "description": "Component pack contract for Markii (docs/packs.md): the pack.json manifest shape, namespace and engine rules, hand-rolled validation, and the .mkp prebuilt-archive reader. No React, no parsing.",
5
5
  "keywords": [
6
6
  "markdown",
@@ -42,6 +42,6 @@
42
42
  "lint": "eslint ."
43
43
  },
44
44
  "dependencies": {
45
- "@markii/bundle": "^0.12.1"
45
+ "@markii/bundle": "^0.14.0"
46
46
  }
47
47
  }