memory-extract 0.1.3 → 0.1.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/README.md CHANGED
@@ -0,0 +1,83 @@
1
+ # memory-extract
2
+
3
+ Embed files into a PNG image and open them back from the PNG alone.
4
+
5
+ Use it to ship static sites, single assets or small apps as portable `.png` cartridges. The image still opens as a normal PNG and the payload lives in a custom chunk inside the file.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install memory-extract
11
+ ```
12
+
13
+ ## CLI
14
+
15
+ ### `memory-pack [path] [filename]`
16
+
17
+ Pack a file or directory into a memory PNG
18
+
19
+ ```bash
20
+ memory-pack # pack current directory → ./<folder-name>.png
21
+ memory-pack dist # pack ./dist → ./dist.png
22
+ memory-pack dist App # pack ./dist → ./App.png
23
+ ```
24
+
25
+
26
+ | Argument | Default | Description |
27
+ | ---------- | ------------------ | ----------------------------------------- |
28
+ | `path` | `.` | File or directory to pack |
29
+ | `filename` | basename of `path` | Output PNG name (`.png` added if missing) |
30
+
31
+
32
+
33
+ | Flag | Description |
34
+ | ---------------- | -------------------------------------------- |
35
+ | `--cover <path>` | Cover image instead of the default blank PNG |
36
+ | `--out <path>` | Output PNG path |
37
+
38
+
39
+
40
+
41
+ ### `memory-unpack <png> [out-dir]`
42
+
43
+ Extract the packed files from a memory PNG
44
+
45
+ ```bash
46
+ memory-unpack dist.png
47
+ memory-unpack dist.png ./restored
48
+ ```
49
+
50
+
51
+
52
+ ### `memory-play <png>`
53
+
54
+ Open a memory PNG in the browser
55
+
56
+ ```bash
57
+ memory-play App.png
58
+ ```
59
+
60
+ ## Library
61
+
62
+ Browser exports:
63
+
64
+ ```js
65
+ import {
66
+ extractMemory,
67
+ listManifestFiles,
68
+ readMemoryFile,
69
+ createMemoryBlob,
70
+ } from "memory-extract";
71
+ ```
72
+
73
+
74
+
75
+ Node helpers:
76
+
77
+ ```js
78
+ import { embedMemory, encodeMemoryPayload } from "memory-extract/node";
79
+ import { guessMimeType, encodeV2Archive } from "memory-extract/payload";
80
+ ```
81
+
82
+ `createMemoryBlob()` is for in-browser launch via blob URLs. The CLI uses HTTP or `file://` instead.
83
+
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "memory-extract",
3
3
  "description": "Tool for embedding data into PNG images",
4
4
  "license": "MIT",
5
- "version": "0.1.3",
5
+ "version": "0.1.5",
6
6
  "author": "semigarden",
