@inlang/sdk 0.34.3 → 0.34.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.
Files changed (38) hide show
  1. package/LICENSE +201 -0
  2. package/dist/adapter/solidAdapter.test.js +1 -1
  3. package/dist/api.d.ts +13 -0
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/createMessagesQuery.d.ts +14 -2
  6. package/dist/createMessagesQuery.d.ts.map +1 -1
  7. package/dist/createMessagesQuery.js +338 -6
  8. package/dist/createMessagesQuery.test.js +133 -88
  9. package/dist/createNodeishFsWithWatcher.d.ts +1 -0
  10. package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
  11. package/dist/createNodeishFsWithWatcher.js +2 -2
  12. package/dist/createNodeishFsWithWatcher.test.js +8 -0
  13. package/dist/loadProject.d.ts.map +1 -1
  14. package/dist/loadProject.js +21 -487
  15. package/dist/persistence/filelock/acquireFileLock.d.ts +3 -0
  16. package/dist/persistence/filelock/acquireFileLock.d.ts.map +1 -0
  17. package/dist/persistence/filelock/acquireFileLock.js +109 -0
  18. package/dist/persistence/filelock/releaseLock.d.ts +3 -0
  19. package/dist/persistence/filelock/releaseLock.d.ts.map +1 -0
  20. package/dist/persistence/filelock/releaseLock.js +23 -0
  21. package/dist/v2/index.d.ts +2 -0
  22. package/dist/v2/index.d.ts.map +1 -0
  23. package/dist/v2/index.js +1 -0
  24. package/dist/v2/types.d.ts +411 -0
  25. package/dist/v2/types.d.ts.map +1 -0
  26. package/dist/v2/types.js +69 -0
  27. package/package.json +8 -7
  28. package/src/adapter/solidAdapter.test.ts +1 -1
  29. package/src/api.ts +15 -0
  30. package/src/createMessagesQuery.test.ts +147 -109
  31. package/src/createMessagesQuery.ts +477 -8
  32. package/src/createNodeishFsWithWatcher.test.ts +13 -0
  33. package/src/createNodeishFsWithWatcher.ts +2 -2
  34. package/src/loadProject.ts +20 -666
  35. package/src/persistence/filelock/acquireFileLock.ts +124 -0
  36. package/src/persistence/filelock/releaseLock.ts +28 -0
  37. package/src/v2/index.ts +1 -0
  38. package/src/v2/types.ts +142 -0
