@lapis-notes/api 0.1.0 → 0.1.2

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 (109) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +5 -0
  3. package/dist/app-database-host.d.ts +1 -0
  4. package/dist/app-database-host.js +1 -0
  5. package/dist/browser-sync-security.d.ts +45 -0
  6. package/dist/browser-sync-security.js +189 -0
  7. package/dist/build-tools/vite.d.ts +15 -0
  8. package/dist/build-tools/vite.js +36 -0
  9. package/dist/cache.svelte.d.ts +1 -1
  10. package/dist/cache.svelte.js +4 -1
  11. package/dist/components/editor/editor.css +54 -2
  12. package/dist/components/editor/extensions/lint/lapis-lint-tooltip.svelte +18 -5
  13. package/dist/components/editor/extensions/lint/lint-tooltip-actions.d.ts +0 -2
  14. package/dist/components/editor/extensions/lint/lint-tooltip-actions.js +0 -14
  15. package/dist/components/editor/language-service/index.js +23 -2
  16. package/dist/components/icon/icon-list.svelte.d.ts +1 -1
  17. package/dist/components/select/select.svelte.d.ts +1 -1
  18. package/dist/context.svelte.d.ts +11 -1
  19. package/dist/context.svelte.js +38 -1
  20. package/dist/docs-api.d.ts +2 -0
  21. package/dist/docs-api.js +1 -0
  22. package/dist/generated/plugin-host-modules.schema.generated.d.ts +443 -13
  23. package/dist/generated/plugin-host-modules.schema.generated.js +970 -306
  24. package/dist/index.d.ts +5 -0
  25. package/dist/index.js +5 -0
  26. package/dist/language-service/manager.d.ts +5 -0
  27. package/dist/language-service/manager.js +58 -14
  28. package/dist/lapis-extension.d.ts +1 -1
  29. package/dist/lapis-extension.js +12 -0
  30. package/dist/nostr-signer.d.ts +68 -0
  31. package/dist/nostr-signer.js +49 -0
  32. package/dist/plugin-asset-server.d.ts +1 -0
  33. package/dist/plugin-asset-server.js +3 -0
  34. package/dist/plugin-capability-facade.d.ts +1 -1
  35. package/dist/plugin-capability-facade.js +6 -0
  36. package/dist/plugin-distribution/catalog-media.d.ts +4 -0
  37. package/dist/plugin-distribution/catalog-media.js +40 -0
  38. package/dist/plugin-distribution/download-stats.d.ts +4 -0
  39. package/dist/plugin-distribution/download-stats.js +19 -0
  40. package/dist/plugin-distribution/index.d.ts +4 -0
  41. package/dist/plugin-distribution/index.js +4 -0
  42. package/dist/plugin-distribution/installer.d.ts +3 -0
  43. package/dist/plugin-distribution/installer.js +9 -2
  44. package/dist/plugin-distribution/manager.d.ts +71 -5
  45. package/dist/plugin-distribution/manager.js +376 -37
  46. package/dist/plugin-distribution/nostr-registry-adapter.d.ts +29 -0
  47. package/dist/plugin-distribution/nostr-registry-adapter.js +152 -0
  48. package/dist/plugin-distribution/plugin-bundle-verifier.d.ts +43 -0
  49. package/dist/plugin-distribution/plugin-bundle-verifier.js +64 -1
  50. package/dist/plugin-distribution/plugin-bundle.d.ts +14 -0
  51. package/dist/plugin-distribution/plugin-bundle.js +46 -7
  52. package/dist/plugin-distribution/plugin-markdown.d.ts +6 -0
  53. package/dist/plugin-distribution/plugin-markdown.js +94 -0
  54. package/dist/plugin-distribution/registry-client.d.ts +6 -1
  55. package/dist/plugin-distribution/registry-client.js +32 -10
  56. package/dist/plugin-distribution/registry-source-state.d.ts +15 -0
  57. package/dist/plugin-distribution/registry-source-state.js +42 -0
  58. package/dist/plugin-distribution/schemas.d.ts +2511 -2824
  59. package/dist/plugin-distribution/schemas.js +111 -42
  60. package/dist/plugin-distribution/types.d.ts +116 -5
  61. package/dist/plugin-distribution-host.d.ts +4 -0
  62. package/dist/plugin-distribution-host.js +2 -0
  63. package/dist/plugin-install-progress.js +1 -1
  64. package/dist/plugin-manager.d.ts +3 -0
  65. package/dist/plugin-manager.js +21 -13
  66. package/dist/plugin-settings.d.ts +21 -0
  67. package/dist/plugin-settings.js +41 -0
  68. package/dist/plugin.d.ts +13 -0
  69. package/dist/plugin.js +56 -0
  70. package/dist/registry-schema/catalog-v1.d.ts +4143 -0
  71. package/dist/registry-schema/catalog-v1.js +172 -0
  72. package/dist/registry-schema/catalog-v2.d.ts +5275 -0
  73. package/dist/registry-schema/catalog-v2.js +137 -0
  74. package/dist/registry-schema/index.d.ts +1 -0
  75. package/dist/registry-schema/index.js +1 -0
  76. package/dist/registry-schema/release-notes.d.ts +5 -0
  77. package/dist/registry-schema/release-notes.js +21 -0
  78. package/dist/server-host.d.ts +28 -0
  79. package/dist/server-host.js +17 -0
  80. package/dist/storage/app-database.d.ts +37 -2
  81. package/dist/storage/app-database.js +41 -1
  82. package/dist/storage/browser-coordinated-app-database.d.ts +3 -1
  83. package/dist/storage/browser-coordinated-app-database.js +8 -0
  84. package/dist/storage/browser.d.ts +4 -1
  85. package/dist/storage/browser.js +84 -0
  86. package/dist/storage/desktop-native.d.ts +4 -2
  87. package/dist/storage/desktop-native.js +90 -1
  88. package/dist/storage/fs.d.ts +17 -0
  89. package/dist/storage/index.d.ts +1 -0
  90. package/dist/storage/index.js +1 -0
  91. package/dist/storage/memory-vault-adapter.d.ts +3 -1
  92. package/dist/storage/memory-vault-adapter.js +60 -0
  93. package/dist/storage/remote-app-database.d.ts +19 -0
  94. package/dist/storage/remote-app-database.js +130 -0
  95. package/dist/storage/task-projection.d.ts +2 -1
  96. package/dist/storage/task-projection.js +7 -2
  97. package/dist/storage/turso-app-database.d.ts +3 -1
  98. package/dist/storage/turso-app-database.js +45 -0
  99. package/dist/storage/vault-session.js +11 -0
  100. package/dist/storage/vault-state.d.ts +1 -1
  101. package/dist/storage/vault.svelte.d.ts +3 -1
  102. package/dist/storage/vault.svelte.js +76 -3
  103. package/dist/sync-host-security.d.ts +285 -0
  104. package/dist/sync-host-security.js +2237 -0
  105. package/dist/sync-host.d.ts +179 -0
  106. package/dist/sync-host.js +769 -0
  107. package/dist/workspace.svelte.d.ts +1 -1
  108. package/dist/workspace.svelte.js +1 -1
  109. package/package.json +48 -10
