@notifiles/sdk 1.3.0 → 1.4.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@notifiles/sdk",
3
3
  "type": "module",
4
- "version": "1.3.0",
4
+ "version": "1.4.0",
5
5
  "description": "",
6
6
  "author": {
7
7
  "name": "adoucoure",
@@ -0,0 +1,11 @@
1
+ // Serializes concurrent reads/writes to the same file path so a read can
2
+ // never observe a write mid-truncate (e.g. multiple locale versions of a
3
+ // template racing on the same .activity.json during a parallel sync).
4
+ const queues = new Map()
5
+
6
+ export default function withFileLock(key, fn) {
7
+ const previous = queues.get(key) || Promise.resolve()
8
+ const run = previous.catch(() => {}).then(fn)
9
+ queues.set(key, run.catch(() => {}))
10
+ return run
11
+ }
@@ -1,5 +1,7 @@
1
1
  import fs from "fs"
2
+ import { fileURLToPath } from "url"
2
3
  import checkFileExists from "./checkFileExists.js"
4
+ import withFileLock from "./fileLock.js"
3
5
 
4
6
  const expandEnvPlaceholders = (value) => {
5
7
  if (typeof value === 'string') {
@@ -29,7 +31,9 @@ export default async _url => {
29
31
  return null
30
32
  }
31
33
  const fileUrl = new URL(_url, import.meta.url)
32
- const parsedPackageJSON = JSON.parse(await fs.promises.readFile(fileUrl, 'utf8'))
34
+ const parsedPackageJSON = await withFileLock(fileURLToPath(fileUrl), async () => {
35
+ return JSON.parse(await fs.promises.readFile(fileUrl, 'utf8'))
36
+ })
33
37
  return expandEnvPlaceholders(parsedPackageJSON)
34
38
  // } catch (e) {
35
39
  // console.log("[notifiles]", `importJSONAsync → `, e)
@@ -2,6 +2,7 @@ import fsPath from 'path'
2
2
  import fs from 'fs'
3
3
  import checkFileExists from '../../../lib/fs/checkFileExists.js'
4
4
  import importJSONAsync from '../../../lib/fs/importJSONAsync.js'
5
+ import withFileLock from '../../../lib/fs/fileLock.js'
5
6
 
6
7
 
7
8
  export default async ({
@@ -24,7 +25,9 @@ export default async ({
24
25
  ...content,
25
26
  }
26
27
 
27
- await fs.promises.writeFile(filePath, JSON.stringify(_content, null, 2))
28
+ await withFileLock(fsPath.resolve(filePath), () =>
29
+ fs.promises.writeFile(filePath, JSON.stringify(_content, null, 2))
30
+ )
28
31
  return true
29
32
  } catch (e) {
30
33
  console.error(e)
@@ -9,6 +9,9 @@ export default async (props) => {
9
9
 
10
10
  const { entry, settings } = props
11
11
  const { path, pushNotification, buildPaths, } = entry
12
+ // pushNotification is null unless the template folder has a pushnotification.txt (set in lib/entries/getAtPath.js).
13
+ // Every template must ship a pushnotification.txt (an empty file is fine, see templates/social/subscription/*)
14
+ // or this destructure throws before the existence check below ever runs.
12
15
  const { text, } = pushNotification
13
16
 
14
17
  const textPath = `${path}/pushnotification.txt`
@@ -9,6 +9,9 @@ export default async (props) => {
9
9
 
10
10
  const { entry, settings } = props
11
11
  const { path, textMessage, buildPaths, } = entry
12
+ // textMessage is null unless the template folder has a textmessage.txt (set in lib/entries/getAtPath.js).
13
+ // Every template must ship a textmessage.txt (an empty file is fine, see templates/social/subscription/*)
14
+ // or this destructure throws before the existence check below ever runs.
12
15
  const { text, } = textMessage
13
16
 
14
17
  const textPath = `${path}/textmessage.txt`