@sjcrh/proteinpaint-front 2.207.1 → 2.209.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.
package/README.md CHANGED
@@ -11,33 +11,24 @@ an embedder.
11
11
 
12
12
  ## Usage
13
13
 
14
- The recommended usage is to include a postinstall script in your project as follows.
15
- ```json
16
- "scripts": {
17
- "postinstall": "proteinpaint-front https://my.host.tld"
18
- }
19
- ```
14
+ This package is used inside a ppfull container (https://github.com/stjude/proteinpaint/pkgs/container/ppfull).
15
+ The container image ensures that a matching proteinpaint-server is used to serve the data as expected by
16
+ proteinpaint-client code. Standalone usage outside of a ppfull container is not recommended. From within
17
+ the container, the command below is used:
20
18
 
21
- You can manually test your project's postinstall lifecyle script by running the following
22
- to extract the Proteinpaint bundles, with the correct public path for the dynamically
23
- loaded Proteinpaint bundles.
24
19
  ```bash
25
- npx proteinpaint-front [URL_PUBLIC_PATH]
20
+ npx proteinpaint-front
26
21
  ```
27
22
 
28
- If you omit the URL_PUBLIC_PATH argument above, then it will default to `'.'`, which assumes that the bundle is served by
29
- the same PP server. But note that in this case, the web bundle or server host may NOT be embedded or used as host from
30
- other domains, even if CORS is allowed, since script bundles will not be sourced properly and loaded dynamically.
31
-
32
- Note that you can serve the web bundle using the Proteinpaint server or any web server,
33
- see the example in the Develop section.
34
-
35
23
  ## Develop
36
24
 
25
+ The usual dev workflow typically focuses on the client workspace development. For troubleshooting builds
26
+ for production, the following may be performed:
27
+
37
28
  ```bash
38
- # must run the module bundling of proteinpaint-client
39
- cd ../client
40
- npm run rollw
29
+ # must run the module bundling of proteinpaint-client, either from sjpp dev process or below
30
+ cd ../client
31
+ npm run dev
41
32
 
42
33
  # in another terminal
43
34
  cd ../front
package/bundles.tgz CHANGED
Binary file
package/init.js CHANGED
@@ -6,54 +6,87 @@ const ps = require('child_process')
6
6
  const fs = require('fs')
7
7
  const { execSync } = require('child_process')
8
8
 
9
- let URLPATH = process.argv[2] || '.'
10
- if (URLPATH.endsWith('/')) URLPATH = URLPATH.slice(0, -1)
11
9
  const publicBinOnly = process.argv.includes('--publicBinOnly')
12
10
 
13
11
  const CWD = process.cwd()
12
+ const PUBLIC_DIR = `${CWD}/public`
13
+ // The client bundle is generated into a PER-CONTAINER dir at CWD/bin — a sibling of public/, in the
14
+ // container's own writable layer, NOT under any mount — and is served at /bin by the server, which
15
+ // routes /bin to CWD/bin when it exists (see the server's serverconfig binDir + app.middlewares.js).
16
+ //
17
+ // It is deliberately NOT written into public/: when several instances bind-mount the SAME public/,
18
+ // regenerating a shared public/bin at startup makes them clobber one another (one instance's recursive
19
+ // rmdir races another's tar extract -> "ENOTEMPTY, Directory not empty: .../public/bin", crashing a
20
+ // startup). Generating per-container removes the shared write entirely, so concurrent starts can't race
21
+ // — no lock or cross-instance key needed. It also makes a rolling deploy safe: init never mutates the
22
+ // shared public/bin, so instances still on an older release keep serving their own public/bin while new
23
+ // instances serve their CWD/bin. An older image that has no CWD/bin also still works — the server falls
24
+ // back to serving /bin from public/bin.
25
+ const BIN_DIR = `${CWD}/bin`
26
+ // A distinctively named marker written LAST, only after the bundle is fully extracted. It serves two
27
+ // purposes:
28
+ // - completion: reuse requires it, so a container that stops mid-generation (on a partial extraction)
29
+ // regenerates on its next start instead of reusing an incomplete bundle forever — which the health
30
+ // gate can't catch, since an incomplete proteinpaint.js still returns 200.
31
+ // - ownership: the server publishes CWD/bin at /bin ONLY when this marker is present (see the server's
32
+ // app.middlewares.js), so launching the server package from a consumer project never exposes an
33
+ // unrelated CWD/bin (e.g. that project's own bin/ scripts) over HTTP. The name is distinctive so it
34
+ // won't collide with a consumer's files.
35
+ const BIN_READY = `${BIN_DIR}/.pp-bundle-ready`
14
36
 
15
37
  console.log('CWD', CWD)
16
38
  try {
17
- if (!fs.existsSync(`${CWD}/public`)) {
39
+ if (!fs.existsSync(PUBLIC_DIR)) {
18
40
  console.log(`making a public directory at ${CWD}`)
19
- fs.mkdirSync(`${CWD}/public`)
20
- }
21
- if (fs.existsSync(`${CWD}/public/bin`)) {
22
- console.log(`removing the old public/bin at ${CWD}`)
23
- // should update as part of v16 upgrade
24
- fs.rmdirSync(`${CWD}/public/bin`, { recursive: true, force: true }, () => {})
41
+ fs.mkdirSync(PUBLIC_DIR)
25
42
  }
43
+ // index.html / cards are ensured independently of the bundle below (idempotent, and needed even when
44
+ // the bundle is reused), so a skipped regeneration still leaves the public scaffolding in place.
26
45
  if (!publicBinOnly) {
27
- if (!fs.existsSync(`${CWD}/public/index.html`)) {
46
+ if (!fs.existsSync(`${PUBLIC_DIR}/index.html`)) {
28
47
  console.log(`creating a public/index.html file`)
29
- fs.copyFileSync(path.join(__dirname, './public/index.html'), `${CWD}/public/index.html`)
48
+ fs.copyFileSync(path.join(__dirname, './public/index.html'), `${PUBLIC_DIR}/index.html`)
30
49
  }
31
- if (!fs.existsSync(`${CWD}/public/cards`)) {
50
+ if (!fs.existsSync(`${PUBLIC_DIR}/cards`)) {
32
51
  console.log(`Copying cards into public/cards folder`)
33
- execSync(`cp -r ${CWD}/node_modules/@sjcrh/proteinpaint-front/public/cards ${CWD}/public/cards`, {
52
+ execSync(`cp -r ${CWD}/node_modules/@sjcrh/proteinpaint-front/public/cards ${PUBLIC_DIR}/cards`, {
34
53
  stdio: 'inherit'
35
54
  })
36
55
  }
37
56
  }
38
- const tar = ps.spawnSync('tar', [`-xzf`, `${__dirname}/bundles.tgz`, `-C`, `${CWD}`], { encoding: 'utf8' })
39
- if (tar.status !== 0) {
40
- throw new Error(`Tar command failed with exit code ${tar.status}: ${tar.stderr}`)
41
- }
42
- if (tar.stderr) {
43
- console.warn('Tar command warnings:', tar.stderr)
44
- }
45
- console.log(`Setting the dynamic bundle path to ${URLPATH}`)
46
- const codeFile = `${CWD}/public/bin/proteinpaint.js`
47
- // remember the modified time before setting the bundle public path
48
- const mtime = fs.statSync(codeFile).mtime
49
- const code = fs.readFileSync(codeFile, { encoding: 'utf8' })
50
- const newcode = code.replace(`__PP_URL__`, `${URLPATH}/bin/`)
51
- fs.writeFileSync(codeFile, newcode, { encoding: 'utf8' })
52
- try {
53
- // reset the atime and mtime to the original mtime before setting the bundle publit path
54
- fs.utimesSync(codeFile, mtime, mtime)
55
- } catch (e) {
56
- console.log('--- !!! unable to reset the mtime for the extracted proteinpaint bundle: ', e)
57
+
58
+ // Generate the bundle into CWD/bin only if it isn't already there. CWD/bin is in the container's
59
+ // writable layer, so it persists across restarts of THIS container (reuse) and is empty in any freshly
60
+ // created container (regenerate) — including after an image update, which always yields a new
61
+ // container. Because CWD/bin is per-container, there is no shared directory to race on.
62
+ //
63
+ // NOTE: reuse is keyed on the completion marker (a fully extracted bundle), not on its version. That
64
+ // is correct for the normal container lifecycle (a new image arrives with a new container and hence an
65
+ // empty CWD/bin). It would be stale only if the SAME container were reused across a bundle change —
66
+ // e.g. CWD/bin persisted via a mounted volume — uncommon, and outside this simple scheme.
67
+ if (fs.existsSync(BIN_READY)) {
68
+ console.log(`bundle already present and complete at ${BIN_DIR}; reusing it`)
69
+ } else {
70
+ if (fs.existsSync(BIN_DIR)) {
71
+ console.log(`removing an incomplete ${BIN_DIR}`)
72
+ fs.rmSync(BIN_DIR, { recursive: true, force: true })
73
+ }
74
+ // bundles.tgz contains public/bin/*; --strip-components=1 drops the leading public/ so the files
75
+ // land in CWD/bin/* (this container's private bin), not in the shared public/.
76
+ const tar = ps.spawnSync('tar', [`-xzf`, `${__dirname}/bundles.tgz`, `-C`, `${CWD}`, `--strip-components=1`], {
77
+ encoding: 'utf8'
78
+ })
79
+ if (tar.status !== 0) {
80
+ throw new Error(`Tar command failed with exit code ${tar.status}: ${tar.stderr}`)
81
+ }
82
+ if (tar.stderr) {
83
+ console.warn('Tar command warnings:', tar.stderr)
84
+ }
85
+ // The bundle needs no URL patching: webpack's output.publicPath is 'auto', so the client derives
86
+ // the /bin/ base path at runtime from the <script> tag it was loaded from (see front/webpack.config.js).
87
+ // mark generation complete LAST (see BIN_READY above): a crash before this leaves no marker, so
88
+ // the next start regenerates rather than reusing a partial bundle.
89
+ fs.writeFileSync(BIN_READY, '')
57
90
  }
58
91
  } catch (e) {
59
92
  console.error(e)
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "@sjcrh/proteinpaint-front",
3
- "version": "2.207.1",
3
+ "version": "2.209.0",
4
4
  "browser": "src/app.js",
5
5
  "main": "src/index.js",
6
6
  "bin": "init.js",
7
7
  "scripts": {
8
- "dev": "./dev.sh &",
8
+ "dev": "./dev.sh",
9
9
  "//TODO": "# delete webpack use, once esbuild migration is fully tested and unlikely to be reverted",
10
10
  "client-dist": "if ! -d ../client/dist; then cd ../client; npm pack; fi",
11
- "prepack": "npm run client-dist && rm -rf public/bin && rm -rf *.tgz && webpack && tar -czf bundles.tgz public/bin",
11
+ "prepack": "npm run client-dist && rm -rf public/bin && rm -rf *.tgz && webpack && tar -czf bundles.tgz public/bin && npm pkg set _buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)",
12
+ "postpack": "npm pkg delete _buildTime",
12
13
  "test:unit": "echo 'TODO: front unit tests'",
13
14
  "test:integration": "echo 'TODO: front integration tests'"
14
15
  },
15
16
  "devDependencies": {
16
- "@sjcrh/proteinpaint-client": "2.207.1",
17
+ "@sjcrh/proteinpaint-client": "2.209.0",
17
18
  "webpack": "^5.76.0",
18
19
  "webpack-cli": "^4.9.2",
19
20
  "webpack-node-externals": "^3.0.0"
@@ -30,5 +31,6 @@
30
31
  "type": "git",
31
32
  "url": "https://github.com/stjude/proteinpaint",
32
33
  "directory": "front"
33
- }
34
+ },
35
+ "_buildTime": "2026-09-16T15:51:58Z"
34
36
  }
package/public/index.html CHANGED
@@ -8,7 +8,7 @@
8
8
  best practice is to use <script defer> or to put script tags before </body> end tag and
9
9
  just before the runproteinpaint() call
10
10
  -->
11
- <script src="/bin/front/proteinpaint.js" charset="utf-8"></script>
11
+ <script src="/bin/proteinpaint.js" charset="utf-8"></script>
12
12
  </head>
13
13
  <body style="margin:0px;width:100%;height:100%">
14
14
 
@@ -18,6 +18,7 @@
18
18
 
19
19
  <script>
20
20
  runproteinpaint({
21
+ //host: 'http://localhost:3000',
21
22
  host: 'https://proteinpaint.stjude.org',
22
23
  holder:document.getElementById('aaa'),
23
24
  parseurl:true,