@@ -1,6 +1,15 @@
1
+ import { normalizeRegistryCatalogEntry, normalizeRegistryCatalogIndex, normalizeRegistryCatalogDetail, } from "../registry-schema/index.js";
1
2
  import { z } from "zod";
2
3
  import { PluginDistributionError } from "./errors";
3
4
  const isoDate = z.string().datetime({ offset: true });
5
+ const utcDate = z
6
+ .string()
7
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
8
+ .refine((value) => {
9
+ const parsed = new Date(`${value}T00:00:00.000Z`);
10
+ return (!Number.isNaN(parsed.valueOf()) &&
11
+ parsed.toISOString().slice(0, 10) === value);
12
+ });
4
13
  const sha256 = z.string().regex(/^[a-fA-F0-9]{64}$/);
5
14
  const nonEmpty = z.string().min(1);
6
15
  const urlOrRelative = z.string().min(1);
@@ -18,6 +27,11 @@ export const pluginProvenanceSchema = z.enum([
18
27
  "development",
19
28
  ]);
20
29
  export const pluginRegistryChannelSchema = z.enum(["official", "community"]);
30
+ export const pluginCatalogStatusSchema = z.enum([
31
+ "active",
32
+ "pending",
33
+ "revoked",
34
+ ]);
21
35
  export const pluginPlatformSchema = z.enum(["web", "electron", "desktop"]);
