@fugood/bricks-project 2.25.0-beta.22 → 2.25.0-beta.24

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/utils/id.ts CHANGED
@@ -1,38 +1,83 @@
1
1
  import { v4 as uuid } from 'uuid'
2
+ import { createHash } from 'node:crypto'
3
+ import { readFileSync } from 'node:fs'
4
+ import { join } from 'node:path'
2
5
 
3
6
  let count = 0
4
7
 
5
- // Used for snapshot mode
6
- const countUUID = () => {
7
- const current = count
8
- count += 1
9
- return `00000000-0000-0000-0000-${current.toString().padStart(12, '0')}`
8
+ type IdType =
9
+ | 'animation'
10
+ | 'brick'
11
+ | 'canvas'
12
+ | 'generator'
13
+ | 'data'
14
+ | 'switch'
15
+ | 'property_bank_command'
16
+ | 'property_bank_calc'
17
+ | 'dynamic-brick'
18
+ | 'automation_map'
19
+ | 'test'
20
+ | 'test_case'
21
+ | 'test_var'
22
+ | 'subspace'
23
+
24
+ type IdOptions = {
25
+ snapshotMode?: boolean
26
+ }
27
+
28
+ const APPLICATION_ID_FALLBACK = 'unknown-application'
29
+
30
+ const usedStableAliases = new Set<string>()
31
+ let applicationIdCache: { cwd: string; id: string } | null = null
32
+
33
+ const readApplicationId = () => {
34
+ const cwd = process.cwd()
35
+ if (applicationIdCache?.cwd === cwd) return applicationIdCache.id
36
+
37
+ let id = APPLICATION_ID_FALLBACK
38
+ try {
39
+ const app = JSON.parse(readFileSync(join(cwd, 'application.json'), 'utf8'))
40
+ if (typeof app.id === 'string' && app.id) id = app.id
41
+ } catch {
42
+ // `makeId` is also used from tests and utilities outside project roots.
43
+ }
44
+
45
+ applicationIdCache = { cwd, id }
46
+ return id
47
+ }
48
+
49
+ const hashToRandomBytes = (parts: string[]) => {
50
+ const hash = createHash('sha256').update(JSON.stringify(parts)).digest()
51
+ return new Uint8Array(hash.subarray(0, 16))
10
52
  }
11
53
 
12
- // Make a random id if not want to use fixed id
13
- export const makeId = (
14
- type:
15
- | 'animation'
16
- | 'brick'
17
- | 'canvas'
18
- | 'generator'
19
- | 'data'
20
- | 'switch'
21
- | 'property_bank_command'
22
- | 'property_bank_calc'
23
- | 'dynamic-brick'
24
- | 'automation_map'
25
- | 'test'
26
- | 'test_case'
27
- | 'test_var'
28
- | 'subspace',
29
- opts?: {
30
- snapshotMode?: boolean
31
- },
32
- ) => {
54
+ const makeStableUuid = (type: string, alias?: string) => {
55
+ const applicationId = readApplicationId()
56
+ const seed = alias ?? String(count)
57
+ if (alias === undefined) count += 1
58
+
59
+ if (alias !== undefined) {
60
+ const aliasKey = JSON.stringify([applicationId, type, alias])
61
+ if (usedStableAliases.has(aliasKey)) {
62
+ throw new Error(`Duplicate makeId alias '${alias}' for type '${type}'`)
63
+ }
64
+ usedStableAliases.add(aliasKey)
65
+ }
66
+
67
+ return uuid({
68
+ random: hashToRandomBytes([applicationId, type, seed]),
69
+ })
70
+ }
71
+
72
+ // Make stable ids by default; explicit snapshotMode: false preserves the random escape hatch.
73
+ export const makeId = (type: IdType, aliasOrOpts?: string | IdOptions, opts?: IdOptions) => {
33
74
  if (type === 'subspace') {
34
75
  throw new Error('Currently subspace is not supported for ID generation, please use a fixed ID')
35
76
  }
77
+
78
+ const alias = typeof aliasOrOpts === 'string' ? aliasOrOpts : undefined
79
+ const options = typeof aliasOrOpts === 'string' ? opts : (aliasOrOpts ?? opts)
80
+
36
81
  let prefix = ''
37
82
  switch (type) {
38
83
  case 'animation':
@@ -76,5 +121,11 @@ export const makeId = (
76
121
  break
77
122
  default:
78
123
  }
79
- return `${prefix}${opts?.snapshotMode ? countUUID() : uuid()}`
124
+
125
+ const useCountFallback = aliasOrOpts === undefined && opts === undefined
126
+ const id =
127
+ alias !== undefined || options?.snapshotMode || useCountFallback
128
+ ? makeStableUuid(type, alias)
129
+ : uuid()
130
+ return `${prefix}${id}`
80
131
  }