@aglyn/plugins-marketplace 1.0.0-beta.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +7 -0
- package/package.json +51 -0
- package/src/index.d.ts +19 -0
- package/src/index.js +20 -0
- package/src/index.js.map +1 -0
- package/src/lib/components/artifact-update-dialog.component.d.ts +27 -0
- package/src/lib/components/artifact-update-dialog.component.js +244 -0
- package/src/lib/components/artifact-update-dialog.component.js.map +1 -0
- package/src/lib/components/host-plugins-card.component.d.ts +23 -0
- package/src/lib/components/host-plugins-card.component.js +566 -0
- package/src/lib/components/host-plugins-card.component.js.map +1 -0
- package/src/lib/components/listing-content.component.d.ts +57 -0
- package/src/lib/components/listing-content.component.js +1880 -0
- package/src/lib/components/listing-content.component.js.map +1 -0
- package/src/lib/components/listing-image.component.d.ts +46 -0
- package/src/lib/components/listing-image.component.js +38 -0
- package/src/lib/components/listing-image.component.js.map +1 -0
- package/src/lib/components/listing-reviews.component.d.ts +22 -0
- package/src/lib/components/listing-reviews.component.js +419 -0
- package/src/lib/components/listing-reviews.component.js.map +1 -0
- package/src/lib/components/marketplace-browse.component.d.ts +38 -0
- package/src/lib/components/marketplace-browse.component.js +802 -0
- package/src/lib/components/marketplace-browse.component.js.map +1 -0
- package/src/lib/components/plugin-site-set-panel.component.d.ts +24 -0
- package/src/lib/components/plugin-site-set-panel.component.js +137 -0
- package/src/lib/components/plugin-site-set-panel.component.js.map +1 -0
- package/src/lib/components/plugin-site-set.component.d.ts +43 -0
- package/src/lib/components/plugin-site-set.component.js +425 -0
- package/src/lib/components/plugin-site-set.component.js.map +1 -0
- package/src/lib/components/rating-input.component.d.ts +3 -0
- package/src/lib/components/rating-input.component.js +47 -0
- package/src/lib/components/rating-input.component.js.map +1 -0
- package/src/lib/components/report-target.component.d.ts +36 -0
- package/src/lib/components/report-target.component.js +181 -0
- package/src/lib/components/report-target.component.js.map +1 -0
- package/src/lib/components/uninstall-impact-dialog.component.d.ts +40 -0
- package/src/lib/components/uninstall-impact-dialog.component.js +244 -0
- package/src/lib/components/uninstall-impact-dialog.component.js.map +1 -0
- package/src/lib/constants/bundle-common.d.ts +8 -0
- package/src/lib/constants/bundle-common.js +9 -0
- package/src/lib/constants/bundle-common.js.map +1 -0
- package/src/lib/hooks/use-artifact-update.d.ts +41 -0
- package/src/lib/hooks/use-artifact-update.js +169 -0
- package/src/lib/hooks/use-artifact-update.js.map +1 -0
- package/src/lib/hooks/use-marketplace-actions.d.ts +37 -0
- package/src/lib/hooks/use-marketplace-actions.js +523 -0
- package/src/lib/hooks/use-marketplace-actions.js.map +1 -0
- package/src/lib/model/artifact-merge.d.ts +154 -0
- package/src/lib/model/artifact-merge.js +228 -0
- package/src/lib/model/artifact-merge.js.map +1 -0
- package/src/lib/model/index.d.ts +43 -0
- package/src/lib/model/index.js +41 -0
- package/src/lib/model/index.js.map +1 -0
- package/src/lib/model/marketplace-props.d.ts +57 -0
- package/src/lib/model/marketplace-props.js +441 -0
- package/src/lib/model/marketplace-props.js.map +1 -0
- package/src/lib/model/marketplace.d.ts +833 -0
- package/src/lib/model/marketplace.js +929 -0
- package/src/lib/model/marketplace.js.map +1 -0
- package/src/lib/model/rating-field.d.ts +24 -0
- package/src/lib/model/rating-field.js +33 -0
- package/src/lib/model/rating-field.js.map +1 -0
- package/src/lib/plugin.d.ts +33 -0
- package/src/lib/plugin.js +86 -0
- package/src/lib/plugin.js.map +1 -0
- package/src/lib/server/billing-webhook.d.ts +25 -0
- package/src/lib/server/billing-webhook.js +1062 -0
- package/src/lib/server/billing-webhook.js.map +1 -0
- package/src/lib/server/checkout.d.ts +76 -0
- package/src/lib/server/checkout.js +477 -0
- package/src/lib/server/checkout.js.map +1 -0
- package/src/lib/server/connect.d.ts +24 -0
- package/src/lib/server/connect.js +205 -0
- package/src/lib/server/connect.js.map +1 -0
- package/src/lib/server/install-dataset-schema.d.ts +32 -0
- package/src/lib/server/install-dataset-schema.js +255 -0
- package/src/lib/server/install-dataset-schema.js.map +1 -0
- package/src/lib/server/install-email-starter.d.ts +65 -0
- package/src/lib/server/install-email-starter.js +279 -0
- package/src/lib/server/install-email-starter.js.map +1 -0
- package/src/lib/server/install-email-template.d.ts +34 -0
- package/src/lib/server/install-email-template.js +252 -0
- package/src/lib/server/install-email-template.js.map +1 -0
- package/src/lib/server/install-layout.d.ts +29 -0
- package/src/lib/server/install-layout.js +323 -0
- package/src/lib/server/install-layout.js.map +1 -0
- package/src/lib/server/install-pin-counts.d.ts +173 -0
- package/src/lib/server/install-pin-counts.js +248 -0
- package/src/lib/server/install-pin-counts.js.map +1 -0
- package/src/lib/server/install-plugin.d.ts +18 -0
- package/src/lib/server/install-plugin.js +405 -0
- package/src/lib/server/install-plugin.js.map +1 -0
- package/src/lib/server/install-template.d.ts +37 -0
- package/src/lib/server/install-template.js +348 -0
- package/src/lib/server/install-template.js.map +1 -0
- package/src/lib/server/install-theme.d.ts +42 -0
- package/src/lib/server/install-theme.js +313 -0
- package/src/lib/server/install-theme.js.map +1 -0
- package/src/lib/server/install.d.ts +27 -0
- package/src/lib/server/install.js +276 -0
- package/src/lib/server/install.js.map +1 -0
- package/src/lib/server/listing-versions.d.ts +26 -0
- package/src/lib/server/listing-versions.js +378 -0
- package/src/lib/server/listing-versions.js.map +1 -0
- package/src/lib/server/preview-image.d.ts +37 -0
- package/src/lib/server/preview-image.js +216 -0
- package/src/lib/server/preview-image.js.map +1 -0
- package/src/lib/server/provenance.d.ts +100 -0
- package/src/lib/server/provenance.js +118 -0
- package/src/lib/server/provenance.js.map +1 -0
- package/src/lib/server/publish-dataset-schema.d.ts +31 -0
- package/src/lib/server/publish-dataset-schema.js +202 -0
- package/src/lib/server/publish-dataset-schema.js.map +1 -0
- package/src/lib/server/publish-email-starter.d.ts +44 -0
- package/src/lib/server/publish-email-starter.js +231 -0
- package/src/lib/server/publish-email-starter.js.map +1 -0
- package/src/lib/server/publish-email-template.d.ts +33 -0
- package/src/lib/server/publish-email-template.js +211 -0
- package/src/lib/server/publish-email-template.js.map +1 -0
- package/src/lib/server/publish-layout.d.ts +33 -0
- package/src/lib/server/publish-layout.js +243 -0
- package/src/lib/server/publish-layout.js.map +1 -0
- package/src/lib/server/publish-plugin.d.ts +18 -0
- package/src/lib/server/publish-plugin.js +485 -0
- package/src/lib/server/publish-plugin.js.map +1 -0
- package/src/lib/server/publish-preconditions.d.ts +86 -0
- package/src/lib/server/publish-preconditions.js +160 -0
- package/src/lib/server/publish-preconditions.js.map +1 -0
- package/src/lib/server/publish-template.d.ts +27 -0
- package/src/lib/server/publish-template.js +215 -0
- package/src/lib/server/publish-template.js.map +1 -0
- package/src/lib/server/publish-theme.d.ts +38 -0
- package/src/lib/server/publish-theme.js +186 -0
- package/src/lib/server/publish-theme.js.map +1 -0
- package/src/lib/server/publish.d.ts +26 -0
- package/src/lib/server/publish.js +204 -0
- package/src/lib/server/publish.js.map +1 -0
- package/src/lib/server/publisher-profile-save.d.ts +30 -0
- package/src/lib/server/publisher-profile-save.js +226 -0
- package/src/lib/server/publisher-profile-save.js.map +1 -0
- package/src/lib/server/publisher-profile.d.ts +86 -0
- package/src/lib/server/publisher-profile.js +116 -0
- package/src/lib/server/publisher-profile.js.map +1 -0
- package/src/lib/server/purchase-entitlement.d.ts +83 -0
- package/src/lib/server/purchase-entitlement.js +118 -0
- package/src/lib/server/purchase-entitlement.js.map +1 -0
- package/src/lib/server/report.d.ts +32 -0
- package/src/lib/server/report.js +101 -0
- package/src/lib/server/report.js.map +1 -0
- package/src/lib/server/reviews.d.ts +41 -0
- package/src/lib/server/reviews.js +240 -0
- package/src/lib/server/reviews.js.map +1 -0
- package/src/lib/server/update-artifact.d.ts +19 -0
- package/src/lib/server/update-artifact.js +569 -0
- package/src/lib/server/update-artifact.js.map +1 -0
- package/src/lib/server/verification-request.d.ts +31 -0
- package/src/lib/server/verification-request.js +173 -0
- package/src/lib/server/verification-request.js.map +1 -0
- package/src/lib/server/version-stats.d.ts +202 -0
- package/src/lib/server/version-stats.js +305 -0
- package/src/lib/server/version-stats.js.map +1 -0
- package/src/lib/server.d.ts +30 -0
- package/src/lib/server.js +95 -0
- package/src/lib/server.js.map +1 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
/**
|
|
18
|
+
* Per-version install counters (AGL-1036).
|
|
19
|
+
*
|
|
20
|
+
* The listing already carries `installCount` (cumulative) and `activeInstalls`
|
|
21
|
+
* (live pins), but neither answers the question a publisher asks before
|
|
22
|
+
* changing anything: **who is still on the old version?** A listing-level
|
|
23
|
+
* number cannot, and a re-pin does not even move it — the pin already existed,
|
|
24
|
+
* so nothing increments while the install quietly changes version underneath.
|
|
25
|
+
*
|
|
26
|
+
* This module is the one writer of the per-version pair, deliberately: the
|
|
27
|
+
* listing-level counters drifted apart precisely because five install routes
|
|
28
|
+
* each incremented their own, and the per-version pair has twice the surface —
|
|
29
|
+
* every version change is TWO writes, a decrement of what was left and an
|
|
30
|
+
* increment of what was taken.
|
|
31
|
+
*/ /**
|
|
32
|
+
* Where a version's counters live, which differs by artifact type.
|
|
33
|
+
*
|
|
34
|
+
* Plugins keep their versions in `pluginVersions` (publish internals, review
|
|
35
|
+
* state, bundle pointers); everything else keeps content snapshots in
|
|
36
|
+
* `versions`. Both are server-only, which is why the counters are read back
|
|
37
|
+
* out through the listing-versions route rather than by the client.
|
|
38
|
+
*/ export function versionCollectionFor(artifactType) {
|
|
39
|
+
return artifactType === 'plugin' ? 'pluginVersions' : 'versions';
|
|
40
|
+
}
|
|
41
|
+
const asCount = (value)=>{
|
|
42
|
+
const parsed = Number(value != null ? value : 0);
|
|
43
|
+
return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Reconciles one counter across the two levels that both claim to hold it.
|
|
47
|
+
*
|
|
48
|
+
* The listing total is not authoritative and neither is the per-version sum —
|
|
49
|
+
* each has a failure mode the other does not (see `reconcileInstallTallies`).
|
|
50
|
+
* So the total is the larger of the two, and the shortfall is either
|
|
51
|
+
* attributed or NAMED, never silently absorbed.
|
|
52
|
+
*/ function reconcileCounter(tracked, stored) {
|
|
53
|
+
const sum = tracked.reduce((running, value)=>running + value, 0);
|
|
54
|
+
const total = Math.max(sum, stored);
|
|
55
|
+
const values = [
|
|
56
|
+
...tracked
|
|
57
|
+
];
|
|
58
|
+
let untracked = total - sum;
|
|
59
|
+
// The ONE case where attribution is provable rather than guessed: with a
|
|
60
|
+
// single version there is nowhere else an install could be. Every other
|
|
61
|
+
// shape keeps the remainder as a remainder — a split that reads as fact and
|
|
62
|
+
// is actually a guess is the defect this function exists to remove.
|
|
63
|
+
if (untracked > 0 && values.length === 1) {
|
|
64
|
+
values[0] += untracked;
|
|
65
|
+
untracked = 0;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
values,
|
|
69
|
+
total,
|
|
70
|
+
untracked
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Makes the listing-level and per-version counters agree (AGL-1418).
|
|
75
|
+
*
|
|
76
|
+
* The marketplace keeps the same two quantities twice: once on the listing and
|
|
77
|
+
* once per version. They are written by different code under different trigger
|
|
78
|
+
* conditions, each swallowing its own failures, and nothing ever checked that
|
|
79
|
+
* they agree — so the listing page printed `7 installs · 2 active` beside
|
|
80
|
+
* `3 installs · 1 on this version` for a listing with ONE version and two live
|
|
81
|
+
* pins. Three numbers, one quantity, and a publisher with no way to tell which
|
|
82
|
+
* to believe.
|
|
83
|
+
*
|
|
84
|
+
* Neither level is trustworthy on its own:
|
|
85
|
+
*
|
|
86
|
+
* * The per-version pair only exists from AGL-1036 onwards and was never
|
|
87
|
+
* backfilled, so it under-counts every listing older than that.
|
|
88
|
+
* * The listing-level pair is not incremented at all by the copied-artifact
|
|
89
|
+
* install routes, so it under-counts every component, theme, layout, email
|
|
90
|
+
* template and dataset schema.
|
|
91
|
+
*
|
|
92
|
+
* Both fail in the same direction — downwards — which is why the total is the
|
|
93
|
+
* larger of the two rather than an average or a preference.
|
|
94
|
+
*
|
|
95
|
+
* `activeInstalls` is clamped to `installCount` at both levels. That is not a
|
|
96
|
+
* guess: every writer increments the pair together, so "more live than ever
|
|
97
|
+
* landed" is unrepresentable in a healthy database and printing it would
|
|
98
|
+
* advertise the corruption rather than the count.
|
|
99
|
+
*
|
|
100
|
+
* Pure, and deliberately: it decides what to SAY, and the caller decides
|
|
101
|
+
* whether to repair anything.
|
|
102
|
+
*
|
|
103
|
+
* ## When the pins are in hand (AGL-1419)
|
|
104
|
+
*
|
|
105
|
+
* Pass `pins` and the reasoning above stops applying to `activeInstalls`,
|
|
106
|
+
* because it only existed to pick between two unreliable readings. The pin
|
|
107
|
+
* count is the quantity itself, so it is taken exactly — including DOWNWARDS,
|
|
108
|
+
* which no amount of reconciling between two accumulators could ever do, and
|
|
109
|
+
* which is the whole reason `z6glT_UDAQ` advertised three active installs
|
|
110
|
+
* against two live pins.
|
|
111
|
+
*
|
|
112
|
+
* The clamp flips direction with it. Without pins, `activeInstalls` is capped
|
|
113
|
+
* at `installCount`, because "more live than ever landed" means one of the two
|
|
114
|
+
* is corrupt and the all-time figure is the more believable. With pins it is
|
|
115
|
+
* `installCount` that gets raised: a pin that exists now is an install that
|
|
116
|
+
* landed, so an all-time counter below the live one is simply behind.
|
|
117
|
+
*/ export function reconcileInstallTallies(versions, listing, pins) {
|
|
118
|
+
const trackedInstalls = versions.map((entry)=>asCount(entry.installCount));
|
|
119
|
+
const trackedActive = versions.map((entry)=>asCount(entry.activeInstalls));
|
|
120
|
+
const verified = pins ? asCount(pins.activeInstalls) : null;
|
|
121
|
+
const activeTotalRaw = verified != null ? verified : 0;
|
|
122
|
+
const active = verified == null ? reconcileCounter(trackedActive, asCount(listing == null ? void 0 : listing.activeInstalls)) : splitVerifiedActive(versions, trackedActive, activeTotalRaw, pins == null ? void 0 : pins.byVersion);
|
|
123
|
+
// All-time stays an accumulator even with pins in hand: an uninstall deletes
|
|
124
|
+
// its pin and leaves nothing behind, so the pins cannot say how many
|
|
125
|
+
// installs there have EVER been. They can only say the floor.
|
|
126
|
+
const installs = reconcileCounter(trackedInstalls, Math.max(asCount(listing == null ? void 0 : listing.installCount), verified != null ? verified : 0));
|
|
127
|
+
let installValues = installs.values;
|
|
128
|
+
let installTotal = installs.total;
|
|
129
|
+
let activeValues = active.values;
|
|
130
|
+
let activeTotal = verified != null ? verified : active.total;
|
|
131
|
+
if (verified == null) {
|
|
132
|
+
activeValues = active.values.map((value, index)=>{
|
|
133
|
+
var _installs_values_index;
|
|
134
|
+
return Math.min(value, (_installs_values_index = installs.values[index]) != null ? _installs_values_index : 0);
|
|
135
|
+
});
|
|
136
|
+
activeTotal = Math.min(active.total, installs.total);
|
|
137
|
+
} else {
|
|
138
|
+
installValues = installs.values.map((value, index)=>{
|
|
139
|
+
var _activeValues_index;
|
|
140
|
+
return Math.max(value, (_activeValues_index = activeValues[index]) != null ? _activeValues_index : 0);
|
|
141
|
+
});
|
|
142
|
+
installTotal = Math.max(installs.total, installValues.reduce((running, value)=>running + value, 0));
|
|
143
|
+
}
|
|
144
|
+
const activeSum = activeValues.reduce((running, value)=>running + value, 0);
|
|
145
|
+
const installSum = installValues.reduce((running, value)=>running + value, 0);
|
|
146
|
+
return {
|
|
147
|
+
versions: versions.map((entry, index)=>{
|
|
148
|
+
var _installValues_index, _activeValues_index;
|
|
149
|
+
return _extends({}, entry, {
|
|
150
|
+
installCount: (_installValues_index = installValues[index]) != null ? _installValues_index : 0,
|
|
151
|
+
activeInstalls: (_activeValues_index = activeValues[index]) != null ? _activeValues_index : 0
|
|
152
|
+
});
|
|
153
|
+
}),
|
|
154
|
+
installCount: installTotal,
|
|
155
|
+
activeInstalls: activeTotal,
|
|
156
|
+
untrackedInstallCount: Math.max(0, installTotal - installSum),
|
|
157
|
+
untrackedActiveInstalls: Math.max(0, activeTotal - activeSum)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Splits a verified pin count across the versions (AGL-1419).
|
|
162
|
+
*
|
|
163
|
+
* Three cases, in descending order of how much is known:
|
|
164
|
+
*
|
|
165
|
+
* 1. A per-version pin count was taken too — use it, and let the remainder
|
|
166
|
+
* stand for pins on versions this history does not show (deleted, or past
|
|
167
|
+
* the 20 the route reads).
|
|
168
|
+
* 2. One version — everything live is on it. Provable, not guessed.
|
|
169
|
+
* 3. Neither — keep the stored per-version actives but let no prefix of them
|
|
170
|
+
* exceed the verified total, and name whatever is left. Trimming rather
|
|
171
|
+
* than scaling because a stored count that fits is still evidence, and a
|
|
172
|
+
* fabricated fraction of one is not.
|
|
173
|
+
*/ function splitVerifiedActive(versions, tracked, total, byVersion) {
|
|
174
|
+
const lookup = byVersion instanceof Map ? byVersion : byVersion ? new Map(Object.entries(byVersion).map(([k, v])=>[
|
|
175
|
+
k,
|
|
176
|
+
asCount(v)
|
|
177
|
+
])) : null;
|
|
178
|
+
if (lookup) {
|
|
179
|
+
const values = versions.map((entry)=>asCount(lookup.get(entry.version)));
|
|
180
|
+
const sum = values.reduce((running, value)=>running + value, 0);
|
|
181
|
+
return {
|
|
182
|
+
values,
|
|
183
|
+
total,
|
|
184
|
+
untracked: Math.max(0, total - sum)
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (versions.length === 1) return {
|
|
188
|
+
values: [
|
|
189
|
+
total
|
|
190
|
+
],
|
|
191
|
+
total,
|
|
192
|
+
untracked: 0
|
|
193
|
+
};
|
|
194
|
+
let remaining = total;
|
|
195
|
+
const values = tracked.map((value)=>{
|
|
196
|
+
const taken = Math.min(value, remaining);
|
|
197
|
+
remaining -= taken;
|
|
198
|
+
return taken;
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
values,
|
|
202
|
+
total,
|
|
203
|
+
untracked: remaining
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const asId = (value)=>value == null || value === '' ? null : String(value);
|
|
207
|
+
/**
|
|
208
|
+
* Moves one install from one version's tally to another's.
|
|
209
|
+
*
|
|
210
|
+
* Rules, all of which exist because a wrong count is worse than no count:
|
|
211
|
+
*
|
|
212
|
+
* * `activeInstalls` is clamped at zero, so a repeat uninstall — which the
|
|
213
|
+
* plugin route already tolerates at listing level — cannot go negative.
|
|
214
|
+
* * `installCount` only rises, and only on the version being taken: it records
|
|
215
|
+
* that an install once landed there, which stays true after they leave.
|
|
216
|
+
* * A version whose document is gone is skipped rather than created. Writing
|
|
217
|
+
* `increment()` blind would resurrect a deleted version as a counters-only
|
|
218
|
+
* stub, and in `pluginVersions` that stub would surface through
|
|
219
|
+
* `newestApprovedVersion` as a version with no publish date and no review.
|
|
220
|
+
* * `from === to` is a no-op. Re-installing the same version is not a new
|
|
221
|
+
* install of it, and counting it would inflate every re-install.
|
|
222
|
+
* * **A move CONSERVES (AGL-1420).** When a `from` version is named, the
|
|
223
|
+
* arrival on `to` is recorded only if the departure from `from` actually
|
|
224
|
+
* landed. See below — this is the rule that stops a restored install
|
|
225
|
+
* minting a real one.
|
|
226
|
+
*
|
|
227
|
+
* Never on the critical path: a counter that cannot be written must not fail
|
|
228
|
+
* an install the user asked for.
|
|
229
|
+
*
|
|
230
|
+
* ## Why a move has to conserve
|
|
231
|
+
*
|
|
232
|
+
* The two halves used to be independent writes, either of which could
|
|
233
|
+
* silently no-op while the other landed — and both no-op paths are on the
|
|
234
|
+
* `from` half, so the error was one-directional: **net +1, every time.**
|
|
235
|
+
*
|
|
236
|
+
* * `if (!snapshot.exists) return` skipped a `from` version whose document
|
|
237
|
+
* had been deleted, and the `to` increment still landed.
|
|
238
|
+
* * `Math.max(0, active - 1)` swallowed a decrement against a version whose
|
|
239
|
+
* tally was already zero, and the `to` increment still landed.
|
|
240
|
+
*
|
|
241
|
+
* A `from` that cannot be decremented is not a bookkeeping inconvenience; it
|
|
242
|
+
* is evidence that **this copy was never in the tracked population**. The
|
|
243
|
+
* case that produces it in the wild is a site restore: `IMPORTABLE_FIELDS`
|
|
244
|
+
* deliberately carries `marketplace` / `installedFrom` / `source` through
|
|
245
|
+
* `POST /api/hosts/import` (site-export-round-trip.spec.ts asserts it, and it
|
|
246
|
+
* is what keeps update-available detection working on a legitimate backup),
|
|
247
|
+
* so a restored component or dataset arrives fully stamped with provenance
|
|
248
|
+
* having never run an install route. No `installCount` increment, no pin,
|
|
249
|
+
* nothing counted. The first time the publisher ships an update and someone
|
|
250
|
+
* presses Update, `update-artifact` reads that stamp and calls this function
|
|
251
|
+
* with a `from` nothing ever credited.
|
|
252
|
+
*
|
|
253
|
+
* That phantom then propagates rather than staying local: `reconcileCounter`
|
|
254
|
+
* takes `max(versionSum, stored)` — because every accumulator in this system
|
|
255
|
+
* fails DOWNWARD and the larger is therefore the better estimate — so an
|
|
256
|
+
* inflated per-version tally raises the listing total the page prints to
|
|
257
|
+
* buyers. Nothing can pull it back down again except a pin count, and
|
|
258
|
+
* `verifiedLivePins` returns `null` for everything that is not a plugin,
|
|
259
|
+
* which is exactly the set the import can restore. Over-counts are permanent;
|
|
260
|
+
* under-counts are representable (`untracked`) and recoverable.
|
|
261
|
+
*
|
|
262
|
+
* So the bias goes the way the rest of the arithmetic already goes. The cost
|
|
263
|
+
* is a real install whose `from` version document the publisher has since
|
|
264
|
+
* deleted: its arrival is not credited to `to`. That install is still counted
|
|
265
|
+
* at listing level, so `max(versionSum, stored)` keeps the total right and
|
|
266
|
+
* the per-version breakdown reports it as `untracked` — the shape this module
|
|
267
|
+
* already has for "we know it happened, we cannot say where".
|
|
268
|
+
*
|
|
269
|
+
* A first install (`from` null) is unaffected: there is nothing to conserve,
|
|
270
|
+
* and `to` is credited exactly as before.
|
|
271
|
+
*/ export async function recordVersionMove(input) {
|
|
272
|
+
const from = asId(input.from);
|
|
273
|
+
const to = asId(input.to);
|
|
274
|
+
if (from === to) return;
|
|
275
|
+
const collection = input.listingRef.collection(versionCollectionFor(input.artifactType));
|
|
276
|
+
/** Resolves true only if the counter was actually moved. */ const bump = async (versionId, activeDelta)=>{
|
|
277
|
+
const ref = collection.doc(versionId);
|
|
278
|
+
return await input.firestore.runTransaction(async (tx)=>{
|
|
279
|
+
var _snapshot_get, _snapshot_get1;
|
|
280
|
+
const snapshot = await tx.get(ref);
|
|
281
|
+
// A version whose document is gone is skipped rather than created:
|
|
282
|
+
// writing blind would resurrect it as a counters-only stub.
|
|
283
|
+
if (!snapshot.exists) return false;
|
|
284
|
+
const active = Number((_snapshot_get = snapshot.get('activeInstalls')) != null ? _snapshot_get : 0);
|
|
285
|
+
// Nothing to take off this version. Clamping and reporting success
|
|
286
|
+
// is what let a phantom departure fund a real arrival.
|
|
287
|
+
if (activeDelta < 0 && active <= 0) return false;
|
|
288
|
+
tx.update(ref, _extends({
|
|
289
|
+
activeInstalls: Math.max(0, active + activeDelta)
|
|
290
|
+
}, activeDelta > 0 ? {
|
|
291
|
+
installCount: Number((_snapshot_get1 = snapshot.get('installCount')) != null ? _snapshot_get1 : 0) + 1
|
|
292
|
+
} : {}));
|
|
293
|
+
return true;
|
|
294
|
+
}).catch(()=>false);
|
|
295
|
+
};
|
|
296
|
+
if (from) {
|
|
297
|
+
const departed = await bump(from, -1);
|
|
298
|
+
// The move did not come from where it claims to have come from, so there
|
|
299
|
+
// is no install here to move. Crediting `to` would invent one.
|
|
300
|
+
if (!departed) return;
|
|
301
|
+
}
|
|
302
|
+
if (to) await bump(to, 1);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
//# sourceMappingURL=version-stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/marketplace/src/lib/server/version-stats.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { MarketplaceArtifactType } from '../model/marketplace'\n\n/**\n * Per-version install counters (AGL-1036).\n *\n * The listing already carries `installCount` (cumulative) and `activeInstalls`\n * (live pins), but neither answers the question a publisher asks before\n * changing anything: **who is still on the old version?** A listing-level\n * number cannot, and a re-pin does not even move it — the pin already existed,\n * so nothing increments while the install quietly changes version underneath.\n *\n * This module is the one writer of the per-version pair, deliberately: the\n * listing-level counters drifted apart precisely because five install routes\n * each incremented their own, and the per-version pair has twice the surface —\n * every version change is TWO writes, a decrement of what was left and an\n * increment of what was taken.\n */\n\n/**\n * Where a version's counters live, which differs by artifact type.\n *\n * Plugins keep their versions in `pluginVersions` (publish internals, review\n * state, bundle pointers); everything else keeps content snapshots in\n * `versions`. Both are server-only, which is why the counters are read back\n * out through the listing-versions route rather than by the client.\n */\nexport function versionCollectionFor(\n artifactType: MarketplaceArtifactType | string | null | undefined,\n): 'pluginVersions' | 'versions' {\n return artifactType === 'plugin' ? 'pluginVersions' : 'versions'\n}\n\n/** One version's pair of counters, as the version doc stores them. */\nexport interface VersionInstallTally {\n version: string\n installCount: number\n activeInstalls: number\n}\n\n/** The listing-level pair, which counts the same two things (AGL-1418). */\nexport interface ListingInstallTally {\n installCount?: number | null\n activeInstalls?: number | null\n}\n\n/**\n * The same quantity counted from the pins, which are ground truth (AGL-1419).\n *\n * Both stored levels are accumulators, and an accumulator cannot come back\n * down: nothing decrements when a tenant erase sweeps `installs` or the\n * console deletes a host pin client-side. The pins can, because they ARE the\n * installs — so when this is present it does not get reconciled against\n * anything, it simply wins.\n */\nexport interface LivePinTally {\n /** Live pins for the listing. A verified `0` is a count, not a gap. */\n activeInstalls: number\n /** Live pins per version id, when the split was derived too. */\n byVersion?: Map<string, number> | Record<string, number> | null\n}\n\nexport interface ReconciledInstallTallies {\n /** Per-version counters, reconciled against the listing totals. */\n versions: VersionInstallTally[]\n /** The totals the page should print, and that the versions sum to. */\n installCount: number\n activeInstalls: number\n /**\n * Installs the totals include but no version claims. Non-zero means the\n * per-version split is INCOMPLETE, and a caller must say so rather than\n * print a breakdown that does not add up.\n */\n untrackedInstallCount: number\n untrackedActiveInstalls: number\n}\n\nconst asCount = (value: unknown): number => {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : 0\n}\n\n/**\n * Reconciles one counter across the two levels that both claim to hold it.\n *\n * The listing total is not authoritative and neither is the per-version sum —\n * each has a failure mode the other does not (see `reconcileInstallTallies`).\n * So the total is the larger of the two, and the shortfall is either\n * attributed or NAMED, never silently absorbed.\n */\nfunction reconcileCounter(\n tracked: number[],\n stored: number,\n): { values: number[]; total: number; untracked: number } {\n const sum = tracked.reduce((running, value) => running + value, 0)\n const total = Math.max(sum, stored)\n const values = [...tracked]\n let untracked = total - sum\n // The ONE case where attribution is provable rather than guessed: with a\n // single version there is nowhere else an install could be. Every other\n // shape keeps the remainder as a remainder — a split that reads as fact and\n // is actually a guess is the defect this function exists to remove.\n if (untracked > 0 && values.length === 1) {\n values[0] += untracked\n untracked = 0\n }\n return { values, total, untracked }\n}\n\n/**\n * Makes the listing-level and per-version counters agree (AGL-1418).\n *\n * The marketplace keeps the same two quantities twice: once on the listing and\n * once per version. They are written by different code under different trigger\n * conditions, each swallowing its own failures, and nothing ever checked that\n * they agree — so the listing page printed `7 installs · 2 active` beside\n * `3 installs · 1 on this version` for a listing with ONE version and two live\n * pins. Three numbers, one quantity, and a publisher with no way to tell which\n * to believe.\n *\n * Neither level is trustworthy on its own:\n *\n * * The per-version pair only exists from AGL-1036 onwards and was never\n * backfilled, so it under-counts every listing older than that.\n * * The listing-level pair is not incremented at all by the copied-artifact\n * install routes, so it under-counts every component, theme, layout, email\n * template and dataset schema.\n *\n * Both fail in the same direction — downwards — which is why the total is the\n * larger of the two rather than an average or a preference.\n *\n * `activeInstalls` is clamped to `installCount` at both levels. That is not a\n * guess: every writer increments the pair together, so \"more live than ever\n * landed\" is unrepresentable in a healthy database and printing it would\n * advertise the corruption rather than the count.\n *\n * Pure, and deliberately: it decides what to SAY, and the caller decides\n * whether to repair anything.\n *\n * ## When the pins are in hand (AGL-1419)\n *\n * Pass `pins` and the reasoning above stops applying to `activeInstalls`,\n * because it only existed to pick between two unreliable readings. The pin\n * count is the quantity itself, so it is taken exactly — including DOWNWARDS,\n * which no amount of reconciling between two accumulators could ever do, and\n * which is the whole reason `z6glT_UDAQ` advertised three active installs\n * against two live pins.\n *\n * The clamp flips direction with it. Without pins, `activeInstalls` is capped\n * at `installCount`, because \"more live than ever landed\" means one of the two\n * is corrupt and the all-time figure is the more believable. With pins it is\n * `installCount` that gets raised: a pin that exists now is an install that\n * landed, so an all-time counter below the live one is simply behind.\n */\nexport function reconcileInstallTallies(\n versions: VersionInstallTally[],\n listing: ListingInstallTally,\n pins?: LivePinTally | null,\n): ReconciledInstallTallies {\n const trackedInstalls = versions.map((entry) => asCount(entry.installCount))\n const trackedActive = versions.map((entry) => asCount(entry.activeInstalls))\n\n const verified = pins ? asCount(pins.activeInstalls) : null\n const activeTotalRaw = verified ?? 0\n const active =\n verified == null\n ? reconcileCounter(trackedActive, asCount(listing?.activeInstalls))\n : splitVerifiedActive(versions, trackedActive, activeTotalRaw, pins?.byVersion)\n\n // All-time stays an accumulator even with pins in hand: an uninstall deletes\n // its pin and leaves nothing behind, so the pins cannot say how many\n // installs there have EVER been. They can only say the floor.\n const installs = reconcileCounter(\n trackedInstalls,\n Math.max(asCount(listing?.installCount), verified ?? 0),\n )\n\n let installValues = installs.values\n let installTotal = installs.total\n let activeValues = active.values\n let activeTotal = verified ?? active.total\n if (verified == null) {\n activeValues = active.values.map((value, index) =>\n Math.min(value, installs.values[index] ?? 0),\n )\n activeTotal = Math.min(active.total, installs.total)\n } else {\n installValues = installs.values.map((value, index) =>\n Math.max(value, activeValues[index] ?? 0),\n )\n installTotal = Math.max(\n installs.total,\n installValues.reduce((running, value) => running + value, 0),\n )\n }\n const activeSum = activeValues.reduce((running, value) => running + value, 0)\n const installSum = installValues.reduce((running, value) => running + value, 0)\n return {\n versions: versions.map((entry, index) => ({\n ...entry,\n installCount: installValues[index] ?? 0,\n activeInstalls: activeValues[index] ?? 0,\n })),\n installCount: installTotal,\n activeInstalls: activeTotal,\n untrackedInstallCount: Math.max(0, installTotal - installSum),\n untrackedActiveInstalls: Math.max(0, activeTotal - activeSum),\n }\n}\n\n/**\n * Splits a verified pin count across the versions (AGL-1419).\n *\n * Three cases, in descending order of how much is known:\n *\n * 1. A per-version pin count was taken too — use it, and let the remainder\n * stand for pins on versions this history does not show (deleted, or past\n * the 20 the route reads).\n * 2. One version — everything live is on it. Provable, not guessed.\n * 3. Neither — keep the stored per-version actives but let no prefix of them\n * exceed the verified total, and name whatever is left. Trimming rather\n * than scaling because a stored count that fits is still evidence, and a\n * fabricated fraction of one is not.\n */\nfunction splitVerifiedActive(\n versions: VersionInstallTally[],\n tracked: number[],\n total: number,\n byVersion: LivePinTally['byVersion'],\n): { values: number[]; total: number; untracked: number } {\n const lookup =\n byVersion instanceof Map\n ? byVersion\n : byVersion\n ? new Map(Object.entries(byVersion).map(([k, v]) => [k, asCount(v)]))\n : null\n if (lookup) {\n const values = versions.map((entry) => asCount(lookup.get(entry.version)))\n const sum = values.reduce((running, value) => running + value, 0)\n return { values, total, untracked: Math.max(0, total - sum) }\n }\n if (versions.length === 1) return { values: [total], total, untracked: 0 }\n let remaining = total\n const values = tracked.map((value) => {\n const taken = Math.min(value, remaining)\n remaining -= taken\n return taken\n })\n return { values, total, untracked: remaining }\n}\n\nexport interface VersionMoveInput {\n firestore: FirebaseFirestore.Firestore\n listingRef: FirebaseFirestore.DocumentReference\n artifactType: MarketplaceArtifactType | string | null | undefined\n /** The version this install was on, if any. Null for a first install. */\n from?: string | number | null\n /** The version it is on now. Null for an uninstall. */\n to?: string | number | null\n}\n\nconst asId = (value: string | number | null | undefined): string | null =>\n value == null || value === '' ? null : String(value)\n\n/**\n * Moves one install from one version's tally to another's.\n *\n * Rules, all of which exist because a wrong count is worse than no count:\n *\n * * `activeInstalls` is clamped at zero, so a repeat uninstall — which the\n * plugin route already tolerates at listing level — cannot go negative.\n * * `installCount` only rises, and only on the version being taken: it records\n * that an install once landed there, which stays true after they leave.\n * * A version whose document is gone is skipped rather than created. Writing\n * `increment()` blind would resurrect a deleted version as a counters-only\n * stub, and in `pluginVersions` that stub would surface through\n * `newestApprovedVersion` as a version with no publish date and no review.\n * * `from === to` is a no-op. Re-installing the same version is not a new\n * install of it, and counting it would inflate every re-install.\n * * **A move CONSERVES (AGL-1420).** When a `from` version is named, the\n * arrival on `to` is recorded only if the departure from `from` actually\n * landed. See below — this is the rule that stops a restored install\n * minting a real one.\n *\n * Never on the critical path: a counter that cannot be written must not fail\n * an install the user asked for.\n *\n * ## Why a move has to conserve\n *\n * The two halves used to be independent writes, either of which could\n * silently no-op while the other landed — and both no-op paths are on the\n * `from` half, so the error was one-directional: **net +1, every time.**\n *\n * * `if (!snapshot.exists) return` skipped a `from` version whose document\n * had been deleted, and the `to` increment still landed.\n * * `Math.max(0, active - 1)` swallowed a decrement against a version whose\n * tally was already zero, and the `to` increment still landed.\n *\n * A `from` that cannot be decremented is not a bookkeeping inconvenience; it\n * is evidence that **this copy was never in the tracked population**. The\n * case that produces it in the wild is a site restore: `IMPORTABLE_FIELDS`\n * deliberately carries `marketplace` / `installedFrom` / `source` through\n * `POST /api/hosts/import` (site-export-round-trip.spec.ts asserts it, and it\n * is what keeps update-available detection working on a legitimate backup),\n * so a restored component or dataset arrives fully stamped with provenance\n * having never run an install route. No `installCount` increment, no pin,\n * nothing counted. The first time the publisher ships an update and someone\n * presses Update, `update-artifact` reads that stamp and calls this function\n * with a `from` nothing ever credited.\n *\n * That phantom then propagates rather than staying local: `reconcileCounter`\n * takes `max(versionSum, stored)` — because every accumulator in this system\n * fails DOWNWARD and the larger is therefore the better estimate — so an\n * inflated per-version tally raises the listing total the page prints to\n * buyers. Nothing can pull it back down again except a pin count, and\n * `verifiedLivePins` returns `null` for everything that is not a plugin,\n * which is exactly the set the import can restore. Over-counts are permanent;\n * under-counts are representable (`untracked`) and recoverable.\n *\n * So the bias goes the way the rest of the arithmetic already goes. The cost\n * is a real install whose `from` version document the publisher has since\n * deleted: its arrival is not credited to `to`. That install is still counted\n * at listing level, so `max(versionSum, stored)` keeps the total right and\n * the per-version breakdown reports it as `untracked` — the shape this module\n * already has for \"we know it happened, we cannot say where\".\n *\n * A first install (`from` null) is unaffected: there is nothing to conserve,\n * and `to` is credited exactly as before.\n */\nexport async function recordVersionMove(input: VersionMoveInput): Promise<void> {\n const from = asId(input.from)\n const to = asId(input.to)\n if (from === to) return\n const collection = input.listingRef.collection(\n versionCollectionFor(input.artifactType),\n )\n /** Resolves true only if the counter was actually moved. */\n const bump = async (\n versionId: string,\n activeDelta: 1 | -1,\n ): Promise<boolean> => {\n const ref = collection.doc(versionId)\n return await input.firestore\n .runTransaction(async (tx) => {\n const snapshot = await tx.get(ref)\n // A version whose document is gone is skipped rather than created:\n // writing blind would resurrect it as a counters-only stub.\n if (!snapshot.exists) return false\n const active = Number(snapshot.get('activeInstalls') ?? 0)\n // Nothing to take off this version. Clamping and reporting success\n // is what let a phantom departure fund a real arrival.\n if (activeDelta < 0 && active <= 0) return false\n tx.update(ref, {\n activeInstalls: Math.max(0, active + activeDelta),\n ...(activeDelta > 0\n ? { installCount: Number(snapshot.get('installCount') ?? 0) + 1 }\n : {}),\n })\n return true\n })\n .catch(() => false)\n }\n if (from) {\n const departed = await bump(from, -1)\n // The move did not come from where it claims to have come from, so there\n // is no install here to move. Crediting `to` would invent one.\n if (!departed) return\n }\n if (to) await bump(to, 1)\n}\n"],"names":["versionCollectionFor","artifactType","asCount","value","parsed","Number","isFinite","Math","floor","reconcileCounter","tracked","stored","sum","reduce","running","total","max","values","untracked","length","reconcileInstallTallies","versions","listing","pins","trackedInstalls","map","entry","installCount","trackedActive","activeInstalls","verified","activeTotalRaw","active","splitVerifiedActive","byVersion","installs","installValues","installTotal","activeValues","activeTotal","index","min","activeSum","installSum","untrackedInstallCount","untrackedActiveInstalls","lookup","Map","Object","entries","k","v","get","version","remaining","taken","asId","String","recordVersionMove","input","from","to","collection","listingRef","bump","versionId","activeDelta","ref","doc","firestore","runTransaction","tx","snapshot","exists","update","catch","departed"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Registers the marketplace plugin's console-side API routes (AGL-396):
|
|
19
|
+
* marketplace publish/install of templates and plugins, and the Stripe
|
|
20
|
+
* Connect + checkout flows for paid listings.
|
|
21
|
+
*
|
|
22
|
+
* Upload-bodied routes register here too. The note that used to sit here —
|
|
23
|
+
* that they had to stay as named console routes because the dispatcher
|
|
24
|
+
* "can't grant per-route bodyParser limits" — was a Pages Router leftover:
|
|
25
|
+
* `runLegacyHandler` reads the request through the same
|
|
26
|
+
* `pluginRequestFromWeb` a named App Router route would, and imposes no
|
|
27
|
+
* limit of its own. publish-plugin's 8 MB bundle has been going through the
|
|
28
|
+
* dispatcher the whole time.
|
|
29
|
+
*/
|
|
30
|
+
export declare function registerMarketplaceConsoleApi(): void;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { registerBillingWebhookHandler, registerPluginApiRoute, registerCustomFieldType } from "@aglyn/aglyn/server";
|
|
17
|
+
import { marketplaceBillingWebhookHandler } from "./server/billing-webhook.js";
|
|
18
|
+
import { publishPluginHandler } from "./server/publish-plugin.js";
|
|
19
|
+
import { verificationRequestHandler } from "./server/verification-request.js";
|
|
20
|
+
import { checkoutHandler } from "./server/checkout.js";
|
|
21
|
+
import { connectHandler } from "./server/connect.js";
|
|
22
|
+
import { installHandler } from "./server/install.js";
|
|
23
|
+
import { installPluginHandler } from "./server/install-plugin.js";
|
|
24
|
+
import { listingVersionsHandler } from "./server/listing-versions.js";
|
|
25
|
+
import { RATING_FIELD } from "./model/rating-field.js";
|
|
26
|
+
import { installDatasetSchemaHandler } from "./server/install-dataset-schema.js";
|
|
27
|
+
import { installEmailStarterHandler } from "./server/install-email-starter.js";
|
|
28
|
+
import { installEmailTemplateHandler } from "./server/install-email-template.js";
|
|
29
|
+
import { installLayoutHandler } from "./server/install-layout.js";
|
|
30
|
+
import { installTemplateHandler } from "./server/install-template.js";
|
|
31
|
+
import { installThemeHandler } from "./server/install-theme.js";
|
|
32
|
+
import { previewImageHandler } from "./server/preview-image.js";
|
|
33
|
+
import { publishHandler } from "./server/publish.js";
|
|
34
|
+
import { reportHandler } from "./server/report.js";
|
|
35
|
+
import { reviewsHandler } from "./server/reviews.js";
|
|
36
|
+
import { publisherProfileSaveHandler } from "./server/publisher-profile-save.js";
|
|
37
|
+
import { publishDatasetSchemaHandler } from "./server/publish-dataset-schema.js";
|
|
38
|
+
import { publishEmailStarterHandler } from "./server/publish-email-starter.js";
|
|
39
|
+
import { publishEmailTemplateHandler } from "./server/publish-email-template.js";
|
|
40
|
+
import { publishLayoutHandler } from "./server/publish-layout.js";
|
|
41
|
+
import { publishTemplateHandler } from "./server/publish-template.js";
|
|
42
|
+
import { publishThemeHandler } from "./server/publish-theme.js";
|
|
43
|
+
import { updateArtifactHandler } from "./server/update-artifact.js";
|
|
44
|
+
/**
|
|
45
|
+
* Registers the marketplace plugin's console-side API routes (AGL-396):
|
|
46
|
+
* marketplace publish/install of templates and plugins, and the Stripe
|
|
47
|
+
* Connect + checkout flows for paid listings.
|
|
48
|
+
*
|
|
49
|
+
* Upload-bodied routes register here too. The note that used to sit here —
|
|
50
|
+
* that they had to stay as named console routes because the dispatcher
|
|
51
|
+
* "can't grant per-route bodyParser limits" — was a Pages Router leftover:
|
|
52
|
+
* `runLegacyHandler` reads the request through the same
|
|
53
|
+
* `pluginRequestFromWeb` a named App Router route would, and imposes no
|
|
54
|
+
* limit of its own. publish-plugin's 8 MB bundle has been going through the
|
|
55
|
+
* dispatcher the whole time.
|
|
56
|
+
*/ export function registerMarketplaceConsoleApi() {
|
|
57
|
+
// Server side of the rating custom field (AGL-434): validators run
|
|
58
|
+
// on import/write paths even when no client loaded the plugin.
|
|
59
|
+
registerCustomFieldType(RATING_FIELD);
|
|
60
|
+
registerPluginApiRoute('marketplace/checkout', checkoutHandler);
|
|
61
|
+
registerPluginApiRoute('marketplace/connect', connectHandler);
|
|
62
|
+
registerPluginApiRoute('marketplace/install', installHandler);
|
|
63
|
+
registerPluginApiRoute('marketplace/install-plugin', installPluginHandler);
|
|
64
|
+
registerPluginApiRoute('marketplace/install-layout', installLayoutHandler);
|
|
65
|
+
registerPluginApiRoute('marketplace/install-template', installTemplateHandler);
|
|
66
|
+
// A theme install DOES change the running site (AGL-1020) — it is the site's
|
|
67
|
+
// appearance — so this route also owns the ways back: `revert` and `reset`.
|
|
68
|
+
registerPluginApiRoute('marketplace/install-theme', installThemeHandler);
|
|
69
|
+
registerPluginApiRoute('marketplace/install-dataset-schema', installDatasetSchemaHandler);
|
|
70
|
+
registerPluginApiRoute('marketplace/install-email-starter', installEmailStarterHandler);
|
|
71
|
+
registerPluginApiRoute('marketplace/install-email-template', installEmailTemplateHandler);
|
|
72
|
+
// Updating a copied artifact is its own route (AGL-1018), not a flag on
|
|
73
|
+
// install: install writes the publisher's version, this one reconciles it
|
|
74
|
+
// with a copy that has diverged and refuses to overwrite silently.
|
|
75
|
+
registerPluginApiRoute('marketplace/update-artifact', updateArtifactHandler);
|
|
76
|
+
registerPluginApiRoute('marketplace/listing-versions', listingVersionsHandler);
|
|
77
|
+
registerPluginApiRoute('marketplace/preview-image', previewImageHandler);
|
|
78
|
+
registerPluginApiRoute('marketplace/publish', publishHandler);
|
|
79
|
+
registerPluginApiRoute('marketplace/report', reportHandler);
|
|
80
|
+
registerPluginApiRoute('marketplace/reviews', reviewsHandler);
|
|
81
|
+
registerPluginApiRoute('marketplace/publisher-profile', publisherProfileSaveHandler);
|
|
82
|
+
registerPluginApiRoute('marketplace/verification-request', verificationRequestHandler);
|
|
83
|
+
registerPluginApiRoute('marketplace/publish-layout', publishLayoutHandler);
|
|
84
|
+
registerPluginApiRoute('marketplace/publish-template', publishTemplateHandler);
|
|
85
|
+
registerPluginApiRoute('marketplace/publish-theme', publishThemeHandler);
|
|
86
|
+
registerPluginApiRoute('marketplace/publish-dataset-schema', publishDatasetSchemaHandler);
|
|
87
|
+
registerPluginApiRoute('marketplace/publish-email-starter', publishEmailStarterHandler);
|
|
88
|
+
registerPluginApiRoute('marketplace/publish-email-template', publishEmailTemplateHandler);
|
|
89
|
+
// Relocated console routes (AGL-418): URLs preserved via the dispatcher.
|
|
90
|
+
registerPluginApiRoute('marketplace/publish-plugin', publishPluginHandler);
|
|
91
|
+
// Marketplace purchases ride the platform Stripe webhook (AGL-418).
|
|
92
|
+
registerBillingWebhookHandler(marketplaceBillingWebhookHandler);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/marketplace/src/lib/server.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n registerBillingWebhookHandler,\n registerPluginApiRoute,\n registerCustomFieldType,\n} from '@aglyn/aglyn/server'\nimport { marketplaceBillingWebhookHandler } from './server/billing-webhook'\nimport { publishPluginHandler } from './server/publish-plugin'\nimport { verificationRequestHandler } from './server/verification-request'\nimport { checkoutHandler } from './server/checkout'\nimport { connectHandler } from './server/connect'\nimport { installHandler } from './server/install'\nimport { installPluginHandler } from './server/install-plugin'\nimport { listingVersionsHandler } from './server/listing-versions'\nimport { RATING_FIELD } from './model/rating-field'\nimport { installDatasetSchemaHandler } from './server/install-dataset-schema'\nimport { installEmailStarterHandler } from './server/install-email-starter'\nimport { installEmailTemplateHandler } from './server/install-email-template'\nimport { installLayoutHandler } from './server/install-layout'\nimport { installTemplateHandler } from './server/install-template'\nimport { installThemeHandler } from './server/install-theme'\nimport { previewImageHandler } from './server/preview-image'\nimport { publishHandler } from './server/publish'\nimport { reportHandler } from './server/report'\nimport { reviewsHandler } from './server/reviews'\nimport { publisherProfileSaveHandler } from './server/publisher-profile-save'\nimport { publishDatasetSchemaHandler } from './server/publish-dataset-schema'\nimport { publishEmailStarterHandler } from './server/publish-email-starter'\nimport { publishEmailTemplateHandler } from './server/publish-email-template'\nimport { publishLayoutHandler } from './server/publish-layout'\nimport { publishTemplateHandler } from './server/publish-template'\nimport { publishThemeHandler } from './server/publish-theme'\nimport { updateArtifactHandler } from './server/update-artifact'\n\n/**\n * Registers the marketplace plugin's console-side API routes (AGL-396):\n * marketplace publish/install of templates and plugins, and the Stripe\n * Connect + checkout flows for paid listings.\n *\n * Upload-bodied routes register here too. The note that used to sit here —\n * that they had to stay as named console routes because the dispatcher\n * \"can't grant per-route bodyParser limits\" — was a Pages Router leftover:\n * `runLegacyHandler` reads the request through the same\n * `pluginRequestFromWeb` a named App Router route would, and imposes no\n * limit of its own. publish-plugin's 8 MB bundle has been going through the\n * dispatcher the whole time.\n */\nexport function registerMarketplaceConsoleApi(): void {\n // Server side of the rating custom field (AGL-434): validators run\n // on import/write paths even when no client loaded the plugin.\n registerCustomFieldType(RATING_FIELD)\n registerPluginApiRoute('marketplace/checkout', checkoutHandler)\n registerPluginApiRoute('marketplace/connect', connectHandler)\n registerPluginApiRoute('marketplace/install', installHandler)\n registerPluginApiRoute('marketplace/install-plugin', installPluginHandler)\n registerPluginApiRoute('marketplace/install-layout', installLayoutHandler)\n registerPluginApiRoute('marketplace/install-template', installTemplateHandler)\n // A theme install DOES change the running site (AGL-1020) — it is the site's\n // appearance — so this route also owns the ways back: `revert` and `reset`.\n registerPluginApiRoute('marketplace/install-theme', installThemeHandler)\n registerPluginApiRoute(\n 'marketplace/install-dataset-schema',\n installDatasetSchemaHandler,\n )\n registerPluginApiRoute(\n 'marketplace/install-email-starter',\n installEmailStarterHandler,\n )\n registerPluginApiRoute(\n 'marketplace/install-email-template',\n installEmailTemplateHandler,\n )\n // Updating a copied artifact is its own route (AGL-1018), not a flag on\n // install: install writes the publisher's version, this one reconciles it\n // with a copy that has diverged and refuses to overwrite silently.\n registerPluginApiRoute('marketplace/update-artifact', updateArtifactHandler)\n registerPluginApiRoute('marketplace/listing-versions', listingVersionsHandler)\n registerPluginApiRoute('marketplace/preview-image', previewImageHandler)\n registerPluginApiRoute('marketplace/publish', publishHandler)\n registerPluginApiRoute('marketplace/report', reportHandler)\n registerPluginApiRoute('marketplace/reviews', reviewsHandler)\n registerPluginApiRoute(\n 'marketplace/publisher-profile',\n publisherProfileSaveHandler,\n )\n registerPluginApiRoute(\n 'marketplace/verification-request',\n verificationRequestHandler,\n )\n registerPluginApiRoute('marketplace/publish-layout', publishLayoutHandler)\n registerPluginApiRoute('marketplace/publish-template', publishTemplateHandler)\n registerPluginApiRoute('marketplace/publish-theme', publishThemeHandler)\n registerPluginApiRoute(\n 'marketplace/publish-dataset-schema',\n publishDatasetSchemaHandler,\n )\n registerPluginApiRoute(\n 'marketplace/publish-email-starter',\n publishEmailStarterHandler,\n )\n registerPluginApiRoute(\n 'marketplace/publish-email-template',\n publishEmailTemplateHandler,\n )\n // Relocated console routes (AGL-418): URLs preserved via the dispatcher.\n registerPluginApiRoute('marketplace/publish-plugin', publishPluginHandler)\n // Marketplace purchases ride the platform Stripe webhook (AGL-418).\n registerBillingWebhookHandler(marketplaceBillingWebhookHandler)\n}\n"],"names":["registerBillingWebhookHandler","registerPluginApiRoute","registerCustomFieldType","marketplaceBillingWebhookHandler","publishPluginHandler","verificationRequestHandler","checkoutHandler","connectHandler","installHandler","installPluginHandler","listingVersionsHandler","RATING_FIELD","installDatasetSchemaHandler","installEmailStarterHandler","installEmailTemplateHandler","installLayoutHandler","installTemplateHandler","installThemeHandler","previewImageHandler","publishHandler","reportHandler","reviewsHandler","publisherProfileSaveHandler","publishDatasetSchemaHandler","publishEmailStarterHandler","publishEmailTemplateHandler","publishLayoutHandler","publishTemplateHandler","publishThemeHandler","updateArtifactHandler","registerMarketplaceConsoleApi"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SACEA,6BAA6B,EAC7BC,sBAAsB,EACtBC,uBAAuB,QAClB,sBAAqB;AAC5B,SAASC,gCAAgC,QAAQ,8BAA0B;AAC3E,SAASC,oBAAoB,QAAQ,6BAAyB;AAC9D,SAASC,0BAA0B,QAAQ,mCAA+B;AAC1E,SAASC,eAAe,QAAQ,uBAAmB;AACnD,SAASC,cAAc,QAAQ,sBAAkB;AACjD,SAASC,cAAc,QAAQ,sBAAkB;AACjD,SAASC,oBAAoB,QAAQ,6BAAyB;AAC9D,SAASC,sBAAsB,QAAQ,+BAA2B;AAClE,SAASC,YAAY,QAAQ,0BAAsB;AACnD,SAASC,2BAA2B,QAAQ,qCAAiC;AAC7E,SAASC,0BAA0B,QAAQ,oCAAgC;AAC3E,SAASC,2BAA2B,QAAQ,qCAAiC;AAC7E,SAASC,oBAAoB,QAAQ,6BAAyB;AAC9D,SAASC,sBAAsB,QAAQ,+BAA2B;AAClE,SAASC,mBAAmB,QAAQ,4BAAwB;AAC5D,SAASC,mBAAmB,QAAQ,4BAAwB;AAC5D,SAASC,cAAc,QAAQ,sBAAkB;AACjD,SAASC,aAAa,QAAQ,qBAAiB;AAC/C,SAASC,cAAc,QAAQ,sBAAkB;AACjD,SAASC,2BAA2B,QAAQ,qCAAiC;AAC7E,SAASC,2BAA2B,QAAQ,qCAAiC;AAC7E,SAASC,0BAA0B,QAAQ,oCAAgC;AAC3E,SAASC,2BAA2B,QAAQ,qCAAiC;AAC7E,SAASC,oBAAoB,QAAQ,6BAAyB;AAC9D,SAASC,sBAAsB,QAAQ,+BAA2B;AAClE,SAASC,mBAAmB,QAAQ,4BAAwB;AAC5D,SAASC,qBAAqB,QAAQ,8BAA0B;AAEhE;;;;;;;;;;;;CAYC,GACD,OAAO,SAASC;IACd,mEAAmE;IACnE,+DAA+D;IAC/D5B,wBAAwBS;IACxBV,uBAAuB,wBAAwBK;IAC/CL,uBAAuB,uBAAuBM;IAC9CN,uBAAuB,uBAAuBO;IAC9CP,uBAAuB,8BAA8BQ;IACrDR,uBAAuB,8BAA8Bc;IACrDd,uBAAuB,gCAAgCe;IACvD,6EAA6E;IAC7E,4EAA4E;IAC5Ef,uBAAuB,6BAA6BgB;IACpDhB,uBACE,sCACAW;IAEFX,uBACE,qCACAY;IAEFZ,uBACE,sCACAa;IAEF,wEAAwE;IACxE,0EAA0E;IAC1E,mEAAmE;IACnEb,uBAAuB,+BAA+B4B;IACtD5B,uBAAuB,gCAAgCS;IACvDT,uBAAuB,6BAA6BiB;IACpDjB,uBAAuB,uBAAuBkB;IAC9ClB,uBAAuB,sBAAsBmB;IAC7CnB,uBAAuB,uBAAuBoB;IAC9CpB,uBACE,iCACAqB;IAEFrB,uBACE,oCACAI;IAEFJ,uBAAuB,8BAA8ByB;IACrDzB,uBAAuB,gCAAgC0B;IACvD1B,uBAAuB,6BAA6B2B;IACpD3B,uBACE,sCACAsB;IAEFtB,uBACE,qCACAuB;IAEFvB,uBACE,sCACAwB;IAEF,yEAAyE;IACzExB,uBAAuB,8BAA8BG;IACrD,oEAAoE;IACpEJ,8BAA8BG;AAChC"}
|