22
36
  export const pluginBadgeSchema = z.enum([
23
37
  "official",
@@ -43,10 +57,38 @@ export const trustedSigningKeySchema = z.object({
43
57
  export const remoteFileReferenceSchema = z
44
58
  .object({
45
59
  url: urlOrRelative,
60
+ downloadUrl: urlOrRelative.optional(),
46
61
  sha256,
47
62
  size: z.number().int().nonnegative().optional(),
48
63
  })
49
64
  .passthrough();
65
+ export const pluginMarkdownReferenceSchema = z
66
+ .object({
67
+ url: httpsUrl,
68
+ sourceUrl: httpsUrl,
69
+ sha256,
70
+ size: z
71
+ .number()
72
+ .int()
73
+ .nonnegative()
74
+ .max(256 * 1024),
75
+ mediaType: z.literal("text/markdown"),
76
+ })
77
+ .passthrough();
78
+ export const pluginCatalogContentSchema = z
79
+ .object({
80
+ overview: pluginMarkdownReferenceSchema.optional(),
81
+ changelog: pluginMarkdownReferenceSchema.optional(),
82
+ })
83
+ .passthrough();
84
+ export const pluginCatalogLinksSchema = z
85
+ .object({
86
+ homepage: httpsUrl.optional(),
87
+ repository: httpsUrl.optional(),
88
+ documentation: httpsUrl.optional(),
89
+ issues: httpsUrl.optional(),
90
+ })
91
+ .passthrough();
50
92
  export const pluginReleaseFileSchema = z
51
93
  .object({
52
94
  path: nonEmpty,
@@ -149,35 +191,43 @@ export const pluginContributionSummarySchema = z
149
191
  .optional(),
150
192
  })
151
193
  .passthrough();
152
- export const pluginCatalogEntrySchema = z
194
+ export const pluginCatalogEntrySchema = sharedCatalogSchema(normalizeRegistryCatalogEntry);
195
+ export const pluginCatalogIndexSchema = sharedCatalogSchema(normalizeRegistryCatalogIndex);
196
+ const nonnegativeSafeInteger = z.number().int().nonnegative().safe();
197
+ const pluginDownloadCountsSchema = z.record(nonnegativeSafeInteger);
198
+ const pluginDownloadStatsPluginSchema = z
153
199
  .object({
154
- id: nonEmpty,
155
- name: nonEmpty,
156
- description: z.string(),
157
- readmeUrl: httpsUrl.optional(),
158
- author: nonEmpty,
159
- channel: pluginRegistryChannelSchema,
160
- latestVersion: nonEmpty,
161
- minAppVersion: nonEmpty,
162
- platforms: z.array(pluginPlatformSchema).min(1),
163
- categories: z.array(nonEmpty),
164
- badges: z.array(pluginBadgeSchema).optional(),
165
- detail: urlOrRelative,
166
- contributes: pluginContributionSummarySchema.optional(),
200
+ total: nonnegativeSafeInteger,
201
+ versions: pluginDownloadCountsSchema,
167
202
  })
168
203
  .passthrough();
169
- export const pluginCatalogIndexSchema = z
204
+ const pluginDownloadStatsPeriodSchema = z
205
+ .object({
206
+ from: utcDate.nullable(),
207
+ through: utcDate.nullable(),
208
+ total: nonnegativeSafeInteger,
209
+ plugins: z.record(pluginDownloadStatsPluginSchema),
210
+ versions: pluginDownloadCountsSchema,
211
+ actions: pluginDownloadCountsSchema,
212
+ platforms: pluginDownloadCountsSchema,
213
+ os: pluginDownloadCountsSchema,
214
+ })
215
+ .passthrough();
216
+ export const pluginDownloadStatsSummarySchema = z
170
217
  .object({
171
218
  schemaVersion: z.literal(1),
172
219
  generatedAt: isoDate,
173
- registries: z
174
- .record(z.object({
175
- name: nonEmpty,
176
- trustTier: registryTrustTierSchema,
177
- }))
178
- .optional(),
179
- plugins: z.array(pluginCatalogEntrySchema),
180
- signatures: z.array(signatureRecordSchema).optional(),
220
+ dataset: z.literal("lapis_plugin_downloads_v1"),
221
+ metric: z.literal("approximate_redirect_requests"),
222
+ trackedSince: utcDate,
223
+ through: utcDate.nullable(),
224
+ periods: z
225
+ .object({
226
+ lifetime: pluginDownloadStatsPeriodSchema,
227
+ "7d": pluginDownloadStatsPeriodSchema,
228
+ "30d": pluginDownloadStatsPeriodSchema,
229
+ })
230
+ .passthrough(),
181
231
  })
182
232
  .passthrough();
183
233
  export const pluginRevocationRecordSchema = z.object({
@@ -206,25 +256,7 @@ export const pluginCatalogReleaseSchema = z
206
256
  revoked: pluginRevocationRecordSchema.optional(),
207
257
  })
208
258
  .passthrough();
209
- export const pluginCatalogDetailSchema = z
210
- .object({
211
- schemaVersion: z.literal(1),
212
- id: nonEmpty,
213
- name: nonEmpty,
214
- description: z.string(),
215
- readmeUrl: httpsUrl.optional(),
216
- channel: pluginRegistryChannelSchema,
217
- owner: z.object({
218
- name: nonEmpty,
219
- verified: z.boolean().optional(),
220
- url: z.string().url().optional(),
221
- }),
222
- latestVersion: nonEmpty,
223
- readme: remoteFileReferenceSchema.optional(),
224
- versions: z.record(pluginCatalogReleaseSchema),
225
- signatures: z.array(signatureRecordSchema).optional(),
226
- })
227
- .passthrough();
259
+ export const pluginCatalogDetailSchema = sharedCatalogSchema(normalizeRegistryCatalogDetail);
228
260
  export const pluginReleaseManifestSchema = z.object({
229
261
  schemaVersion: z.literal(1),
230
262
  type: z.literal("lapis.plugin.release"),
@@ -269,6 +301,17 @@ export const installedPluginRecordSchema = z
269
301
  })),
270
302
  runtimeWarnings: z.array(pluginReleaseRuntimeDiagnosticSchema).optional(),
271
303
  signature: signatureRecordSchema.optional(),
304
+ nostrProof: z
305
+ .object({
306
+ authorizationEventId: nonEmpty,
307
+ releaseEventId: nonEmpty,
308
+ decisionEventId: nonEmpty,
309
+ manifestSha256: sha256,
310
+ artifactSha256: sha256,
311
+ artifactSize: z.number().int().nonnegative(),
312
+ requiresRelayRefresh: z.boolean(),
313
+ })
314
+ .optional(),
272
315
  revoked: pluginRevocationRecordSchema.optional(),
273
316
  restartRequired: z.boolean().optional(),
274
317
  })
@@ -288,6 +331,18 @@ export const installedPluginsStateSchema = z
288
331
  .optional(),
289
332
  })
