@inlang/sdk 0.24.1 → 0.26.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.
@@ -16,10 +16,10 @@ import {
16
16
  ProjectSettingsFileNotFoundError,
17
17
  ProjectSettingsInvalidError,
18
18
  } from "./errors.js"
19
- import { createNodeishMemoryFs, normalizePath } from "@lix-js/fs"
19
+ import { normalizePath } from "@lix-js/fs"
20
20
  import { createMessage } from "./test-utilities/createMessage.js"
21
21
  import { tryCatch } from "@inlang/result"
22
- import { mockRepo, openRepository } from "@lix-js/client"
22
+ import { mockRepo } from "@lix-js/client"
23
23
  import { type Snapshot } from "@lix-js/fs"
24
24
  // eslint-disable-next-line no-restricted-imports -- test
25
25
  import { readFileSync } from "node:fs"
@@ -109,15 +109,11 @@ const _import: ImportFunction = async (name) =>
109
109
  default: name === "plugin.js" ? mockPlugin : mockMessageLintRule,
110
110
  } satisfies InlangModule)
111
111
 
112
- async function openCiTestRepo() {
113
- return await mockRepo({
114
- fromSnapshot: JSON.parse(
115
- readFileSync(resolve(__dirname, "../mocks/ci-test-repo-no-shallow.json"), {
116
- encoding: "utf-8",
117
- })
118
- ) as Snapshot,
112
+ const ciTestRepoSnapshot = JSON.parse(
113
+ readFileSync(resolve(__dirname, "../mocks/ci-test-repo-no-shallow.json"), {
114
+ encoding: "utf-8",
119
115
  })
120
- }
116
+ ) as Snapshot
121
117
 
122
118
  // ------------------------------------------------------------------------------------------------
123
119
 
@@ -128,8 +124,7 @@ async function openCiTestRepo() {
128
124
  * like files: they can be renamed and moved around.
129
125
  */
130
126
  it("should throw if a project (path) does not have a name", async () => {
131
- const fs = createNodeishMemoryFs()
132
- const repo = await openRepository("file://", { nodeishFs: fs })
127
+ const repo = await mockRepo()
133
128
  const project = await tryCatch(() =>
134
129
  loadProject({
135
130
  projectPath: "/source-code/.inlang",
@@ -141,8 +136,7 @@ it("should throw if a project (path) does not have a name", async () => {
141
136
  })
142
137
 
143
138
  it("should throw if a project path does not end with .inlang", async () => {
144
- const fs = createNodeishMemoryFs()
145
- const repo = await openRepository("file://", { nodeishFs: fs })
139
+ const repo = await mockRepo()
146
140
 
147
141
  const invalidPaths = [
148
142
  "/source-code/frontend.inlang/settings",
@@ -164,8 +158,7 @@ it("should throw if a project path does not end with .inlang", async () => {
164
158
 
165
159
  describe("initialization", () => {
166
160
  it("should throw if projectPath is not an absolute path", async () => {
167
- const fs = createNodeishMemoryFs()
168
- const repo = await openRepository("file://", { nodeishFs: fs })
161
+ const repo = await mockRepo()
169
162
 
170
163
  const result = await tryCatch(() =>
171
164
  loadProject({
@@ -179,7 +172,7 @@ describe("initialization", () => {
179
172
  })
180
173
 
181
174
  it("should generate projectId on missing projectid", async () => {
182
- const repo = await openCiTestRepo()
175
+ const repo = await mockRepo({ fromSnapshot: ciTestRepoSnapshot })
183
176
 
184
177
  const existing = await repo.nodeishFs
185
178
  .readFile("/project.inlang/project_id", {
@@ -215,7 +208,7 @@ describe("initialization", () => {
215
208
  })
216
209
 
217
210
  it("should reuse projectId on existing projectid", async () => {
218
- const repo = await openCiTestRepo()
211
+ const repo = await mockRepo({ fromSnapshot: ciTestRepoSnapshot })
219
212
 
220
213
  repo.nodeishFs.writeFile("/project.inlang/project_id", "testId")
221
214
 
@@ -242,10 +235,10 @@ describe("initialization", () => {
242
235
  })
243
236
 
244
237
  it("should resolve from a windows path", async () => {
245
- const fs = createNodeishMemoryFs()
238
+ const repo = await mockRepo()
239
+ const fs = repo.nodeishFs
246
240
  fs.mkdir("C:\\Users\\user\\project.inlang", { recursive: true })
247
241
  fs.writeFile("C:\\Users\\user\\project.inlang\\settings.json", JSON.stringify(settings))
248
- const repo = await openRepository("file://", { nodeishFs: fs })
249
242
 
250
243
  const result = await tryCatch(() =>
251
244
  loadProject({
@@ -261,9 +254,9 @@ describe("initialization", () => {
261
254
 
262
255
  describe("settings", () => {
263
256
  it("should return an error if settings file is not found", async () => {
264
- const fs = createNodeishMemoryFs()
257
+ const repo = await mockRepo()
258
+ const fs = repo.nodeishFs
265
259
  fs.mkdir("/user/project", { recursive: true })
266
- const repo = await openRepository("file://", { nodeishFs: fs })
267
260
 
268
261
  const project = await loadProject({
269
262
  projectPath: "/user/non-existend-project.inlang",
@@ -275,10 +268,10 @@ describe("initialization", () => {
275
268
  })
276
269
 
277
270
  it("should return an error if settings file is not a valid JSON", async () => {
278
- const fs = await createNodeishMemoryFs()
271
+ const repo = await mockRepo()
272
+ const fs = repo.nodeishFs
279
273
  await fs.mkdir("/user/project.inlang", { recursive: true })
280
274
  await fs.writeFile("/user/project.inlang/settings.json", "invalid json")
281
- const repo = await openRepository("file://", { nodeishFs: fs })
282
275
 
283
276
  const project = await loadProject({
284
277
  projectPath: "/user/project.inlang",
@@ -290,10 +283,10 @@ describe("initialization", () => {
290
283
  })
291
284
 
292
285
  it("should return an error if settings file is does not match schema", async () => {
293
- const fs = await createNodeishMemoryFs()
286
+ const repo = await mockRepo()
287
+ const fs = repo.nodeishFs
294
288
  await fs.mkdir("/user/project.inlang", { recursive: true })
295
289
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify({}))
296
- const repo = await openRepository("file://", { nodeishFs: fs })
297
290
 
298
291
  const project = await loadProject({
299
292
  projectPath: "/user/project.inlang",
@@ -305,10 +298,10 @@ describe("initialization", () => {
305
298
  })
306
299
 
307
300
  it("should return the parsed settings", async () => {
308
- const fs = await createNodeishMemoryFs()
301
+ const repo = await mockRepo()
302
+ const fs = repo.nodeishFs
309
303
  await fs.mkdir("/user/project.inlang", { recursive: true })
310
304
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
311
- const repo = await openRepository("file://", { nodeishFs: fs })
312
305
  const project = await loadProject({
313
306
  projectPath: "/user/project.inlang",
314
307
  repo,
@@ -319,11 +312,11 @@ describe("initialization", () => {
319
312
  })
320
313
 
321
314
  it("should not re-write the settings to disk when initializing", async () => {
322
- const fs = await createNodeishMemoryFs()
315
+ const repo = await mockRepo()
316
+ const fs = repo.nodeishFs
323
317
  const settingsWithDeifferentFormatting = JSON.stringify(settings, undefined, 4)
324
318
  await fs.mkdir("/user/project.inlang", { recursive: true })
325
319
  await fs.writeFile("/user/project.inlang/settings.json", settingsWithDeifferentFormatting)
326
- const repo = await openRepository("file://", { nodeishFs: fs })
327
320
 
328
321
  const project = await loadProject({
329
322
  projectPath: "/user/project.inlang",
@@ -354,10 +347,10 @@ describe("initialization", () => {
354
347
  default: {} as Plugin,
355
348
  } satisfies InlangModule)
356
349
 
357
- const fs = createNodeishMemoryFs()
350
+ const repo = await mockRepo()
351
+ const fs = repo.nodeishFs
358
352
  await fs.mkdir("/user/project.inlang", { recursive: true })
359
353
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
360
- const repo = await openRepository("file://", { nodeishFs: fs })
361
354
 
362
355
  const project = await loadProject({
363
356
  projectPath: "/user/project.inlang",
@@ -388,10 +381,10 @@ describe("initialization", () => {
388
381
  describe("functionality", () => {
389
382
  describe("settings", () => {
390
383
  it("should return the settings", async () => {
391
- const fs = await createNodeishMemoryFs()
384
+ const repo = await mockRepo()
385
+ const fs = repo.nodeishFs
392
386
  await fs.mkdir("/user/project.inlang", { recursive: true })
393
387
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
394
- const repo = await openRepository("file://", { nodeishFs: fs })
395
388
  const project = await loadProject({
396
389
  projectPath: "/user/project.inlang",
397
390
  repo,
@@ -402,10 +395,10 @@ describe("functionality", () => {
402
395
  })
403
396
 
404
397
  it("should set a new settings", async () => {
405
- const fs = await createNodeishMemoryFs()
398
+ const repo = await mockRepo()
399
+ const fs = repo.nodeishFs
406
400
  await fs.mkdir("/user/project.inlang", { recursive: true })
407
401
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
408
- const repo = await openRepository("file://", { nodeishFs: fs })
409
402
  const project = await loadProject({
410
403
  projectPath: "/user/project.inlang",
411
404
  repo,
@@ -429,10 +422,10 @@ describe("functionality", () => {
429
422
 
430
423
  describe("setSettings", () => {
431
424
  it("should fail if settings are not valid", async () => {
432
- const fs = await createNodeishMemoryFs()
425
+ const repo = await mockRepo()
426
+ const fs = repo.nodeishFs
433
427
  await fs.mkdir("/user/project.inlang", { recursive: true })
434
428
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
435
- const repo = await openRepository("file://", { nodeishFs: fs })
436
429
  const project = await loadProject({
437
430
  projectPath: "/user/project.inlang",
438
431
  repo,
@@ -445,7 +438,8 @@ describe("functionality", () => {
445
438
  })
446
439
 
447
440
  it("should throw an error if sourceLanguageTag is not in languageTags", async () => {
448
- const fs = await createNodeishMemoryFs()
441
+ const repo = await mockRepo()
442
+ const fs = repo.nodeishFs
449
443
  await fs.mkdir("/user/project.inlang", { recursive: true })
450
444
 
451
445
  const settings: ProjectSettings = {
@@ -455,7 +449,6 @@ describe("functionality", () => {
455
449
  }
456
450
 
457
451
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
458
- const repo = await openRepository("file://", { nodeishFs: fs })
459
452
 
460
453
  const project = await loadProject({
461
454
  projectPath: "/user/project.inlang",
@@ -468,10 +461,10 @@ describe("functionality", () => {
468
461
  })
469
462
 
470
463
  it("should write settings to disk", async () => {
471
- const fs = await createNodeishMemoryFs()
464
+ const repo = await mockRepo()
465
+ const fs = repo.nodeishFs
472
466
  await fs.mkdir("/user/project.inlang", { recursive: true })
473
467
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
474
- const repo = await openRepository("file://", { nodeishFs: fs })
475
468
  const project = await loadProject({
476
469
  projectPath: "/user/project.inlang",
477
470
  repo,
@@ -496,7 +489,8 @@ describe("functionality", () => {
496
489
 
497
490
  describe("installed", () => {
498
491
  it("should return the installed items", async () => {
499
- const fs = createNodeishMemoryFs()
492
+ const repo = await mockRepo()
493
+ const fs = repo.nodeishFs
500
494
  const settings: ProjectSettings = {
501
495
  sourceLanguageTag: "en",
502
496
  languageTags: ["en"],
@@ -504,7 +498,6 @@ describe("functionality", () => {
504
498
  }
505
499
  await fs.mkdir("/user/project.inlang", { recursive: true })
506
500
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
507
- const repo = await openRepository("file://", { nodeishFs: fs })
508
501
  const project = await loadProject({
509
502
  projectPath: "/user/project.inlang",
510
503
  repo,
@@ -528,7 +521,8 @@ describe("functionality", () => {
528
521
  })
529
522
 
530
523
  it("should apply 'warning' as default lint level to lint rules that have no lint level defined in the settings", async () => {
531
- const fs = createNodeishMemoryFs()
524
+ const repo = await mockRepo()
525
+ const fs = repo.nodeishFs
532
526
 
533
527
  const settings: ProjectSettings = {
534
528
  sourceLanguageTag: "en",
@@ -538,7 +532,6 @@ describe("functionality", () => {
538
532
 
539
533
  await fs.mkdir("/user/project.inlang", { recursive: true })
540
534
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
541
- const repo = await openRepository("file://", { nodeishFs: fs })
542
535
 
543
536
  const project = await loadProject({
544
537
  projectPath: "/user/project.inlang",
@@ -571,7 +564,8 @@ describe("functionality", () => {
571
564
  loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
572
565
  saveMessages: () => undefined,
573
566
  }
574
- const fs = await createNodeishMemoryFs()
567
+ const repo = await mockRepo()
568
+ const fs = repo.nodeishFs
575
569
  await fs.mkdir("/user/project.inlang", { recursive: true })
576
570
  await fs.writeFile(
577
571
  "/user/project.inlang/settings.json",
@@ -581,7 +575,6 @@ describe("functionality", () => {
581
575
  modules: ["plugin.js", "lintRule.js"],
582
576
  } satisfies ProjectSettings)
583
577
  )
584
- const repo = await openRepository("file://", { nodeishFs: fs })
585
578
 
586
579
  const _import: ImportFunction = async (name) => {
587
580
  return {
@@ -623,7 +616,8 @@ describe("functionality", () => {
623
616
  loadMessages: () => [{ id: "some-message", selectors: [], variants: [] }],
624
617
  saveMessages: () => undefined,
625
618
  }
626
- const fs = await createNodeishMemoryFs()
619
+ const repo = await mockRepo()
620
+ const fs = repo.nodeishFs
627
621
  await fs.mkdir("/user/project.inlang", { recursive: true })
628
622
  await fs.writeFile(
629
623
  "/user/project.inlang/settings.json",
@@ -633,7 +627,6 @@ describe("functionality", () => {
633
627
  modules: ["plugin.js", "lintRule.js"],
634
628
  } satisfies ProjectSettings)
635
629
  )
636
- const repo = await openRepository("file://", { nodeishFs: fs })
637
630
  const _import: ImportFunction = async (name) => {
638
631
  return {
639
632
  default: name === "plugin.js" ? _mockPlugin : _mockLintRule,
@@ -656,10 +649,10 @@ describe("functionality", () => {
656
649
 
657
650
  describe("errors", () => {
658
651
  it("should return the errors", async () => {
659
- const fs = await createNodeishMemoryFs()
652
+ const repo = await mockRepo()
653
+ const fs = repo.nodeishFs
660
654
  await fs.mkdir("/user/project.inlang", { recursive: true })
661
655
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
662
- const repo = await openRepository("file://", { nodeishFs: fs })
663
656
  const project = await loadProject({
664
657
  projectPath: "/user/project.inlang",
665
658
  repo,
@@ -673,10 +666,10 @@ describe("functionality", () => {
673
666
 
674
667
  describe("customApi", () => {
675
668
  it("should return the app specific api", async () => {
676
- const fs = await createNodeishMemoryFs()
669
+ const repo = await mockRepo()
670
+ const fs = repo.nodeishFs
677
671
  await fs.mkdir("/user/project.inlang", { recursive: true })
678
672
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
679
- const repo = await openRepository("file://", { nodeishFs: fs })
680
673
  const project = await loadProject({
681
674
  projectPath: "/user/project.inlang",
682
675
  repo,
@@ -691,10 +684,10 @@ describe("functionality", () => {
691
684
 
692
685
  describe("messages", () => {
693
686
  it("should return the messages", async () => {
694
- const fs = await createNodeishMemoryFs()
687
+ const repo = await mockRepo()
688
+ const fs = repo.nodeishFs
695
689
  await fs.mkdir("/user/project.inlang", { recursive: true })
696
690
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
697
- const repo = await openRepository("file://", { nodeishFs: fs })
698
691
  const project = await loadProject({
699
692
  projectPath: "/user/project.inlang",
700
693
  repo,
@@ -707,7 +700,8 @@ describe("functionality", () => {
707
700
 
708
701
  describe("query", () => {
709
702
  it("should call saveMessages() on updates", async () => {
710
- const fs = createNodeishMemoryFs()
703
+ const repo = await mockRepo()
704
+ const fs = repo.nodeishFs
711
705
 
712
706
  const settings: ProjectSettings = {
713
707
  sourceLanguageTag: "en",
@@ -722,7 +716,6 @@ describe("functionality", () => {
722
716
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
723
717
 
724
718
  await fs.mkdir("./resources")
725
- const repo = await openRepository("file://", { nodeishFs: fs })
726
719
 
727
720
  const mockSaveFn = vi.fn()
728
721
 
@@ -885,7 +878,8 @@ describe("functionality", () => {
885
878
  * - Might be slow for a large number of messages. The requirement hasn't popped up yet though.
886
879
  */
887
880
  it("should pass all messages, regardless of which message changed, to saveMessages()", async () => {
888
- const fs = createNodeishMemoryFs()
881
+ const repo = await mockRepo()
882
+ const fs = repo.nodeishFs
889
883
 
890
884
  const settings: ProjectSettings = {
891
885
  sourceLanguageTag: "en",
@@ -898,7 +892,6 @@ describe("functionality", () => {
898
892
 
899
893
  await fs.mkdir("./project.inlang", { recursive: true })
900
894
  await fs.writeFile("./project.inlang/settings.json", JSON.stringify(settings))
901
- const repo = await openRepository("file://", { nodeishFs: fs })
902
895
 
903
896
  const mockSaveFn = vi.fn()
904
897
 
@@ -938,10 +931,10 @@ describe("functionality", () => {
938
931
 
939
932
  describe("lint", () => {
940
933
  it.todo("should throw if lint reports are not initialized yet", async () => {
941
- const fs = await createNodeishMemoryFs()
934
+ const repo = await mockRepo()
935
+ const fs = repo.nodeishFs
942
936
  await fs.mkdir("/user/project", { recursive: true })
943
937
  await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
944
- const repo = await openRepository("file://", { nodeishFs: fs })
945
938
  const project = await loadProject({
946
939
  projectPath: "/user/project/project.inlang.json",
947
940
  repo,
@@ -961,10 +954,10 @@ describe("functionality", () => {
961
954
  languageTags: ["en"],
962
955
  modules: ["lintRule.js"],
963
956
  }
964
- const fs = createNodeishMemoryFs()
957
+ const repo = await mockRepo()
958
+ const fs = repo.nodeishFs
965
959
  await fs.mkdir("/user/project.inlang", { recursive: true })
966
960
  await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(settings))
967
- const repo = await openRepository("file://", { nodeishFs: fs })
968
961
  const project = await loadProject({
969
962
  projectPath: "/user/project.inlang",
970
963
  repo,
@@ -979,7 +972,8 @@ describe("functionality", () => {
979
972
 
980
973
  describe("watcher", () => {
981
974
  it("changing files in resources should trigger callback of message query", async () => {
982
- const fs = createNodeishMemoryFs()
975
+ const repo = await mockRepo()
976
+ const fs = repo.nodeishFs
983
977
 
984
978
  const messages = {
985
979
  $schema: "https://inlang.com/schema/inlang-message-format",
@@ -1030,7 +1024,6 @@ describe("functionality", () => {
1030
1024
 
1031
1025
  await fs.mkdir("./project.inlang", { recursive: true })
1032
1026
  await fs.writeFile("./project.inlang/settings.json", JSON.stringify(settings))
1033
- const repo = await openRepository("file://", { nodeishFs: fs })
1034
1027
 
1035
1028
  // establish watcher
1036
1029
  const project = await loadProject({
@@ -30,39 +30,16 @@ import { maybeMigrateToDirectory } from "./migrations/migrateToDirectory.js"
30
30
  import { maybeCreateFirstProjectId } from "./migrations/maybeCreateFirstProjectId.js"
31
31
  import type { Repository } from "@lix-js/client"
32
32
  import { capture } from "./telemetry/capture.js"
33
+ import { identifyProject } from "./telemetry/groupIdentify.js"
33
34
 
34
35
  const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
35
36
 
36
- /**
37
- * Creates an inlang instance.
38
- *
39
- * @param projectPath - Absolute path to the inlang settings file.
40
- * @param @deprecated nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
41
- * @param _import - Use `_import` to pass a custom import function for testing,
42
- * and supporting legacy resolvedModules such as CJS.
43
- *
44
- */
45
- export async function loadProject(args: {
46
- projectPath: string
47
- nodeishFs: Repository["nodeishFs"]
48
- /**
49
- * The app id is used to identify the app that is using the SDK.
50
- *
51
- * We use the app id to group events in telemetry to answer questions
52
- * like "Which apps causes these errors?" or "Which apps are used more than others?".
53
- *
54
- * @example
55
- * appId: "app.inlang.badge"
56
- */
57
- appId?: string
58
- _import?: ImportFunction
59
- }): Promise<InlangProject>
60
-
61
37
  /**
62
38
  * @param projectPath - Absolute path to the inlang settings file.
63
39
  * @param repo - An instance of a lix repo as returned by `openRepository`.
64
40
  * @param _import - Use `_import` to pass a custom import function for testing,
65
41
  * and supporting legacy resolvedModules such as CJS.
42
+ * @param appId - The app id to use for telemetry e.g "app.inlang.badge"
66
43
  *
67
44
  */
68
45
  export async function loadProject(args: {
@@ -70,14 +47,6 @@ export async function loadProject(args: {
70
47
  repo: Repository
71
48
  appId?: string
72
49
  _import?: ImportFunction
73
- }): Promise<InlangProject>
74
-
75
- export async function loadProject(args: {
76
- projectPath: string
77
- repo?: Repository
78
- appId?: string
79
- _import?: ImportFunction
80
- nodeishFs?: Repository["nodeishFs"]
81
50
  }): Promise<InlangProject> {
82
51
  const projectPath = normalizePath(args.projectPath)
83
52
 
@@ -98,15 +67,7 @@ export async function loadProject(args: {
98
67
  )
99
68
  }
100
69
 
101
- let fs: Repository["nodeishFs"]
102
- if (args.nodeishFs) {
103
- // TODO: deprecate
104
- fs = args.nodeishFs
105
- } else if (args.repo) {
106
- fs = args.repo.nodeishFs
107
- } else {
108
- throw new LoadProjectInvalidArgument(`Repo missing from arguments.`, { argument: "repo" })
109
- }
70
+ const fs = args.repo.nodeishFs
110
71
 
111
72
  const nodeishFs = createNodeishFsWithAbsolutePaths({
112
73
  projectPath,
@@ -319,6 +280,14 @@ export async function loadProject(args: {
319
280
  if (projectId && projectLoadedCapturedAlready === false) {
320
281
  projectLoadedCapturedAlready = true
321
282
  // TODO ensure that capture is "awaited" without blocking the the app from starting
283
+ await identifyProject({
284
+ projectId,
285
+ properties: {
286
+ // using the id for now as a name but can be changed in the future
287
+ // we need at least one property to make a project visible in the dashboard
288
+ name: projectId,
289
+ },
290
+ })
322
291
  await capture("SDK loaded project", {
323
292
  projectId,
324
293
  properties: {
@@ -332,6 +301,7 @@ export async function loadProject(args: {
332
301
  }
333
302
 
334
303
  return {
304
+ id: projectId,
335
305
  installed: {
336
306
  plugins: createSubscribable(() => installedPlugins()),
337
307
  messageLintRules: createSubscribable(() => installedMessageLintRules()),
@@ -0,0 +1,41 @@
1
+ import { ENV_VARIABLES } from "../env-variables/index.js"
2
+
3
+ /**
4
+ * Capture an event.
5
+ *
6
+ * - manually calling the PostHog API because the SDKs were not platform angostic (and generally bloated)
7
+ */
8
+ export const identifyProject = async (args: {
9
+ projectId: string
10
+ /**
11
+ * Please use snake_case for property names.
12
+ */
13
+ properties: Record<string, any>
14
+ }) => {
15
+ // do not send events if the token is not set
16
+ // (assuming this eases testing)
17
+ if (ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN === undefined) {
18
+ return
19
+ }
20
+ try {
21
+ await fetch("https://eu.posthog.com/capture/", {
22
+ method: "POST",
23
+ body: JSON.stringify({
24
+ api_key: ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN,
25
+ event: "$groupidentify",
26
+ // id is "unknown" because no user information is available
27
+ distinct_id: "unknown",
28
+ properties: {
29
+ $group_type: "project",
30
+ $group_key: args.projectId,
31
+ $group_set: {
32
+ ...args.properties,
33
+ },
34
+ },
35
+ }),
36
+ })
37
+ } catch (e) {
38
+ // TODO implement sentry logging
39
+ // do not console.log and avoid exposing internal errors to the user
40
+ }
41
+ }