@@ -0,0 +1,124 @@
1
+ import { type NodeishFilesystem } from "@lix-js/fs"
2
+ import type { NodeishStats } from "@lix-js/fs"
3
+ import _debug from "debug"
4
+ const debug = _debug("sdk:acquireFileLock")
5
+
6
+ const maxRetries = 10
7
+ const nProbes = 50
8
+ const probeInterval = 100
9
+ export async function acquireFileLock(
10
+ fs: NodeishFilesystem,
11
+ lockDirPath: string,
12
+ lockOrigin: string,
13
+ tryCount: number = 0
14
+ ): Promise<number> {
15
+ if (tryCount > maxRetries) {
16
+ throw new Error(lockOrigin + " exceeded maximum Retries (5) to acquire lockfile " + tryCount)
17
+ }
18
+
19
+ try {
20
+ debug(lockOrigin + " tries to acquire a lockfile Retry Nr.: " + tryCount)
21
+ await fs.mkdir(lockDirPath)
22
+ const stats = await fs.stat(lockDirPath)
23
+ debug(lockOrigin + " acquired a lockfile Retry Nr.: " + tryCount)
24
+ return stats.mtimeMs
25
+ } catch (error: any) {
26
+ if (error.code !== "EEXIST") {
27
+ // we only expect the error that the file exists already (locked by other process)
28
+ throw error
29
+ }
30
+ }
31
+
32
+ let currentLockTime: number
33
+
34
+ try {
35
+ const stats = await fs.stat(lockDirPath)
36
+ currentLockTime = stats.mtimeMs
37
+ } catch (fstatError: any) {
38
+ if (fstatError.code === "ENOENT") {
39
+ // lock file seems to be gone :) - lets try again
40
+ debug(lockOrigin + " tryCount++ lock file seems to be gone :) - lets try again " + tryCount)
41
+ return acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
42
+ }
43
+ throw fstatError
44
+ }
45
+ debug(
46
+ lockOrigin +
47
+ " tries to acquire a lockfile - lock currently in use... starting probe phase " +
48
+ tryCount
49
+ )
50
+
51
+ return new Promise((resolve, reject) => {
52
+ let probeCounts = 0
53
+ const scheduleProbationTimeout = () => {
54
+ setTimeout(async () => {
55
+ probeCounts += 1
56
+ let lockFileStats: undefined | NodeishStats = undefined
57
+ try {
58
+ debug(
59
+ lockOrigin + " tries to acquire a lockfile - check if the lock is free now " + tryCount
60
+ )
61
+
62
+ // alright lets give it another try
63
+ lockFileStats = await fs.stat(lockDirPath)
64
+ } catch (fstatError: any) {
65
+ if (fstatError.code === "ENOENT") {
66
+ debug(
67
+ lockOrigin +
68
+ " tryCount++ in Promise - tries to acquire a lockfile - lock file seems to be free now - try to acquire " +
69
+ tryCount
70
+ )
71
+ const lock = acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
72
+ return resolve(lock)
73
+ }
74
+ return reject(fstatError)
75
+ }
76
+
77
+ // still the same locker! -
78
+ if (lockFileStats.mtimeMs === currentLockTime) {
79
+ if (probeCounts >= nProbes) {
80
+ // ok maximum lock time ran up (we waitetd nProbes * probeInterval) - we consider the lock to be stale
81
+ debug(
82
+ lockOrigin +
83
+ " tries to acquire a lockfile - lock not free - but stale lets drop it" +
84
+ tryCount
85
+ )
86
+ try {
87
+ await fs.rmdir(lockDirPath)
88
+ } catch (rmLockError: any) {
89
+ if (rmLockError.code === "ENOENT") {
90
+ // lock already gone?
91
+ // Option 1: The "stale process" decided to get rid of it
92
+ // Option 2: Another process acquiring the lock and detected a stale one as well
93
+ }
94
+ return reject(rmLockError)
95
+ }
96
+ try {
97
+ debug(
98
+ lockOrigin +
99
+ " tryCount++ same locker - try to acquire again after removing stale lock " +
100
+ tryCount
101
+ )
102
+ const lock = await acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
103
+ return resolve(lock)
104
+ } catch (lockAquireException) {
105
+ return reject(lockAquireException)
106
+ }
107
+ } else {
108
+ // lets schedule a new probation
109
+ return scheduleProbationTimeout()
110
+ }
111
+ } else {
112
+ try {
113
+ debug(lockOrigin + " tryCount++ different locker - try to acquire again " + tryCount)
114
+ const lock = await acquireFileLock(fs, lockDirPath, lockOrigin, tryCount + 1)
115
+ return resolve(lock)
116
+ } catch (error) {
117
+ return reject(error)
118
+ }
119
+ }
120
+ }, probeInterval)
121
+ }
122
+ scheduleProbationTimeout()
123
+ })
124
+ }
@@ -0,0 +1,28 @@
1
+ import { type NodeishFilesystem } from "@lix-js/fs"
2
+ import _debug from "debug"
3
+ const debug = _debug("sdk:releaseLock")
4
+
5
+ export async function releaseLock(
6
+ fs: NodeishFilesystem,
7
+ lockDirPath: string,
8
+ lockOrigin: string,
9
+ lockTime: number
10
+ ) {
11
+ debug(lockOrigin + " releasing the lock ")
12
+ try {
13
+ const stats = await fs.stat(lockDirPath)
14
+ if (stats.mtimeMs === lockTime) {
15
+ // this can be corrupt as welll since the last getStat and the current a modification could have occured :-/
16
+ await fs.rmdir(lockDirPath)
17
+ }
18
+ } catch (statError: any) {
19
+ debug(lockOrigin + " couldn't release the lock")
20
+ if (statError.code === "ENOENT") {
21
+ // ok seeks like the log was released by someone else
22
+ debug(lockOrigin + " WARNING - the lock was released by a different process")
23
+ return
24
+ }
25
+ debug(statError)
26
+ throw statError
27
+ }
28
+ }
@@ -0,0 +1 @@
1
+ export type * from "./types.js"
@@ -0,0 +1,142 @@
1
+ import { Type, type Static } from "@sinclair/typebox"
2
+
3
+ /**
4
+ * Follows the IETF BCP 47 language tag schema.
5
+ *
6
+ * @see https://www.ietf.org/rfc/bcp/bcp47.txt
7
+ * @see https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
8
+ */
9
+ export type LanguageTag = Static<typeof LanguageTag>
10
+ /**
11
+ * Follows the IETF BCP 47 language tag schema with modifications.
12
+ * @see REAMDE.md file for more information on the validation.
13
+ */
14
+
15
+ export const pattern =
16
+ "^((?<grandfathered>(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang))|((?<language>([A-Za-z]{2,3}(-(?<extlang>[A-Za-z]{3}(-[A-Za-z]{3}){0,2}))?))(-(?<script>[A-Za-z]{4}))?(-(?<region>[A-Za-z]{2}|[0-9]{3}))?(-(?<variant>[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*))$"
17
+
18
+ export const LanguageTag = Type.String({
19
+ pattern: pattern,
20
+ description: "The language tag must be a valid IETF BCP 47 language tag.",
21
+ examples: ["en", "de", "en-US", "zh-Hans", "es-419"],
22
+ })
23
+
24
+ export type Literal = Static<typeof Literal>
25
+ export const Literal = Type.Object({
26
+ type: Type.Literal("literal"),
27
+ value: Type.String(),
28
+ })
29
+
30
+ /**
31
+ * A (text) element that is translatable and rendered to the UI.
32
+ */
33
+ export type Text = Static<typeof Text>
34
+ export const Text = Type.Object({
35
+ type: Type.Literal("text"),
36
+ value: Type.String(),
37
+ })
38
+
39
+ export type VariableReference = Static<typeof VariableReference>
40
+ export const VariableReference = Type.Object({
41
+ type: Type.Literal("variable"),
42
+ name: Type.String(),
43
+ })
44
+
45
+ export type Option = Static<typeof Option>
46
+ export const Option = Type.Object({
47
+ name: Type.String(),
48
+ value: Type.Union([Literal, VariableReference]),
49
+ })
50
+
51
+ export type FunctionAnnotation = Static<typeof FunctionAnnotation>
52
+ export const FunctionAnnotation = Type.Object({
53
+ type: Type.Literal("function"),
54
+ name: Type.String(),
55
+ options: Type.Array(Option),
56
+ })
57
+
58
+ /**
59
+ * An expression is a reference to a variable or a function.
60
+ *
61
+ * Think of expressions as elements that are rendered to a
62
+ * text value during runtime.
63
+ */
64
+ export type Expression = Static<typeof Expression>
65
+ export const Expression = Type.Object({
66
+ type: Type.Literal("expression"),
67
+ arg: Type.Union([Literal, VariableReference]),
68
+ annotation: Type.Optional(FunctionAnnotation),
69
+ })
70
+
71
+ // export type FunctionReference = {
72
+ // type: "function"
73
+ // name: string
74
+ // operand?: Text | VariableReference
75
+ // options?: Option[]
76
+ // }
77
+
78
+ /**
79
+ * A pattern is a sequence of elements that comprise
80
+ * a message that is rendered to the UI.
81
+ */
82
+ export type Pattern = Static<typeof Pattern>
83
+ export const Pattern = Type.Array(Type.Union([Text, Expression]))
84
+
85
+ /**
86
+ * A variant contains a pattern that is rendered to the UI.
87
+ */
88
+ export type Variant = Static<typeof Variant>
89
+ export const Variant = Type.Object({
90
+ /**
91
+ * The number of keys in each variant match MUST equal the number of expressions in the selectors.
92
+ *
93
+ * Inspired by: https://github.com/unicode-org/message-format-wg/blob/main/spec/formatting.md#pattern-selection
94
+ */
95
+ // a match can always only be string-based because a string is what is rendered to the UI
96
+ match: Type.Array(Type.String()),
97
+ pattern: Pattern,
98
+ })
99
+
100
+ export type InputDeclaration = Static<typeof InputDeclaration>
101
+ export const InputDeclaration = Type.Object({
102
+ type: Type.Literal("input"),
103
+ name: Type.String(),
104
+
105
+ //TODO make this generic so that only Variable-Ref Expressions are allowed
106
+ value: Expression,
107
+ })
108
+
109
+ // local declarations are not supported.
110
+ // Will only add when required. See discussion:
111
+ // https://github.com/opral/monorepo/pull/2700#discussion_r1591070701
112
+ //
113
+ // export type LocalDeclaration = Static<typeof InputDeclaration>
114
+ // export const LocalDeclaration = Type.Object({
115
+ // type: Type.Literal("local"),
116
+ // name: Type.String(),
117
+ // value: Expression,
118
+ // })
119
+ //
120
+ // export type Declaration = Static<typeof Declaration>
121
+ // export const Declaration = Type.Union([LocalDeclaration, InputDeclaration])
122
+
123
+ export type Declaration = Static<typeof Declaration>
124
+ export const Declaration = Type.Union([InputDeclaration])
125
+
126
+ export type Message = Static<typeof Message>
127
+ export const Message = Type.Object({
128
+ locale: LanguageTag,
129
+ declarations: Type.Array(Declaration),
130
+ /**
131
+ * The order in which the selectors are placed determines the precedence of patterns.
132
+ */
133
+ selectors: Type.Array(Expression),
134
+ variants: Type.Array(Variant),
135
+ })
136
+
137
+ export type MessageBundle = Static<typeof MessageBundle>
138
+ export const MessageBundle = Type.Object({
139
+ id: Type.String(),
140
+ alias: Type.Record(Type.String(), Type.String()),
141
+ messages: Type.Array(Message),
142
+ })