290
333
  .passthrough();
334
+ export const pluginRegistrySourcePreferenceSchema = z
335
+ .object({
336
+ enabled: z.boolean(),
337
+ })
338
+ .passthrough();
339
+ export const pluginRegistrySourcePreferencesStateSchema = z
340
+ .object({
341
+ schemaVersion: z.literal(1),
342
+ updatedAt: isoDate,
343
+ sources: z.record(pluginRegistrySourcePreferenceSchema),
344
+ })
345
+ .passthrough();
291
346
  export const parsePluginDistributionMetadata = (schema, value) => {
292
347
  const result = schema.safeParse(value);
293
348
  if (!result.success) {
@@ -297,3 +352,17 @@ export const parsePluginDistributionMetadata = (schema, value) => {
297
352
  }
298
353
  return result.data;
299
354
  };
355
+ function sharedCatalogSchema(normalize) {
356
+ return z.unknown().transform((value, context) => {
357
+ try {
358
+ return normalize(value);
359
+ }
360
+ catch (error) {
361
+ context.addIssue({
362
+ code: z.ZodIssueCode.custom,
363
+ message: error instanceof Error ? error.message : String(error),
364
+ });
365
+ return z.NEVER;
366
+ }
367
+ });
368
+ }
@@ -1,14 +1,38 @@
1
+ import type { RegistryCatalogV2Entry, RegistryCatalogV2Detail, RegistryReleaseChangelog } from "../registry-schema/index.js";
1
2
  export type PluginProvenance = "bundled" | "official" | "community" | "manual" | "development";
2
3
  export type RegistryTrustTier = "official" | "community" | "local";
4
+ export type PluginRegistrySourceKind = "http" | "nostr";
3
5
  export interface PluginRegistrySource {
4
6
  id: string;
5
7
  name: string;
8
+ /** Omitted by legacy sources and treated as `http`. */
9
+ kind?: PluginRegistrySourceKind;
6
10
  url: string;
11
+ downloadStatsUrl?: string;
12
+ relays?: string[];
13
+ curatorPubkeys?: string[];
14
+ installQuorum?: number;
7
15
  trustTier: RegistryTrustTier;
8
16
  enabled: boolean;
9
17
  builtin?: boolean;
10
18
  }
19
+ export type PluginRegistrySourcePhase = "idle" | "ready" | "degraded" | "error";
20
+ export interface PluginRegistrySourceState {
21
+ source: PluginRegistrySource;
22
+ phase: PluginRegistrySourcePhase;
23
+ installReady: boolean;
24
+ message?: string;
25
+ }
26
+ export interface PluginRegistrySourcePreference {
27
+ enabled: boolean;
28
+ }
29
+ export interface PluginRegistrySourcePreferencesState {
30
+ schemaVersion: 1;
31
+ updatedAt: string;
32
+ sources: Record<string, PluginRegistrySourcePreference>;
33
+ }
11
34
  export type PluginRegistryChannel = "official" | "community";
35
+ export type PluginCatalogStatus = RegistryCatalogV2Entry["status"];
12
36
  export type PluginPlatform = "web" | "electron" | "desktop";
13
37
  export type PluginBadge = "official" | "verified" | "community" | "desktop-only" | "requires-trust" | "update-available" | "revoked";
14
38
  export interface PluginContributionSummary {
@@ -36,7 +60,7 @@ export interface PluginContributionSummary {
36
60
  }>;
37
61
  }
38
62
  export interface PluginCatalogIndex {
39
- schemaVersion: 1;
63
+ schemaVersion: 1 | 2;
40
64
  generatedAt: string;
41
65
  registries?: Record<string, {
42
66
  name: string;
@@ -46,47 +70,120 @@ export interface PluginCatalogIndex {
46
70
  signatures?: SignatureRecord[];
47
71
  }
48
72
  export interface PluginCatalogEntry {
73
+ owner?: RegistryCatalogV2Entry["owner"];
74
+ appearance?: RegistryCatalogV2Entry["appearance"];
75
+ authorAvatarUrl?: string;
49
76
  id: string;
50
77
  name: string;
51
78
  description: string;
52
79
  readmeUrl?: string;
53
80
  author: string;
81
+ authorUrl?: string;
54
82
  channel: PluginRegistryChannel;
83
+ status?: PluginCatalogStatus;
55
84
  latestVersion: string;
56
85
  minAppVersion: string;
57
- platforms: PluginPlatform[];
86
+ platforms: string[];
58
87
  categories: string[];
59
- badges?: PluginBadge[];
88
+ badges?: string[];
89
+ latestRelease?: PluginCatalogLatestRelease;
60
90
  detail: string;
61
91
  contributes?: PluginContributionSummary;
62
92
  }
63
- export interface PluginCatalogDetail {
93
+ export interface PluginCatalogLatestRelease {
94
+ releasedAt: string;
95
+ bundleSize: number;
96
+ }
97
+ export interface PluginDownloadStatsPlugin {
98
+ total: number;
99
+ versions: Record<string, number>;
100
+ }
101
+ export interface PluginDownloadStatsPeriod {
102
+ from: string | null;
103
+ through: string | null;
104
+ total: number;
105
+ plugins: Record<string, PluginDownloadStatsPlugin>;
106
+ versions: Record<string, number>;
107
+ actions: Record<string, number>;
108
+ platforms: Record<string, number>;
109
+ os: Record<string, number>;
110
+ }
111
+ export interface PluginDownloadStatsSummary {
64
112
  schemaVersion: 1;
113
+ generatedAt: string;
114
+ dataset: "lapis_plugin_downloads_v1";
115
+ metric: "approximate_redirect_requests";
116
+ trackedSince: string;
117
+ through: string | null;
118
+ periods: {
119
+ lifetime: PluginDownloadStatsPeriod;
120
+ "7d": PluginDownloadStatsPeriod;
121
+ "30d": PluginDownloadStatsPeriod;
122
+ };
123
+ }
124
+ export interface PluginDownloadCounts {
125
+ lifetime: number;
126
+ recent: number;
127
+ }
128
+ export interface PluginCatalogDetail {
129
+ schemaVersion: 1 | 2;
130
+ author?: string;
131
+ authorUrl?: string;
132
+ authorAvatarUrl?: string;
133
+ appearance?: RegistryCatalogV2Detail["appearance"];
134
+ media?: RegistryCatalogV2Detail["media"];
135
+ gallery?: RegistryCatalogV2Detail["gallery"];
65
136
  id: string;
66
137
  name: string;
67
138
  description: string;
68
139
  readmeUrl?: string;
69
140
  channel: PluginRegistryChannel;
141
+ status?: PluginCatalogStatus;
70
142
  owner: {
71
143
  name: string;
72
144
  verified?: boolean;
73
145
  url?: string;
74
146
  };
75
147
  latestVersion: string;
148
+ license?: string;
149
+ links?: PluginCatalogLinks;
150
+ highlights?: string[];
151
+ content?: PluginCatalogContent;
76
152
  readme?: RemoteFileReference;
153
+ contributes?: PluginContributionSummary;
77
154
  versions: Record<string, PluginCatalogRelease>;
78
155
  signatures?: SignatureRecord[];
79
156
  }
157
+ export interface PluginCatalogLinks {
158
+ homepage?: string;
159
+ repository?: string;
160
+ documentation?: string;
161
+ issues?: string;
162
+ }
163
+ export interface PluginMarkdownReference {
164
+ url: string;
165
+ sourceUrl: string;
166
+ sha256: string;
167
+ size: number;
168
+ mediaType: "text/markdown";
169
+ }
170
+ export interface PluginCatalogContent {
171
+ overview?: PluginMarkdownReference;
172
+ changelog?: PluginMarkdownReference;
173
+ }
80
174
  export interface PluginCatalogRelease {
175
+ changelog?: RegistryReleaseChangelog;
176
+ releasePost?: string;
81
177
  version: string;
82
178
  minAppVersion: string;
83
179
  releasedAt: string;
84
- platforms: PluginPlatform[];
180
+ platforms: string[];
85
181
  bundle: RemoteFileReference;
86
182
  revoked?: PluginRevocationRecord;
87
183
  }
88
184
  export interface RemoteFileReference {
89
185
  url: string;
186
+ downloadUrl?: string;
90
187
  sha256: string;
91
188
  size?: number;
92
189
  }
@@ -196,6 +293,20 @@ export interface InstalledPluginRecord {
196
293
  }>;
197
294
  runtimeWarnings?: PluginReleaseRuntimeDiagnostic[];
198
295
  signature?: SignatureRecord;
296
+ nostrProof?: {
297
+ authorizationEventId: string;
298
+ releaseEventId: string;
299
+ decisionEventId: string;
300
+ manifestSha256: string;
301
+ /** Digest and size of the exact inner payload authenticated at installation. */
302
+ artifactSha256: string;
303
+ artifactSize: number;
304
+ /**
305
+ * Portable proof is valid, but a relay quorum has not checked later
306
+ * revocations.
307
+ */
308
+ requiresRelayRefresh: boolean;
309
+ };
199
310
  revoked?: PluginRevocationRecord;
200
311
  restartRequired?: boolean;
201
312
  [key: string]: unknown;
@@ -0,0 +1,4 @@
1
+ export { createNostrPluginRegistryAdapter } from "./plugin-distribution/nostr-registry-adapter";
2
+ export { DEFAULT_OFFICIAL_PLUGIN_REGISTRY_SOURCE } from "./plugin-distribution/manager";
3
+ export type { PluginRegistrySource } from "./plugin-distribution/types";
4
+ export type { PluginDistributionManager } from "./plugin-distribution/manager";
@@ -0,0 +1,2 @@
1
+ export { createNostrPluginRegistryAdapter } from "./plugin-distribution/nostr-registry-adapter";
2
+ export { DEFAULT_OFFICIAL_PLUGIN_REGISTRY_SOURCE } from "./plugin-distribution/manager";
@@ -67,7 +67,7 @@ export async function withPluginInstallProgress(app, options, action) {
67
67
  source: options.source ?? "Plugin registry",
68
68
  location: options.location ?? "status",
69
69
  cancellable: true,
70
- persistOnError: options.persistOnError ?? false,
70
+ persistOnError: options.persistOnError ?? true,
71
71
  }, async (progressHandle, token) => {
72
72
  const removeProgressListener = app.pluginDistribution.addProgressListener((event) => {
73
73
  if (event.pluginId !== options.pluginId) {
@@ -9,6 +9,7 @@ import { type PluginDependencyResolver, type PluginDependencyResolverFactory } f
9
9
  import { type PluginAssetServer } from "./plugin-asset-server";
10
10
  import type { PluginProvenance } from "./plugin-distribution/types";
11
11
  import { EventDispatcher } from "./events";
12
+ import type { SyncHostCapabilities } from "./sync-host";
12
13
  export interface PluginLoader {
13
14
  pluginsPath: string;
14
15
  }
@@ -41,6 +42,8 @@ export interface PluginManagerOptions {
41
42
  communityPluginHost?: CommunityPluginExecutionHost;
42
43
  createCommunityPluginDependencyResolver?: PluginDependencyResolverFactory;
43
44
  pluginAssetServer?: PluginAssetServer;
45
+ createSyncHostCapabilities?: (pluginId: string, grants: readonly HostedPluginCapability[]) => SyncHostCapabilities;
46
+ revokeSyncHostCapabilities?: (pluginId: string) => void;
44
47
  }
45
48
  export interface PluginDataAliasMigration {
46
49
  fromPluginId: string;
@@ -17,14 +17,11 @@ function formatPluginError(error) {
17
17
  }
18
18
  function resolvePluginModuleExport(moduleExports) {
19
19
  const exportRecord = isRecord(moduleExports) ? moduleExports : {};
20
- for (const [, value] of Object.entries(exportRecord)) {
21
- if (value === Plugin) {
22
- return value;
23
- }
20
+ if (exportRecord.default !== undefined) {
21
+ return exportRecord.default;
24
22
  }
25
- return (moduleExports?.default ||
26
- Object.values(exportRecord)[0] ||
27
- moduleExports);
23
+ const namedPluginClass = Object.values(exportRecord).find((value) => typeof value === "function" && value.prototype instanceof Plugin);
24
+ return namedPluginClass ?? Object.values(exportRecord)[0] ?? moduleExports;
28
25
  }
29
26
  function isRecord(value) {
30
27
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -415,6 +412,8 @@ export class PluginManager extends EventDispatcher {
415
412
  #deferredIndexedExtensions = new Map();
416
413
  #communityPluginHost;
417
414
  #pluginAssetServer;
415
+ #createSyncHostCapabilities;
416
+ #revokeSyncHostCapabilities;
418
417
  constructor(app, pluginsPath, adapter, options = {}) {
419
418
  super();
420
419
  this.app = app;
@@ -424,6 +423,8 @@ export class PluginManager extends EventDispatcher {
424
423
  const dependencyResolver = options.createCommunityPluginDependencyResolver?.(this.dependencies) ??
425
424
  this.dependencies;
426
425
  this.#pluginAssetServer = options.pluginAssetServer;
426
+ this.#createSyncHostCapabilities = options.createSyncHostCapabilities;
427
+ this.#revokeSyncHostCapabilities = options.revokeSyncHostCapabilities;
427
428
  const commonJsHost = options.communityPluginHost ??
428
429
  new RendererCommunityPluginExecutionHost(adapter, dependencyResolver);
429
430
  this.#communityPluginHost = options.pluginAssetServer
@@ -1144,6 +1145,12 @@ export class PluginManager extends EventDispatcher {
1144
1145
  if (this.plugins.has(plugin.manifest.id)) {
1145
1146
  throw new Error(`Plugin id ${plugin.manifest.id} is already registered`);
1146
1147
  }
1148
+ const grantedCapabilities = isExternalPluginSource(options.source)
1149
+ ? this.getGrantedCapabilities(plugin.manifest)
1150
+ : options.source === "system"
1151
+ ? (this.#lapisExtensionIndex.get(plugin.manifest.id)
1152
+ ?.grantedCapabilities ?? [])
1153
+ : (plugin.manifest.requiredCapabilities ?? []).filter((capability) => typeof capability === "string");
1147
1154
  plugin.configureRuntime({
1148
1155
  source: options.source,
1149
1156
  provenance: options.provenance,
@@ -1176,12 +1183,13 @@ export class PluginManager extends EventDispatcher {
1176
1183
  ? (this.#lapisExtensionIndex.get(plugin.manifest.id)
1177
1184
  ?.requestedCapabilities ?? [])
1178
1185
  : [],
1179
- grantedCapabilities: isExternalPluginSource(options.source)
1180
- ? this.getGrantedCapabilities(plugin.manifest)
1181
- : options.source === "system"
1182
- ? (this.#lapisExtensionIndex.get(plugin.manifest.id)
1183
- ?.grantedCapabilities ?? [])
1184
- : [],
1186
+ grantedCapabilities,
1187
+ createSyncHostCapabilities: this.#createSyncHostCapabilities
1188
+ ? () => this.#createSyncHostCapabilities(plugin.manifest.id, grantedCapabilities)
1189
+ : undefined,
1190
+ revokeSyncHostCapabilities: this.#revokeSyncHostCapabilities
1191
+ ? () => this.#revokeSyncHostCapabilities(plugin.manifest.id)
1192
+ : undefined,
1185
1193
  });
1186
1194
  this.plugins.set(plugin.manifest.id, plugin);
1187
1195
  if (isExternalPluginSource(options.source) ||
@@ -0,0 +1,21 @@
1
+ import { type WorkspaceActionResult, type WorkspaceActionSetting, type WorkspaceCustomFieldAdapter, type WorkspaceCustomSetting, type WorkspaceCustomSettingProps, type WorkspaceObjectCollectionSetting, type WorkspaceObjectProperty, type WorkspaceObjectRowAction, type WorkspaceOutputSetting, type WorkspaceSettingField, type WorkspaceSettingsDefinition, type WorkspaceSettingsSection, type WorkspaceSettingsSource } from "@lapismd/design-core/workspace/settings";
2
+ import type { App } from "./context.svelte";
3
+ import type { Plugin } from "./plugin";
4
+ import { PluginSettingTab } from "./settings.svelte";
5
+ export type PluginSettingsDefinition = WorkspaceSettingsDefinition;
6
+ export interface PluginSettingsRegistration {
7
+ readonly definition: PluginSettingsDefinition;
8
+ open(fieldId?: string): boolean;
9
+ dispose(): void;
10
+ }
11
+ export type { WorkspaceActionResult, WorkspaceActionSetting, WorkspaceCustomFieldAdapter, WorkspaceCustomSetting, WorkspaceCustomSettingProps, WorkspaceObjectCollectionSetting, WorkspaceObjectProperty, WorkspaceObjectRowAction, WorkspaceOutputSetting, WorkspaceSettingField, WorkspaceSettingsSection, WorkspaceSettingsSource, };
12
+ /**
13
+ * Compatibility settings tab backed by the same controlled definition as the
14
+ * Workspace Settings surface.
15
+ */
16
+ export declare class DeclarativePluginSettingTab extends PluginSettingTab {
17
+ #private;
18
+ constructor(app: App, plugin: Plugin, definition: PluginSettingsDefinition);
19
+ display(): void;
20
+ hide(): void;
21
+ }
@@ -0,0 +1,41 @@
1
+ import { DeclarativeSettingsSection, WorkspaceSettingsController, } from "@lapismd/design-core/workspace/settings";
2
+ import { mount, unmount } from "svelte";
3
+ import { PluginSettingTab } from "./settings.svelte";
4
+ /**
5
+ * Compatibility settings tab backed by the same controlled definition as the
6
+ * Workspace Settings surface.
7
+ */
8
+ export class DeclarativePluginSettingTab extends PluginSettingTab {
9
+ #definition;
10
+ #component = null;
11
+ #controller = null;
12
+ constructor(app, plugin, definition) {
13
+ super(app, plugin);
14
+ this.#definition = definition;
15
+ }
16
+ display() {
17
+ this.#disposeRenderer();
18
+ this.containerEl.empty();
19
+ const controller = new WorkspaceSettingsController();
20
+ controller.registerDefinition(this.#definition);
21
+ this.#controller = controller;
22
+ this.#component = mount(DeclarativeSettingsSection, {
23
+ target: this.containerEl,
24
+ props: {
25
+ controller,
26
+ section: this.#definition.section,
27
+ class: "ui-workspace-settings--legacy-tab",
28
+ },
29
+ });
30
+ }
31
+ hide() {
32
+ this.#disposeRenderer();
33
+ }
34
+ #disposeRenderer() {
35
+ if (this.#component)
36
+ void unmount(this.#component);
37
+ this.#component = null;
38
+ this.#controller?.destroy();
39
+ this.#controller = null;
40
+ }
41
+ }
package/dist/plugin.d.ts CHANGED
@@ -8,11 +8,13 @@ import type { LapisExtensionContributions, LapisExtensionKind, LapisExtensionMan
8
8
  import type { EditorViewContribution } from "./editor-view-registry";
9
9
  import type { ConfigurationOptionSourceProvider, ConfigurationOptionSourceRegistration } from "./configuration-option-source-registry";
10
10
  import type { HostedPluginCapability } from "./plugin-capability-facade";
11
+ import type { SyncHostCapabilities } from "./sync-host";
11
12
  import type { MarkdownDirectiveRenderer, MarkdownPostProcessor, MarkdownPostProcessorContext, MarkdownViewMenuItemProvider } from "./markdown";
12
13
  import type { MarkdownExtensionContribution } from "./markdown-extension-registry";
13
14
  import type { MarkdownFileSurfaceProvider } from "./markdown-file-surface-registry";
14
15
  import type { TypeWidget } from "./metadata.svelte";
15
16
  import type { PluginSettingTab } from "./settings.svelte";
17
+ import type { PluginSettingsDefinition, PluginSettingsRegistration } from "./plugin-settings";
16
18
  import type { EditorSuggest } from "./suggest";
17
19
  import type { TelemetryAttributes, TelemetryMeasurementOptions, TelemetrySpan, TelemetrySpanOptions } from "./telemetry";
18
20
  import type { PluginProvenance } from "./plugin-distribution/types";
@@ -168,6 +170,8 @@ export interface PluginRuntimeOptions {
168
170
  hostMode?: string;
169
171
  requestedCapabilities?: HostedPluginCapability[];
170
172
  grantedCapabilities?: HostedPluginCapability[];
173
+ createSyncHostCapabilities?: () => SyncHostCapabilities;
174
+ revokeSyncHostCapabilities?: () => void;
171
175
  }
172
176
  /**
173
177
  * Base class for Lapis plugins.
@@ -224,6 +228,8 @@ export declare abstract class Plugin extends Component {
224
228
  get requestedCapabilities(): HostedPluginCapability[];
225
229
  get grantedCapabilities(): HostedPluginCapability[];
226
230
  get id(): string;
231
+ /** Host-authenticated durable capabilities for sync and similar plugins. */
232
+ get syncHost(): SyncHostCapabilities;
227
233
  private getDataPath;
228
234
  private readLegacyData;
229
235
  private writeLegacyData;
@@ -347,4 +353,11 @@ export declare abstract class Plugin extends Component {
347
353
  * @see {@link https://docs.obsidian.md/Plugins/User+interface/Settings#Register+a+settings+tab}
348
354
  */
349
355
  addSettingTab(settingTab: PluginSettingTab): void;
356
+ /**
357
+ * Register one controlled declarative settings definition with Workspace
358
+ * Settings. Source-backed values remain owned by the plugin.
359
+ *
360
+ * @public
361
+ */
362
+ registerSettingsSection(definition: PluginSettingsDefinition): PluginSettingsRegistration;
350
363
  }