@peers-app/peers-sdk 0.18.8 → 0.19.6

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 (60) hide show
  1. package/README.md +74 -1
  2. package/dist/data/files/file-read-stream.js +7 -0
  3. package/dist/data/files/file.types.d.ts +6 -0
  4. package/dist/data/files/file.types.js +18 -0
  5. package/dist/data/files/files.test.js +50 -7
  6. package/dist/data/package-version-resolver.d.ts +13 -5
  7. package/dist/data/package-version-resolver.js +64 -6
  8. package/dist/data/package-version-resolver.test.d.ts +0 -4
  9. package/dist/data/package-version-resolver.test.js +127 -5
  10. package/dist/data/package-versions.d.ts +3 -0
  11. package/dist/data/package-versions.js +5 -0
  12. package/dist/data/packages.d.ts +6 -29
  13. package/dist/data/packages.js +8 -6
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +1 -0
  16. package/dist/package-installer/index.d.ts +10 -0
  17. package/dist/package-installer/index.js +26 -0
  18. package/dist/package-installer/package-author-signing.d.ts +54 -0
  19. package/dist/package-installer/package-author-signing.js +82 -0
  20. package/dist/package-installer/package-author-signing.test.d.ts +1 -0
  21. package/dist/package-installer/package-author-signing.test.js +189 -0
  22. package/dist/package-installer/package-cloner.d.ts +16 -0
  23. package/dist/package-installer/package-cloner.js +115 -0
  24. package/dist/package-installer/package-cloner.test.d.ts +1 -0
  25. package/dist/package-installer/package-cloner.test.js +276 -0
  26. package/dist/package-installer/package-creator.d.ts +22 -0
  27. package/dist/package-installer/package-creator.js +154 -0
  28. package/dist/package-installer/package-creator.test.d.ts +1 -0
  29. package/dist/package-installer/package-creator.test.js +354 -0
  30. package/dist/package-installer/package-installer.d.ts +32 -0
  31. package/dist/package-installer/package-installer.js +247 -0
  32. package/dist/package-installer/package-installer.test.d.ts +1 -0
  33. package/dist/package-installer/package-installer.test.js +666 -0
  34. package/dist/package-installer/package-propagation.d.ts +29 -0
  35. package/dist/package-installer/package-propagation.js +364 -0
  36. package/dist/package-installer/package-propagation.test.d.ts +1 -0
  37. package/dist/package-installer/package-propagation.test.js +1145 -0
  38. package/dist/package-installer/package-publisher.d.ts +55 -0
  39. package/dist/package-installer/package-publisher.js +71 -0
  40. package/dist/package-installer/package-publisher.test.d.ts +1 -0
  41. package/dist/package-installer/package-publisher.test.js +142 -0
  42. package/dist/package-installer/package-remote-checker.d.ts +54 -0
  43. package/dist/package-installer/package-remote-checker.js +194 -0
  44. package/dist/package-installer/package-remote-checker.test.d.ts +1 -0
  45. package/dist/package-installer/package-remote-checker.test.js +269 -0
  46. package/dist/package-installer/package-seed-installer.d.ts +45 -0
  47. package/dist/package-installer/package-seed-installer.js +108 -0
  48. package/dist/package-installer/package-seed-installer.test.d.ts +1 -0
  49. package/dist/package-installer/package-seed-installer.test.js +123 -0
  50. package/dist/package-installer/package-tarball.d.ts +35 -0
  51. package/dist/package-installer/package-tarball.js +57 -0
  52. package/dist/package-installer/package-tarball.test.d.ts +1 -0
  53. package/dist/package-installer/package-tarball.test.js +75 -0
  54. package/dist/package-installer/types.d.ts +110 -0
  55. package/dist/package-installer/types.js +2 -0
  56. package/dist/rpc-types.d.ts +14 -0
  57. package/dist/rpc-types.js +6 -0
  58. package/dist/system-ids.d.ts +1 -0
  59. package/dist/system-ids.js +2 -1
  60. package/package.json +3 -2
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const package_cloner_1 = require("./package-cloner");
4
+ // --- Mocks for SDK internals ---
5
+ const mockSignAndSavePkg = jest.fn();
6
+ jest.mock("../data/packages", () => ({
7
+ Packages: () => ({
8
+ signAndSave: mockSignAndSavePkg,
9
+ }),
10
+ }));
11
+ const mockGroupDeviceVar = jest.fn();
12
+ jest.mock("../data/persistent-vars", () => ({
13
+ groupDeviceVar: (...args) => mockGroupDeviceVar(...args),
14
+ }));
15
+ const mockInstallPackageFromBundles = jest.fn();
16
+ const mockGetPackageInfo = jest.fn();
17
+ jest.mock("./package-installer", () => ({
18
+ installPackageFromBundles: (...args) => mockInstallPackageFromBundles(...args),
19
+ getPackageInfo: (...args) => mockGetPackageInfo(...args),
20
+ }));
21
+ // --- Test utilities ---
22
+ function makeMockDeps(shellExec) {
23
+ return {
24
+ fs: {
25
+ readFile: jest.fn(async () => ""),
26
+ writeFile: jest.fn(async () => { }),
27
+ exists: jest.fn(async () => false),
28
+ readJson: jest.fn(async () => ({})),
29
+ },
30
+ shell: {
31
+ exec: shellExec ?? jest.fn(async () => ({ stdout: "", stderr: "" })),
32
+ },
33
+ resolvePath: (...segments) => segments.join("/"),
34
+ packagesRootDir: "/packages",
35
+ };
36
+ }
37
+ function mockDataContext() {
38
+ return {
39
+ packageLoader: {
40
+ loadPackage: jest.fn().mockResolvedValue(undefined),
41
+ },
42
+ };
43
+ }
44
+ // --- Tests ---
45
+ beforeEach(() => {
46
+ jest.clearAllMocks();
47
+ });
48
+ describe("deriveRepoSlug", () => {
49
+ it("extracts slug from https URL with .git suffix", () => {
50
+ expect((0, package_cloner_1.deriveRepoSlug)("https://github.com/peers-app/groceries.git")).toBe("groceries");
51
+ });
52
+ it("extracts slug from https URL without .git suffix", () => {
53
+ expect((0, package_cloner_1.deriveRepoSlug)("https://github.com/user/my-pkg")).toBe("my-pkg");
54
+ });
55
+ it("handles SSH-style URLs", () => {
56
+ expect((0, package_cloner_1.deriveRepoSlug)("git@github.com:user/some-repo.git")).toBe("some-repo");
57
+ });
58
+ it("returns empty string for empty URL", () => {
59
+ expect((0, package_cloner_1.deriveRepoSlug)("")).toBe("");
60
+ });
61
+ });
62
+ describe("clonePackage", () => {
63
+ it("throws when git is not installed", async () => {
64
+ const shellExec = jest.fn(async (cmd) => {
65
+ if (cmd === "git --version")
66
+ throw new Error("not found");
67
+ return { stdout: "", stderr: "" };
68
+ });
69
+ const deps = makeMockDeps(shellExec);
70
+ const dc = mockDataContext();
71
+ await expect((0, package_cloner_1.clonePackage)(dc, deps, { remoteRepo: "https://github.com/peers-app/test.git" })).rejects.toThrow("git is not installed");
72
+ });
73
+ it("throws when npm is not installed", async () => {
74
+ const shellExec = jest.fn(async (cmd) => {
75
+ if (cmd === "npm --version")
76
+ throw new Error("not found");
77
+ return { stdout: "", stderr: "" };
78
+ });
79
+ const deps = makeMockDeps(shellExec);
80
+ const dc = mockDataContext();
81
+ await expect((0, package_cloner_1.clonePackage)(dc, deps, { remoteRepo: "https://github.com/peers-app/test.git" })).rejects.toThrow("npm is not installed");
82
+ });
83
+ it("clones, installs, builds, and registers a package with valid packageId", async () => {
84
+ const deps = makeMockDeps();
85
+ const dc = mockDataContext();
86
+ // Directory doesn't exist
87
+ deps.fs.exists.mockResolvedValue(false);
88
+ // After clone, getPackageInfo returns a valid package
89
+ mockGetPackageInfo.mockResolvedValue({
90
+ packageId: "00mnkoyo7mizav6tseubjzr3a",
91
+ name: "groceries",
92
+ description: "Shopping list app",
93
+ });
94
+ const pvarSetter = jest.fn();
95
+ mockGroupDeviceVar.mockReturnValue(pvarSetter);
96
+ const mockPv = { packageVersionId: "pv001" };
97
+ mockInstallPackageFromBundles.mockResolvedValue({
98
+ packageVersion: mockPv,
99
+ loaded: undefined,
100
+ unchanged: false,
101
+ });
102
+ const result = await (0, package_cloner_1.clonePackage)(dc, deps, {
103
+ remoteRepo: "https://github.com/peers-app/groceries.git",
104
+ });
105
+ // Verify clone
106
+ expect(deps.shell.exec).toHaveBeenCalledWith("git clone https://github.com/peers-app/groceries.git /packages/groceries");
107
+ // Verify install + build
108
+ expect(deps.shell.exec).toHaveBeenCalledWith("npm install", { cwd: "/packages/groceries" });
109
+ expect(deps.shell.exec).toHaveBeenCalledWith("npm run build", { cwd: "/packages/groceries" });
110
+ // Verify package record was created (peers-app org = not disabled)
111
+ expect(mockSignAndSavePkg).toHaveBeenCalledWith(expect.objectContaining({
112
+ packageId: "00mnkoyo7mizav6tseubjzr3a",
113
+ name: "groceries",
114
+ description: "Shopping list app",
115
+ disabled: false,
116
+ remoteRepo: "https://github.com/peers-app/groceries.git",
117
+ }), { restoreIfDeleted: true, saveAsSnapshot: true });
118
+ // Verify device var
119
+ expect(pvarSetter).toHaveBeenCalledWith("/packages/groceries");
120
+ // Verify install
121
+ expect(mockInstallPackageFromBundles).toHaveBeenCalledWith(dc, deps, "00mnkoyo7mizav6tseubjzr3a", { localPath: "/packages/groceries" });
122
+ expect(result.packageId).toBe("00mnkoyo7mizav6tseubjzr3a");
123
+ expect(result.localPath).toBe("/packages/groceries");
124
+ expect(result.remoteRepo).toBe("https://github.com/peers-app/groceries.git");
125
+ expect(result.packageVersion).toBe(mockPv);
126
+ });
127
+ it("auto-disables packages from non-peers-app orgs", async () => {
128
+ const deps = makeMockDeps();
129
+ const dc = mockDataContext();
130
+ deps.fs.exists.mockResolvedValue(false);
131
+ mockGetPackageInfo.mockResolvedValue({
132
+ packageId: "00mnkoyo7mizav6tseubjzr3b",
133
+ name: "external-pkg",
134
+ });
135
+ mockGroupDeviceVar.mockReturnValue(jest.fn());
136
+ mockInstallPackageFromBundles.mockResolvedValue({
137
+ packageVersion: { packageVersionId: "pv" },
138
+ loaded: undefined,
139
+ unchanged: false,
140
+ });
141
+ await (0, package_cloner_1.clonePackage)(dc, deps, {
142
+ remoteRepo: "https://github.com/some-user/external-pkg.git",
143
+ });
144
+ expect(mockSignAndSavePkg).toHaveBeenCalledWith(expect.objectContaining({ disabled: true }), expect.any(Object));
145
+ });
146
+ it("respects explicit disabled override", async () => {
147
+ const deps = makeMockDeps();
148
+ const dc = mockDataContext();
149
+ deps.fs.exists.mockResolvedValue(false);
150
+ mockGetPackageInfo.mockResolvedValue({
151
+ packageId: "00mnkoyo7mizav6tseubjzr3c",
152
+ name: "forced-enabled",
153
+ });
154
+ mockGroupDeviceVar.mockReturnValue(jest.fn());
155
+ mockInstallPackageFromBundles.mockResolvedValue({
156
+ packageVersion: { packageVersionId: "pv" },
157
+ loaded: undefined,
158
+ unchanged: false,
159
+ });
160
+ // External repo but explicitly enabled
161
+ await (0, package_cloner_1.clonePackage)(dc, deps, {
162
+ remoteRepo: "https://github.com/external/pkg.git",
163
+ disabled: false,
164
+ });
165
+ expect(mockSignAndSavePkg).toHaveBeenCalledWith(expect.objectContaining({ disabled: false }), expect.any(Object));
166
+ });
167
+ it("skips build when skipBuild is true", async () => {
168
+ const deps = makeMockDeps();
169
+ const dc = mockDataContext();
170
+ deps.fs.exists.mockResolvedValue(false);
171
+ mockGetPackageInfo.mockResolvedValue({
172
+ packageId: "00mnkoyo7mizav6tseubjzr3d",
173
+ name: "prebuilt",
174
+ });
175
+ mockGroupDeviceVar.mockReturnValue(jest.fn());
176
+ mockInstallPackageFromBundles.mockResolvedValue({
177
+ packageVersion: { packageVersionId: "pv" },
178
+ loaded: undefined,
179
+ unchanged: false,
180
+ });
181
+ await (0, package_cloner_1.clonePackage)(dc, deps, {
182
+ remoteRepo: "https://github.com/peers-app/prebuilt.git",
183
+ skipBuild: true,
184
+ });
185
+ const execCalls = deps.shell.exec.mock.calls.map((c) => c[0]);
186
+ expect(execCalls).toContain("git --version");
187
+ expect(execCalls).toContain("npm --version");
188
+ expect(execCalls).toContain("git clone https://github.com/peers-app/prebuilt.git /packages/prebuilt");
189
+ expect(execCalls).not.toContain("npm install");
190
+ expect(execCalls).not.toContain("npm run build");
191
+ });
192
+ it("generates packageId and patches package.json when repo has no valid ID", async () => {
193
+ const deps = makeMockDeps();
194
+ const dc = mockDataContext();
195
+ deps.fs.exists.mockResolvedValue(false);
196
+ // No valid packageId
197
+ mockGetPackageInfo.mockResolvedValue({
198
+ name: "no-id-package",
199
+ description: "A package without peers config",
200
+ });
201
+ // readJson returns a package.json without peers.packageId
202
+ deps.fs.readJson.mockResolvedValue({
203
+ name: "no-id-package",
204
+ version: "1.0.0",
205
+ });
206
+ mockGroupDeviceVar.mockReturnValue(jest.fn());
207
+ mockInstallPackageFromBundles.mockResolvedValue({
208
+ packageVersion: { packageVersionId: "pv" },
209
+ loaded: undefined,
210
+ unchanged: false,
211
+ });
212
+ const result = await (0, package_cloner_1.clonePackage)(dc, deps, {
213
+ remoteRepo: "https://github.com/peers-app/no-id-package.git",
214
+ });
215
+ // Should have written a patched package.json
216
+ expect(deps.fs.writeFile).toHaveBeenCalledWith("/packages/no-id-package/package.json", expect.any(String));
217
+ const patchedJson = JSON.parse(deps.fs.writeFile.mock.calls[0][1]);
218
+ expect(patchedJson.peers.packageId).toMatch(/^[a-z0-9]{25}$/);
219
+ // Result should use the generated ID
220
+ expect(result.packageId).toMatch(/^[a-z0-9]{25}$/);
221
+ });
222
+ it("delegates to installPackageFromBundles when directory already exists with valid package", async () => {
223
+ const deps = makeMockDeps();
224
+ const dc = mockDataContext();
225
+ // Location exists
226
+ deps.fs.exists.mockResolvedValue(true);
227
+ mockGetPackageInfo.mockResolvedValue({
228
+ packageId: "00mnkoyo7mizav6tseubjzr3e",
229
+ name: "existing",
230
+ });
231
+ const mockPv = { packageVersionId: "pvexist" };
232
+ mockInstallPackageFromBundles.mockResolvedValue({
233
+ packageVersion: mockPv,
234
+ loaded: undefined,
235
+ unchanged: true,
236
+ });
237
+ const result = await (0, package_cloner_1.clonePackage)(dc, deps, {
238
+ remoteRepo: "https://github.com/peers-app/existing.git",
239
+ });
240
+ // Should NOT clone or build
241
+ const execCalls = deps.shell.exec.mock.calls.map((c) => c[0]);
242
+ expect(execCalls).not.toContain(expect.stringContaining("git clone"));
243
+ expect(execCalls).not.toContain("npm install");
244
+ // Should delegate
245
+ expect(mockInstallPackageFromBundles).toHaveBeenCalled();
246
+ expect(result.packageId).toBe("00mnkoyo7mizav6tseubjzr3e");
247
+ });
248
+ it("throws when directory exists but has no valid packageId", async () => {
249
+ const deps = makeMockDeps();
250
+ const dc = mockDataContext();
251
+ deps.fs.exists.mockResolvedValue(true);
252
+ mockGetPackageInfo.mockResolvedValue({ name: "invalid-pkg" });
253
+ await expect((0, package_cloner_1.clonePackage)(dc, deps, { remoteRepo: "https://github.com/user/invalid-pkg.git" })).rejects.toThrow("does not contain a valid Peers package");
254
+ });
255
+ it("uses custom location when provided", async () => {
256
+ const deps = makeMockDeps();
257
+ const dc = mockDataContext();
258
+ deps.fs.exists.mockResolvedValue(false);
259
+ mockGetPackageInfo.mockResolvedValue({
260
+ packageId: "00mnkoyo7mizav6tseubjzr3f",
261
+ name: "custom-loc",
262
+ });
263
+ mockGroupDeviceVar.mockReturnValue(jest.fn());
264
+ mockInstallPackageFromBundles.mockResolvedValue({
265
+ packageVersion: { packageVersionId: "pv" },
266
+ loaded: undefined,
267
+ unchanged: false,
268
+ });
269
+ await (0, package_cloner_1.clonePackage)(dc, deps, {
270
+ remoteRepo: "https://github.com/peers-app/test.git",
271
+ location: "/custom/install/path",
272
+ });
273
+ expect(deps.shell.exec).toHaveBeenCalledWith("git clone https://github.com/peers-app/test.git /custom/install/path");
274
+ expect(deps.shell.exec).toHaveBeenCalledWith("npm install", { cwd: "/custom/install/path" });
275
+ });
276
+ });
@@ -0,0 +1,22 @@
1
+ import type { DataContext } from "../context/data-context";
2
+ import type { ICreatePackageOpts, ICreatePackageResult, IPackageInstallerDeps } from "./types";
3
+ /**
4
+ * Convert a human-readable package name to a filesystem-safe folder slug.
5
+ * "My Widget" → "my-widget", "camelCase Thing" → "camel-case-thing"
6
+ */
7
+ export declare function nameToFolderSlug(name: string): string;
8
+ /**
9
+ * Ensure the package template is available locally.
10
+ * On first call, clones the template repo into a dot-prefixed cache directory.
11
+ * On subsequent calls, attempts a `git pull` to refresh (continues silently if offline).
12
+ * @returns The path to the cached template directory.
13
+ */
14
+ export declare function ensureTemplateCache(deps: IPackageInstallerDeps, templateRepo: string): Promise<string>;
15
+ /**
16
+ * Create a new Peers package from the package template.
17
+ *
18
+ * Clones the template repo, patches IDs and metadata, runs npm install + build,
19
+ * initializes a fresh git repo, then delegates to {@link installPackageFromBundles}
20
+ * to register the dev bundle in the database.
21
+ */
22
+ export declare function createPackage(dataContext: DataContext, deps: IPackageInstallerDeps, opts: ICreatePackageOpts): Promise<ICreatePackageResult>;
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nameToFolderSlug = nameToFolderSlug;
4
+ exports.ensureTemplateCache = ensureTemplateCache;
5
+ exports.createPackage = createPackage;
6
+ const packages_1 = require("../data/packages");
7
+ const persistent_vars_1 = require("../data/persistent-vars");
8
+ const keys_1 = require("../keys");
9
+ const system_ids_1 = require("../system-ids");
10
+ const utils_1 = require("../utils");
11
+ const package_author_signing_1 = require("./package-author-signing");
12
+ const package_installer_1 = require("./package-installer");
13
+ const DEFAULT_TEMPLATE_REPO = "https://github.com/peers-app/peers-package-template.git";
14
+ const TEMPLATE_CACHE_DIR = ".peers-package-template";
15
+ /**
16
+ * Convert a human-readable package name to a filesystem-safe folder slug.
17
+ * "My Widget" → "my-widget", "camelCase Thing" → "camel-case-thing"
18
+ */
19
+ function nameToFolderSlug(name) {
20
+ let slug = name.replace(/[^a-zA-Z0-9]/g, " ");
21
+ slug = slug.replace(/\s+/g, " ").trim();
22
+ return (0, utils_1.camelCaseToHyphens)(slug);
23
+ }
24
+ /**
25
+ * Ensure the package template is available locally.
26
+ * On first call, clones the template repo into a dot-prefixed cache directory.
27
+ * On subsequent calls, attempts a `git pull` to refresh (continues silently if offline).
28
+ * @returns The path to the cached template directory.
29
+ */
30
+ async function ensureTemplateCache(deps, templateRepo) {
31
+ const cachePath = deps.resolvePath(deps.packagesRootDir, TEMPLATE_CACHE_DIR);
32
+ if (await deps.fs.exists(cachePath)) {
33
+ try {
34
+ await deps.shell.exec("git pull", { cwd: cachePath });
35
+ }
36
+ catch {
37
+ // Offline or remote unreachable — continue with stale cache
38
+ }
39
+ }
40
+ else {
41
+ await deps.shell.exec(`git clone ${templateRepo} ${cachePath}`);
42
+ }
43
+ return cachePath;
44
+ }
45
+ /**
46
+ * Create a new Peers package from the package template.
47
+ *
48
+ * Clones the template repo, patches IDs and metadata, runs npm install + build,
49
+ * initializes a fresh git repo, then delegates to {@link installPackageFromBundles}
50
+ * to register the dev bundle in the database.
51
+ */
52
+ async function createPackage(dataContext, deps, opts) {
53
+ const location = opts.location ?? deps.resolvePath(deps.packagesRootDir, nameToFolderSlug(opts.name));
54
+ // If directory already exists, treat as an import rather than a fresh create
55
+ if (await deps.fs.exists(location)) {
56
+ const info = await (0, package_installer_1.getPackageInfo)(deps, location);
57
+ if (info.packageId) {
58
+ const result = await (0, package_installer_1.installPackageFromBundles)(dataContext, deps, info.packageId, {
59
+ localPath: location,
60
+ });
61
+ return {
62
+ packageId: info.packageId,
63
+ localPath: location,
64
+ packageVersion: result.packageVersion,
65
+ };
66
+ }
67
+ throw new Error(`Directory already exists at ${location} but does not contain a valid Peers package`);
68
+ }
69
+ // Generate fresh IDs
70
+ const packageId = (0, utils_1.newid)();
71
+ const contractId = (0, utils_1.newid)();
72
+ const appScreenId = (0, utils_1.newid)();
73
+ const templateRepo = opts.templateRepo ?? DEFAULT_TEMPLATE_REPO;
74
+ // Get template contents into location
75
+ if (opts.templateRepo && opts.templateRepo !== DEFAULT_TEMPLATE_REPO) {
76
+ // Custom template — clone directly (no caching)
77
+ await deps.shell.exec(`git clone ${templateRepo} ${location}`);
78
+ }
79
+ else {
80
+ // Default template — use local cache for speed and offline support
81
+ const cachePath = await ensureTemplateCache(deps, templateRepo);
82
+ await deps.shell.exec(`cp -r ${cachePath}/. ${location}`);
83
+ }
84
+ // Patch package.json
85
+ const packageJsonPath = deps.resolvePath(location, "package.json");
86
+ const packageObj = await deps.fs.readJson(packageJsonPath);
87
+ packageObj.peers = packageObj.peers || {};
88
+ packageObj.peers.packageId = packageId;
89
+ packageObj.name = (0, utils_1.camelCaseToHyphens)(opts.name);
90
+ if (opts.author) {
91
+ packageObj.author = opts.author;
92
+ }
93
+ delete packageObj.repository;
94
+ await deps.fs.writeFile(packageJsonPath, JSON.stringify(packageObj, null, 2));
95
+ // Patch src/consts.ts placeholders
96
+ const constsPath = deps.resolvePath(location, "src", "consts.ts");
97
+ if (await deps.fs.exists(constsPath)) {
98
+ let consts = await deps.fs.readFile(constsPath);
99
+ consts = consts
100
+ .replace("<package-name>", opts.name)
101
+ .replace("<package-id>", packageId)
102
+ .replace("<app-screen-id>", appScreenId)
103
+ .replace("<contract-id>", contractId);
104
+ await deps.fs.writeFile(constsPath, consts);
105
+ }
106
+ // Initialize as a new project: remove template git history, install, build, fresh git
107
+ await deps.shell.exec("rm -rf .git", { cwd: location });
108
+ await deps.shell.exec("npm install", { cwd: location });
109
+ await deps.shell.exec("npm run build", { cwd: location });
110
+ await deps.shell.exec("git init", { cwd: location });
111
+ await deps.shell.exec("git add .", { cwd: location });
112
+ await deps.shell.exec('git commit -m "Initial commit"', { cwd: location });
113
+ // Generate signing keypair and store secret in user's personal group
114
+ let publishPublicKey = "";
115
+ if (opts.personalContext) {
116
+ const keys = (0, keys_1.newKeys)();
117
+ publishPublicKey = keys.publicKey;
118
+ const keyName = `${package_author_signing_1.PACKAGE_SIGNING_KEY_PREFIX}${packageId}`;
119
+ await (0, persistent_vars_1.PersistentVars)(opts.personalContext).save({
120
+ persistentVarId: (0, utils_1.deterministicPvarId)(keyName),
121
+ name: keyName,
122
+ scope: "user",
123
+ value: { value: keys.secretKey },
124
+ isSecret: true,
125
+ description: `Signing key for package "${opts.name}"`,
126
+ modifiedAt: Date.now(),
127
+ });
128
+ }
129
+ // Register the package record
130
+ await (0, packages_1.Packages)(dataContext).signAndSave({
131
+ packageId,
132
+ name: opts.name,
133
+ description: "",
134
+ createdBy: system_ids_1.peersRootUserId,
135
+ disabled: false,
136
+ publishPublicKey,
137
+ signature: "",
138
+ }, { restoreIfDeleted: true, saveAsSnapshot: true });
139
+ // Set the packageLocalPath device var
140
+ const pvar = (0, persistent_vars_1.groupDeviceVar)(`packageLocalPath_${packageId}`, {
141
+ defaultValue: deps.packagesRootDir,
142
+ dataContext,
143
+ });
144
+ pvar(location);
145
+ // Install the dev bundle
146
+ const result = await (0, package_installer_1.installPackageFromBundles)(dataContext, deps, packageId, {
147
+ localPath: location,
148
+ });
149
+ return {
150
+ packageId,
151
+ localPath: location,
152
+ packageVersion: result.packageVersion,
153
+ };
154
+ }
@@ -0,0 +1 @@
1
+ export {};