7
7
  "repository": {
8
8
  "type": "git",
package/tools/play.mjs CHANGED
@@ -153,7 +153,7 @@ const playMemoryPng = async (input, label = input) => {
153
153
  filePaths,
154
154
  playMode.entry
155
155
  );
156
- const openUrl = new URL(playMode.entry, baseUrl).href;
156
+ const openUrl = baseUrl;
157
157
 
158
158
  await runServerSession({
159
159
  input,
@@ -16,6 +16,15 @@ export const normalizeUrlPath = (pathname) => {
16
16
  export const urlPathToManifestPath = (urlPath) =>
17
17
  urlPath === "/" ? "" : urlPath.slice(1);
18
18
 
19
+ export const resolveEntryMountPrefix = (entryPath) => {
20
+ const slash = entryPath.lastIndexOf("/");
21
+ if (slash <= 0) {
22
+ return "";
23
+ }
24
+
25
+ return entryPath.slice(0, slash + 1);
26
+ };
27
+
19
28
  const hrefForEntry = (urlPath, name, isDirectory) => {
20
29
  const base = urlPath === "/" ? "" : urlPath;
21
30
  const href = `${base}/${name}${isDirectory ? "/" : ""}`.replace(/\/+/g, "/");
@@ -30,14 +39,12 @@ export const resolveManifestPathForUrl = (urlPath, manifestPaths, entryPath = ""
30
39
  return direct;
31
40
  }
32
41
 
33
- const entryDir = entryPath.includes("/")
34
- ? entryPath.slice(0, entryPath.lastIndexOf("/"))
35
- : "";
42
+ const mountPrefix = resolveEntryMountPrefix(entryPath);
36
43
 
37
- if (direct && entryDir) {
38
- const joined = `${entryDir}/${direct}`.replace(/\/+/g, "/");
39
- if (manifestSet.has(joined)) {
40
- return joined;
44
+ if (direct && mountPrefix) {
45
+ const mounted = `${mountPrefix}${direct}`.replace(/\/+/g, "/");
46
+ if (manifestSet.has(mounted)) {
47
+ return mounted;
41
48
  }
42
49
  }
43
50
 
@@ -68,27 +75,51 @@ const serveManifestFile = (response, manifest, fileBytes, manifestPath) => {
68
75
  response.end(body);
69
76
  };
70
77
 
78
+ const isHtmlEntryPath = (entryPath) =>
79
+ entryPath.endsWith(".html") || entryPath.endsWith(".htm");
80
+
71
81
  export const createMemoryPlayHandler = (manifest, fileBytes, filePaths, entryPath) => {
72
82
  const manifestPaths = filePaths ?? listManifestFiles(manifest);
83
+ const mountPrefix = resolveEntryMountPrefix(entryPath);
73
84
 
74
85
  return (request, response) => {
75
86
  try {
76
87
  const url = new URL(request.url ?? "/", "http://localhost");
77
88
  const urlPath = normalizeUrlPath(url.pathname);
78
89
 
90
+ if (mountPrefix) {
91
+ const mountRoot = `/${mountPrefix.replace(/\/$/, "")}`;
92
+
93
+ if (urlPath === mountRoot || urlPath.startsWith(`${mountRoot}/`)) {
94
+ const relative =
95
+ urlPath === mountRoot ? "/" : urlPath.slice(mountRoot.length);
96
+ response.writeHead(302, {
97
+ Location: relative.startsWith("/") ? relative : `/${relative}`,
98
+ });
99
+ response.end();
100
+ return;
101
+ }
102
+ }
103
+
104
+ let manifestPath = null;
105
+
79
106
  if (urlPath === "/") {
80
- response.writeHead(302, {
81
- Location: `/${entryPath}`,
82
- });
83
- response.end();
84
- return;
107
+ manifestPath = entryPath;
108
+ } else {
109
+ manifestPath = resolveManifestPathForUrl(
110
+ urlPath,
111
+ manifestPaths,
112
+ entryPath
113
+ );
85
114
  }
86
115
 
87
- const manifestPath = resolveManifestPathForUrl(
88
- urlPath,
89
- manifestPaths,
90
- entryPath
91
- );
116
+ if (
117
+ !manifestPath &&
118
+ isHtmlEntryPath(entryPath) &&
119
+ request.method === "GET"
120
+ ) {
121
+ manifestPath = entryPath;
122
+ }
92
123
 
93
124
  if (!manifestPath) {
94
125
  response.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" });
package/tools/unpack.mjs CHANGED
@@ -27,7 +27,7 @@ const parseArgs = async (argv) => {
27
27
  const main = async () => {
28
28
  const { input, explicitOut, host } = await parseArgs(process.argv.slice(2));
29
29
  const buffer = await readFile(input);
30
- const { png, manifest, fileBytes, version } = extractMemory(buffer);
30
+ const { manifest, fileBytes, version } = extractMemory(buffer);
31
31
  const outDir = resolveUnpackDir({
32
32
  manifestSource: manifest.source ?? "",
33
33
  explicitOut,
@@ -35,7 +35,6 @@ const main = async () => {
35
35
  });
36
36
 
37
37
  await mkdir(outDir, { recursive: true });
38
- await writeFile(path.join(outDir, "cover.png"), png);
39
38
 
40
39
  for (const filePath of listManifestFiles(manifest)) {
41
40
  const target = resolveSafePath(
@@ -48,11 +47,6 @@ const main = async () => {
48
47
  await writeFile(target, readMemoryFile(manifest, filePath, fileBytes));
49
48
  }
50
49
 
51
- await writeFile(
52
- path.join(outDir, "manifest.json"),
53
- `${JSON.stringify(manifest, null, 2)}\n`
54
- );
55
-
56
50
  console.log(
57
51
  `Extracted ${listManifestFiles(manifest).length} files to ${outDir} (format v${version})`
58
52
  );