@90soft/parse-server-kit 2.11.0 → 2.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,131 @@ All notable changes to `@90soft/parse-server-kit`.
8
8
 
9
9
  ---
10
10
 
11
+ ## [2.13.0]
12
+
13
+ ### Changed — BREAKING (file access rules, added in 2.12.0)
14
+
15
+ - **The governed prefix is `acl/`, not `private/`, and the API is renamed to
16
+ match.**
17
+
18
+ `private` was wrong in the most confusing way possible. The commonest case is
19
+ a **publicly readable** row — a login background, say — whose file is then
20
+ correctly served to everybody, from a url that said `private`. The prefix
21
+ never meant "secret". It means **the owning row decides**, and a public row
22
+ decides "everyone".
23
+
24
+ Renamed now because the prefix is baked into stored filenames: changing it
25
+ later orphans every file already written under the old one. 2.12.0 was hours
26
+ old and had one consumer.
27
+
28
+ | 2.12.0 | 2.13.0 |
29
+ |---|---|
30
+ | `private/<Class>/<objectId>/` | `acl/<Class>/<objectId>/` |
31
+ | `PRIVATE_PREFIX` | `ACL_PREFIX` |
32
+ | `isPrivatePath` | `isAclGoverned` |
33
+ | `privateOwnerOf` | `aclOwnerOf` |
34
+ | `privateDirectoryFor` | `aclDirectoryFor` |
35
+ | `attachPrivateFile` | `attachAclFile` |
36
+ | `privateFileRule` | `aclFileRule` |
37
+ | `PrivateFileOwner` | `AclFileOwner` |
38
+ | `PrivateFileRuleOptions` | `AclFileRuleOptions` |
39
+
40
+ **Migrating:** rename the imports, then **re-upload** anything already stored
41
+ under `private/`. Those files keep working — but as ordinary public Parse
42
+ files, because the guard no longer recognises their path. There is no
43
+ rewriting a stored filename.
44
+
45
+ The `Cache-Control: private, no-store` default is unchanged: that `private`
46
+ is the HTTP cache directive and has nothing to do with the prefix.
47
+
48
+ Version 2.13.0 rather than 3.0.0 despite being breaking: 3.0.0 is reserved
49
+ for the public `parse-server-kit` package (see the note at the top of this
50
+ file).
51
+
52
+ ---
53
+
54
+ ## [2.12.0]
55
+
56
+ ### Added
57
+
58
+ - **File access rules — a file's PATH can now carry a permission.**
59
+
60
+ Parse has no per-file permission and cannot have one: the file router is
61
+ handed a name, and a name does not know which row references it. So a file
62
+ url is a bearer capability — anyone holding the string gets the bytes, with
63
+ no session, no ACL check and no expiry. Fine for published pictures; not fine
64
+ for a document, where a url that leaks once works forever, for anyone.
65
+
66
+ Parse does give one hook, `beforeFind` on `Parse.File`. This release owns it
67
+ and makes it usable:
68
+
69
+ ```ts
70
+ import {registerFileAccessRule, privateFileRule, attachPrivateFile} from '@90soft/parse-server-kit';
71
+
72
+ registerFileAccessRule(privateFileRule()); // once, in cloud code
73
+ await attachPrivateFile(row, 'document', upload); // stores under private/<Class>/<objectId>/
74
+ ```
75
+
76
+ A file stored under `private/<ClassName>/<objectId>/` is then served only to
77
+ a caller who can read THAT row, loaded **as the caller** so Parse evaluates
78
+ the row's ACL and the class's CLP exactly as it would for a query. The
79
+ permission is not a copy of the row's ACL; it IS the row's ACL — nothing to
80
+ stamp, nothing to sync, no second source of truth.
81
+
82
+ Why not signed urls: a signed url is the same bearer token on a timer. It
83
+ leaks the same ways, forces a refresh dance and cache invalidation on every
84
+ client, and cannot express "this account was banned thirty seconds ago". The
85
+ rule re-checks every request.
86
+
87
+ **Nothing is registered until you add a rule**, so upgrading changes no
88
+ behaviour in a project that does not opt in, and a project that already
89
+ registers its own file trigger is not quietly overridden.
90
+
91
+ **Five traps this exists to absorb**, each of which cost real debugging:
92
+
93
+ 1. **There is exactly ONE slot.** File triggers are keyed on the pseudo-class
94
+ `@File`, so a second `beforeFind(Parse.File)` anywhere REPLACES the first
95
+ — Parse logs "Only the last one will be used" and keeps serving. Two
96
+ features each registering a rule would silently disable one. Hence a
97
+ registry: the kit holds the slot, rules compose in registration order.
98
+ 2. **Response headers come from `afterFind`.** `beforeFind`'s
99
+ `responseHeaders` are discarded. A rule's `headers` are applied in the
100
+ right place, including on a refusal, so a denial is not cached either.
101
+ 3. **Only a header authenticates.** The file GET route reads the session from
102
+ `X-Parse-Session-Token` and nowhere else — no query parameter, no cookie.
103
+ A browser `<img src>` therefore cannot fetch a governed file; fetch it and
104
+ bind a blob. The upside: nobody can "simplify" this by putting a session
105
+ token in a url, because Parse will not read one there.
106
+ 4. **A throw is global.** This runs on every file download in the app, so a
107
+ mistake does not break documents — it blanks every image at once. The
108
+ public path is one string comparison before any `await`, a rule whose
109
+ predicate throws is skipped rather than fatal, and the behaviour is
110
+ tested.
111
+ 5. **Fail-open is the invisible failure.** "I cannot tell who owns this" must
112
+ mean refuse. `isPrivatePath` (is it governed?) and `privateOwnerOf` (who
113
+ owns it?) are deliberately separate functions, because a single predicate
114
+ returning `owner | null` conflates them: it answers `null` both for an
115
+ ordinary image and for `private/<Class>/<id>/extra/doc.jpg`, one directory
116
+ deeper than expected — and would serve that document to anybody, with the
117
+ symptom that it loads perfectly.
118
+
119
+ `Parse.File.setDirectory` / `.name()` / `.directory()` are now declared, and
120
+ `Parse.Cloud.beforeFind` / `afterFind` accept a class as well as a class name.
121
+ `@types/parse` is a hand-written stub that predates file directories (and is
122
+ empty from 6.x), so these are declared once here rather than cast at every
123
+ call site.
124
+
125
+ Requires Parse Server **>= 9.5.0** for file directories; verified against
126
+ 9.10.4. 20 tests.
127
+
128
+ ### Changed
129
+
130
+ - Development now runs against `parse-server` **9.10.4** (was 9.10.1). The peer
131
+ range is unchanged at `>=9.0.0` — nothing here needs the newer server except
132
+ the file directories above, which are a 9.5.0 feature.
133
+
134
+ ---
135
+
11
136
  ## [2.11.0]
12
137
 
13
138
  ### Security
@@ -0,0 +1,235 @@
1
+ /**
2
+ * File access rules — turn a file's PATH into a permission.
3
+ *
4
+ * Parse has no per-file permission and cannot have one: the file router is
5
+ * handed a name, and a name does not know which row references it. So a file
6
+ * url is a bearer capability — anyone holding the string gets the bytes, with
7
+ * no session, no ACL check and no expiry. For published pictures that is
8
+ * correct and cheap. For a document (a certificate, an ID photo, an invoice) it
9
+ * is not: a url that leaks once works forever, for anyone, which is a
10
+ * REVOCATION problem that signed urls do not solve — a signed url is the same
11
+ * bearer token on a timer, and it cannot express "this account was banned
12
+ * thirty seconds ago".
13
+ *
14
+ * Parse does give us one hook: `beforeFind` on `Parse.File`, which runs on
15
+ * every `GET /files/...` and can throw to refuse. This module owns that hook
16
+ * and makes it usable, because using it directly has five traps:
17
+ *
18
+ * 1. **There is exactly ONE slot.** File triggers are keyed on the
19
+ * pseudo-class `@File`, not on a class, so a second
20
+ * `beforeFind(Parse.File)` anywhere REPLACES the first — Parse logs
21
+ * "Only the last one will be used" and carries on serving. Two features
22
+ * each registering their own rule would silently disable one of them.
23
+ * Hence a registry: this module holds the slot, rules compose.
24
+ * 2. **The headers come from `afterFind`.** `beforeFind`'s `responseHeaders`
25
+ * are discarded; only the `afterFind` request's are written to the
26
+ * response. A rule that marks a response uncacheable has to act there.
27
+ * 3. **Only a header authenticates.** The file GET route reads the session
28
+ * from `X-Parse-Session-Token` and nowhere else — no query parameter, no
29
+ * cookie. A browser `<img src>` therefore cannot fetch a governed file;
30
+ * the client must fetch it and bind a blob. The upside is that nobody can
31
+ * "simplify" this by putting a session token in a url.
32
+ * 4. **A throw is global.** This runs on every file download in the app, so
33
+ * a mistake does not break documents, it blanks every image at once.
34
+ * Everything on the public path here is a string comparison, before any
35
+ * `await`, and cannot throw.
36
+ * 5. **Fail-open is the invisible failure.** If "I cannot tell who owns
37
+ * this" is treated as "public", the document is served and the symptom is
38
+ * that it loads perfectly. So governed-but-unidentifiable must REFUSE.
39
+ *
40
+ * **Nothing is registered until a rule is added.** Upgrading the kit therefore
41
+ * changes no behaviour in a project that does not opt in, and a project that
42
+ * already registers its own file trigger is not quietly overridden.
43
+ *
44
+ * The usual setup, in cloud code:
45
+ *
46
+ * ```ts
47
+ * import {registerFileAccessRule, aclFileRule} from '@90soft/parse-server-kit';
48
+ * registerFileAccessRule(aclFileRule());
49
+ * ```
50
+ *
51
+ * [aclFileRule] is the general case: a file stored under
52
+ * `acl/<ClassName>/<objectId>/` is served only to a caller who can read
53
+ * THAT row, judged by Parse's own ACL and CLP. The permission is not a copy of
54
+ * the row's ACL; it IS the row's ACL — nothing to stamp, nothing to sync.
55
+ */
56
+ /**
57
+ * What a FILE trigger is handed, which is not what a class trigger gets: there
58
+ * is no `object` and no `query`, only the file.
59
+ *
60
+ * Declared HERE rather than taken from the ambient Parse types on purpose.
61
+ * `@types/parse` does not describe file triggers at all, so a consumer relying
62
+ * on it could not name the argument of [evaluateFileAccess] — a published
63
+ * library should not require its callers to declare types for its own API.
64
+ */
65
+ export interface FileTriggerRequest {
66
+ file?: Parse.File;
67
+ fileSize?: number;
68
+ master?: boolean;
69
+ isReadOnly?: boolean;
70
+ user?: Parse.User;
71
+ ip?: string;
72
+ forceDownload?: boolean;
73
+ responseHeaders?: Record<string, string>;
74
+ }
75
+ /** The decision a rule is asked to make. */
76
+ export interface FileAccessContext {
77
+ /** The stored path, including any directory prefix. */
78
+ path: string;
79
+ /** The caller, when they sent a session token. */
80
+ user?: Parse.User;
81
+ /** True for a master-key request. */
82
+ master: boolean;
83
+ }
84
+ /** One rule, governing some subset of stored files. */
85
+ export interface FileAccessRule {
86
+ /**
87
+ * Identifies the rule in diagnostics, and makes replacing one deliberate:
88
+ * registering the same name twice warns rather than stacking silently.
89
+ */
90
+ name: string;
91
+ /**
92
+ * Does this rule govern that path?
93
+ *
94
+ * **Must not throw and should not await.** It is consulted for every file
95
+ * download in the application, including every public image.
96
+ */
97
+ governs(path: string): boolean;
98
+ /**
99
+ * Decide. Throw a `Parse.Error` to refuse.
100
+ *
101
+ * Returning normally allows the download. Only called when [governs] said
102
+ * yes and the request is not master-key.
103
+ */
104
+ authorize(context: FileAccessContext): Promise<void> | void;
105
+ /**
106
+ * Headers to add to a governed response, e.g. `Cache-Control`.
107
+ *
108
+ * Applied whenever [governs] matches — including on a refusal, so that a
109
+ * denial is not cached either.
110
+ */
111
+ headers?: Record<string, string>;
112
+ }
113
+ /**
114
+ * The gate. Public files leave before anything is awaited.
115
+ *
116
+ * Exported so a project can unit-test its rules without going through HTTP.
117
+ */
118
+ export declare function evaluateFileAccess(request: FileTriggerRequest): Promise<void>;
119
+ /**
120
+ * Add the governed response's headers.
121
+ *
122
+ * Separate from [evaluateFileAccess] because Parse reads response headers from
123
+ * the `afterFind` request and ignores `beforeFind`'s.
124
+ */
125
+ export declare function applyFileAccessHeaders(request: FileTriggerRequest): void;
126
+ /**
127
+ * Register a rule, attaching the Parse hooks on the first call.
128
+ *
129
+ * Order matters: rules are consulted in registration order and the first match
130
+ * decides, so register the most specific first.
131
+ */
132
+ export declare function registerFileAccessRule(rule: FileAccessRule): void;
133
+ /** Registered rules, in order. For diagnostics and tests. */
134
+ export declare function getFileAccessRules(): FileAccessRule[];
135
+ /**
136
+ * Drop every rule. For tests.
137
+ *
138
+ * The Parse hooks stay attached — they are harmless with no rules, since every
139
+ * path is then public — and re-attaching would hit Parse's duplicate warning.
140
+ */
141
+ export declare function clearFileAccessRules(): void;
142
+ /**
143
+ * The directory prefix marking a file as governed by [aclFileRule].
144
+ *
145
+ * **Named for the mechanism, not the outcome.** It was `private` first, which
146
+ * was wrong in the most confusing way: the commonest case is a PUBLICLY
147
+ * readable row, whose file is then served to everybody — from a path that
148
+ * said "private". The prefix does not mean secret. It means **the owning row
149
+ * decides**, and a public row decides "everyone".
150
+ *
151
+ * Renamed before anything depended on it, because the prefix is baked into
152
+ * stored filenames: changing it later orphans every file already written.
153
+ *
154
+ * Parse's own `validateDirectory` reserves `metadata` and requires a directory
155
+ * to match `^[a-zA-Z0-9][a-zA-Z0-9_\-/]*$`, so a single lowercase word is safe.
156
+ * It is baked into stored file names, so changing it would orphan every file
157
+ * already written under the old one.
158
+ */
159
+ export declare const ACL_PREFIX = "acl";
160
+ /** Who an ACL-governed file's path says owns it. */
161
+ export interface AclFileOwner {
162
+ className: string;
163
+ objectId: string;
164
+ }
165
+ /**
166
+ * Is this path governed at all?
167
+ *
168
+ * Deliberately separate from [aclOwnerOf], and the separation is the
169
+ * safety property: "under the prefix" and "parses into an owner" are different
170
+ * questions, and a single predicate returning `owner | null` conflates them
171
+ * into a fail-OPEN. It would answer `null` both for an ordinary image and for
172
+ * `acl/<Class>/<id>/extra/doc.jpg` — one directory deeper than expected —
173
+ * and so serve that document to anybody.
174
+ */
175
+ export declare function isAclGoverned(path: string): boolean;
176
+ /**
177
+ * Read the owner out of a governed path, or null if it is not well formed.
178
+ *
179
+ * `null` means "I cannot tell who owns this", which the rule treats as a
180
+ * refusal — never as permission.
181
+ */
182
+ export declare function aclOwnerOf(path: string): AclFileOwner | null;
183
+ /**
184
+ * The directory a row's ACL-governed files live in.
185
+ *
186
+ * Throws rather than quietly producing something Parse will reject: a class
187
+ * whose name cannot be a path segment (`_User`, since a directory must start
188
+ * alphanumeric) surfaces here, at the one call site, instead of as a confusing
189
+ * upload failure later.
190
+ */
191
+ export declare function aclDirectoryFor(className: string, objectId: string): string;
192
+ /**
193
+ * Store a file so that the owning row's ACL governs it.
194
+ *
195
+ * The row must already be saved — its objectId is part of the path, which is
196
+ * what lets the rule find the owner with no index and no second source of
197
+ * truth. For a new record that means save, attach, save again: one extra write
198
+ * on a rare operation.
199
+ *
200
+ * **This should be the only way an application stores a governed file.** The
201
+ * prefix is the entire boundary: a document saved without it is simply public,
202
+ * and nothing anywhere will complain.
203
+ *
204
+ * Private files deliberately get no derivatives — no converted copy, no
205
+ * thumbnail, and no blurhash, which is a small preview of the document and
206
+ * travels inside DTOs where the document itself does not.
207
+ */
208
+ export declare function attachAclFile(row: Parse.Object, field: string, upload: {
209
+ base64: string;
210
+ name?: string;
211
+ }): Promise<void>;
212
+ /** Options for [aclFileRule]. */
213
+ export interface AclFileRuleOptions {
214
+ /**
215
+ * Headers for a governed response. Defaults to `private, no-store`.
216
+ *
217
+ * That `private` is the HTTP cache directive, unrelated to the prefix.
218
+ *
219
+ * One cached governed response served to the next caller undoes the whole
220
+ * check, and the symptom is that the document loads — so this defaults to
221
+ * the strict value rather than being left to the caller.
222
+ */
223
+ headers?: Record<string, string>;
224
+ }
225
+ /**
226
+ * Serve a governed file only to a caller who can read its owning row.
227
+ *
228
+ * The caller is resolved from their session token, so Parse evaluates the
229
+ * row's ACL and the class's CLP exactly as it would for a query. With no
230
+ * session the row is loaded with no auth, which is right rather than a
231
+ * shortcut: if the owning row is publicly readable then its file is public
232
+ * too, because the row's ACL is the whole rule.
233
+ */
234
+ export declare function aclFileRule(options?: AclFileRuleOptions): FileAccessRule;
235
+ //# sourceMappingURL=fileAccess.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileAccess.d.ts","sourceRoot":"","sources":["../../src/files/fileAccess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH;;;;;;;;GAQG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,4CAA4C;AAC5C,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,IAAI,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC;IAClB,qCAAqC;IACrC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAE/B;;;;;OAKG;IACH,SAAS,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAE5D;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAiCD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAef;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,kBAAkB,GAC1B,IAAI,CAON;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAsBjE;AAED,6DAA6D;AAC7D,wBAAgB,kBAAkB,IAAI,cAAc,EAAE,CAErD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,UAAU,QAAQ,CAAC;AAEhC,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAW5D;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAc3E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,KAAK,CAAC,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAC,GACtC,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED,iCAAiC;AACjC,MAAM,WAAW,kBAAkB;IACjC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,cAAc,CA8BhB"}
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ /**
3
+ * File access rules — turn a file's PATH into a permission.
4
+ *
5
+ * Parse has no per-file permission and cannot have one: the file router is
6
+ * handed a name, and a name does not know which row references it. So a file
7
+ * url is a bearer capability — anyone holding the string gets the bytes, with
8
+ * no session, no ACL check and no expiry. For published pictures that is
9
+ * correct and cheap. For a document (a certificate, an ID photo, an invoice) it
10
+ * is not: a url that leaks once works forever, for anyone, which is a
11
+ * REVOCATION problem that signed urls do not solve — a signed url is the same
12
+ * bearer token on a timer, and it cannot express "this account was banned
13
+ * thirty seconds ago".
14
+ *
15
+ * Parse does give us one hook: `beforeFind` on `Parse.File`, which runs on
16
+ * every `GET /files/...` and can throw to refuse. This module owns that hook
17
+ * and makes it usable, because using it directly has five traps:
18
+ *
19
+ * 1. **There is exactly ONE slot.** File triggers are keyed on the
20
+ * pseudo-class `@File`, not on a class, so a second
21
+ * `beforeFind(Parse.File)` anywhere REPLACES the first — Parse logs
22
+ * "Only the last one will be used" and carries on serving. Two features
23
+ * each registering their own rule would silently disable one of them.
24
+ * Hence a registry: this module holds the slot, rules compose.
25
+ * 2. **The headers come from `afterFind`.** `beforeFind`'s `responseHeaders`
26
+ * are discarded; only the `afterFind` request's are written to the
27
+ * response. A rule that marks a response uncacheable has to act there.
28
+ * 3. **Only a header authenticates.** The file GET route reads the session
29
+ * from `X-Parse-Session-Token` and nowhere else — no query parameter, no
30
+ * cookie. A browser `<img src>` therefore cannot fetch a governed file;
31
+ * the client must fetch it and bind a blob. The upside is that nobody can
32
+ * "simplify" this by putting a session token in a url.
33
+ * 4. **A throw is global.** This runs on every file download in the app, so
34
+ * a mistake does not break documents, it blanks every image at once.
35
+ * Everything on the public path here is a string comparison, before any
36
+ * `await`, and cannot throw.
37
+ * 5. **Fail-open is the invisible failure.** If "I cannot tell who owns
38
+ * this" is treated as "public", the document is served and the symptom is
39
+ * that it loads perfectly. So governed-but-unidentifiable must REFUSE.
40
+ *
41
+ * **Nothing is registered until a rule is added.** Upgrading the kit therefore
42
+ * changes no behaviour in a project that does not opt in, and a project that
43
+ * already registers its own file trigger is not quietly overridden.
44
+ *
45
+ * The usual setup, in cloud code:
46
+ *
47
+ * ```ts
48
+ * import {registerFileAccessRule, aclFileRule} from '@90soft/parse-server-kit';
49
+ * registerFileAccessRule(aclFileRule());
50
+ * ```
51
+ *
52
+ * [aclFileRule] is the general case: a file stored under
53
+ * `acl/<ClassName>/<objectId>/` is served only to a caller who can read
54
+ * THAT row, judged by Parse's own ACL and CLP. The permission is not a copy of
55
+ * the row's ACL; it IS the row's ACL — nothing to stamp, nothing to sync.
56
+ */
57
+ Object.defineProperty(exports, "__esModule", { value: true });
58
+ exports.ACL_PREFIX = void 0;
59
+ exports.evaluateFileAccess = evaluateFileAccess;
60
+ exports.applyFileAccessHeaders = applyFileAccessHeaders;
61
+ exports.registerFileAccessRule = registerFileAccessRule;
62
+ exports.getFileAccessRules = getFileAccessRules;
63
+ exports.clearFileAccessRules = clearFileAccessRules;
64
+ exports.isAclGoverned = isAclGoverned;
65
+ exports.aclOwnerOf = aclOwnerOf;
66
+ exports.aclDirectoryFor = aclDirectoryFor;
67
+ exports.attachAclFile = attachAclFile;
68
+ exports.aclFileRule = aclFileRule;
69
+ /** Ordered; the first rule that governs a path decides it. */
70
+ const rules = [];
71
+ /** The hook is attached once, lazily, on the first registration. */
72
+ let attached = false;
73
+ /**
74
+ * 404, never 403.
75
+ *
76
+ * A 403 confirms the file is there and merely forbidden, which for a document
77
+ * is itself the leak — it tells the caller that a particular row has one.
78
+ * "Not found" is the same answer an invented name would get.
79
+ */
80
+ function notFound() {
81
+ return new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'File not found');
82
+ }
83
+ /** The first rule governing this path, or undefined when the file is public. */
84
+ function ruleFor(path) {
85
+ for (const rule of rules) {
86
+ // A rule that throws here would blank every image in the app, so one
87
+ // misbehaving rule must not take the public path down with it.
88
+ try {
89
+ if (rule.governs(path))
90
+ return rule;
91
+ }
92
+ catch {
93
+ continue;
94
+ }
95
+ }
96
+ return undefined;
97
+ }
98
+ /**
99
+ * The gate. Public files leave before anything is awaited.
100
+ *
101
+ * Exported so a project can unit-test its rules without going through HTTP.
102
+ */
103
+ async function evaluateFileAccess(request) {
104
+ const path = request.file?.name() ?? '';
105
+ const rule = ruleFor(path);
106
+ if (!rule)
107
+ return;
108
+ // Cloud code reading deliberately with the master key. Note this is NOT how
109
+ // a worker that fetches a url reaches a file: a plain GET carries no key.
110
+ if (request.master)
111
+ return;
112
+ await rule.authorize({
113
+ path,
114
+ user: request.user,
115
+ master: false,
116
+ });
117
+ }
118
+ /**
119
+ * Add the governed response's headers.
120
+ *
121
+ * Separate from [evaluateFileAccess] because Parse reads response headers from
122
+ * the `afterFind` request and ignores `beforeFind`'s.
123
+ */
124
+ function applyFileAccessHeaders(request) {
125
+ const rule = ruleFor(request.file?.name() ?? '');
126
+ if (!rule?.headers)
127
+ return;
128
+ // The incoming object already carries `X-Content-Type-Options: nosniff`, so
129
+ // extend it rather than replacing it.
130
+ request.responseHeaders = { ...(request.responseHeaders ?? {}), ...rule.headers };
131
+ }
132
+ /**
133
+ * Register a rule, attaching the Parse hooks on the first call.
134
+ *
135
+ * Order matters: rules are consulted in registration order and the first match
136
+ * decides, so register the most specific first.
137
+ */
138
+ function registerFileAccessRule(rule) {
139
+ const existing = rules.findIndex(r => r.name === rule.name);
140
+ if (existing >= 0) {
141
+ console.warn(`[FileAccess] Replacing existing rule "${rule.name}". Two rules with one name is almost always an accident.`);
142
+ rules.splice(existing, 1, rule);
143
+ }
144
+ else {
145
+ rules.push(rule);
146
+ }
147
+ if (attached)
148
+ return;
149
+ attached = true;
150
+ /*
151
+ * `Parse.File` is passed as the class, which Parse maps to `@File`. This is
152
+ * the only pair of triggers in a kit project that is not declared on a model
153
+ * with `@BeforeFind` / `@AfterFind`, because there is no model to declare it
154
+ * on.
155
+ */
156
+ Parse.Cloud.beforeFind(Parse.File, evaluateFileAccess);
157
+ Parse.Cloud.afterFind(Parse.File, applyFileAccessHeaders);
158
+ }
159
+ /** Registered rules, in order. For diagnostics and tests. */
160
+ function getFileAccessRules() {
161
+ return [...rules];
162
+ }
163
+ /**
164
+ * Drop every rule. For tests.
165
+ *
166
+ * The Parse hooks stay attached — they are harmless with no rules, since every
167
+ * path is then public — and re-attaching would hit Parse's duplicate warning.
168
+ */
169
+ function clearFileAccessRules() {
170
+ rules.length = 0;
171
+ }
172
+ // ── The ACL-governed rule ───────────────────────────────────────────────────
173
+ /**
174
+ * The directory prefix marking a file as governed by [aclFileRule].
175
+ *
176
+ * **Named for the mechanism, not the outcome.** It was `private` first, which
177
+ * was wrong in the most confusing way: the commonest case is a PUBLICLY
178
+ * readable row, whose file is then served to everybody — from a path that
179
+ * said "private". The prefix does not mean secret. It means **the owning row
180
+ * decides**, and a public row decides "everyone".
181
+ *
182
+ * Renamed before anything depended on it, because the prefix is baked into
183
+ * stored filenames: changing it later orphans every file already written.
184
+ *
185
+ * Parse's own `validateDirectory` reserves `metadata` and requires a directory
186
+ * to match `^[a-zA-Z0-9][a-zA-Z0-9_\-/]*$`, so a single lowercase word is safe.
187
+ * It is baked into stored file names, so changing it would orphan every file
188
+ * already written under the old one.
189
+ */
190
+ exports.ACL_PREFIX = 'acl';
191
+ /**
192
+ * Is this path governed at all?
193
+ *
194
+ * Deliberately separate from [aclOwnerOf], and the separation is the
195
+ * safety property: "under the prefix" and "parses into an owner" are different
196
+ * questions, and a single predicate returning `owner | null` conflates them
197
+ * into a fail-OPEN. It would answer `null` both for an ordinary image and for
198
+ * `acl/<Class>/<id>/extra/doc.jpg` — one directory deeper than expected —
199
+ * and so serve that document to anybody.
200
+ */
201
+ function isAclGoverned(path) {
202
+ return typeof path === 'string' && path.startsWith(`${exports.ACL_PREFIX}/`);
203
+ }
204
+ /**
205
+ * Read the owner out of a governed path, or null if it is not well formed.
206
+ *
207
+ * `null` means "I cannot tell who owns this", which the rule treats as a
208
+ * refusal — never as permission.
209
+ */
210
+ function aclOwnerOf(path) {
211
+ if (!isAclGoverned(path))
212
+ return null;
213
+ // Exactly: acl / ClassName / objectId / filename
214
+ const parts = path.split('/');
215
+ if (parts.length !== 4)
216
+ return null;
217
+ const [, className, objectId, filename] = parts;
218
+ if (!className || !objectId || !filename)
219
+ return null;
220
+ return { className, objectId };
221
+ }
222
+ /**
223
+ * The directory a row's ACL-governed files live in.
224
+ *
225
+ * Throws rather than quietly producing something Parse will reject: a class
226
+ * whose name cannot be a path segment (`_User`, since a directory must start
227
+ * alphanumeric) surfaces here, at the one call site, instead of as a confusing
228
+ * upload failure later.
229
+ */
230
+ function aclDirectoryFor(className, objectId) {
231
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9_]*$/.test(className)) {
232
+ throw new Parse.Error(Parse.Error.OTHER_CAUSE, `${className} cannot own ACL-governed files: Parse requires a directory segment to start with a letter or digit`);
233
+ }
234
+ if (!/^[a-zA-Z0-9]+$/.test(objectId)) {
235
+ throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'An ACL-governed file needs the owning row saved first, so that it has an objectId');
236
+ }
237
+ return `${exports.ACL_PREFIX}/${className}/${objectId}`;
238
+ }
239
+ /**
240
+ * Store a file so that the owning row's ACL governs it.
241
+ *
242
+ * The row must already be saved — its objectId is part of the path, which is
243
+ * what lets the rule find the owner with no index and no second source of
244
+ * truth. For a new record that means save, attach, save again: one extra write
245
+ * on a rare operation.
246
+ *
247
+ * **This should be the only way an application stores a governed file.** The
248
+ * prefix is the entire boundary: a document saved without it is simply public,
249
+ * and nothing anywhere will complain.
250
+ *
251
+ * Private files deliberately get no derivatives — no converted copy, no
252
+ * thumbnail, and no blurhash, which is a small preview of the document and
253
+ * travels inside DTOs where the document itself does not.
254
+ */
255
+ async function attachAclFile(row, field, upload) {
256
+ if (!row.id) {
257
+ throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Save the row before attaching a governed file — its objectId is part of the path');
258
+ }
259
+ const file = new Parse.File(encodeURIComponent(upload.name ?? 'document'), {
260
+ base64: upload.base64,
261
+ });
262
+ // Sent as `X-Parse-File-Directory`; honoured by Parse Server from 9.5.0.
263
+ file.setDirectory(aclDirectoryFor(row.className, row.id));
264
+ const saved = await file.save({ useMasterKey: true });
265
+ row.set(field, saved);
266
+ }
267
+ /**
268
+ * Serve a governed file only to a caller who can read its owning row.
269
+ *
270
+ * The caller is resolved from their session token, so Parse evaluates the
271
+ * row's ACL and the class's CLP exactly as it would for a query. With no
272
+ * session the row is loaded with no auth, which is right rather than a
273
+ * shortcut: if the owning row is publicly readable then its file is public
274
+ * too, because the row's ACL is the whole rule.
275
+ */
276
+ function aclFileRule(options = {}) {
277
+ const headers = options.headers ?? { 'Cache-Control': 'private, no-store' };
278
+ return {
279
+ name: 'acl-prefix',
280
+ headers,
281
+ governs: isAclGoverned,
282
+ async authorize({ path, user }) {
283
+ /*
284
+ * Under the prefix but not a shape we understand: REFUSE.
285
+ *
286
+ * Treating it as public because no owner could be found is the one
287
+ * failure mode of this design that nobody would notice, because the
288
+ * document would load perfectly.
289
+ */
290
+ const owner = aclOwnerOf(path);
291
+ if (!owner)
292
+ throw notFound();
293
+ const sessionToken = user?.getSessionToken();
294
+ const query = new Parse.Query(owner.className);
295
+ const row = await (sessionToken
296
+ ? query.get(owner.objectId, { sessionToken })
297
+ : query.get(owner.objectId)).catch(() => null);
298
+ if (!row)
299
+ throw notFound();
300
+ },
301
+ };
302
+ }
303
+ //# sourceMappingURL=fileAccess.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fileAccess.js","sourceRoot":"","sources":["../../src/files/fileAccess.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -24,6 +24,8 @@ export { configureRoleCache, invalidateRoles, roleCacheEnabled, roleCacheStats }
24
24
  export type { RoleCacheOptions } from './utils/roleCache';
25
25
  export { validateFunctionRoutes, validateEntityRoutes, restrictRoutes, removeResultMiddleware, conditionalJsonMiddleware } from './middleware/middleware';
26
26
  export { checkRateLimit } from './middleware/rateLimit';
27
+ export { registerFileAccessRule, getFileAccessRules, clearFileAccessRules, evaluateFileAccess, applyFileAccessHeaders, aclFileRule, attachAclFile, isAclGoverned, aclOwnerOf, aclDirectoryFor, ACL_PREFIX } from './files/fileAccess';
28
+ export type { FileAccessRule, FileAccessContext, FileTriggerRequest, AclFileOwner, AclFileRuleOptions } from './files/fileAccess';
27
29
  export { createSchemaConfig } from './database/schema';
28
30
  export type { SchemaConfigOptions } from './database/schema';
29
31
  export { applyAllIndexes, applyUniqueIndexes, getUniqueIndexes, getCompoundIndexes, getFieldIndexes } from './database/indexes';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
  // @90soft/parse-server-kit — complete toolkit for Parse Server
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.getUserRoles = exports.catchError = exports.MAX_QUERY_LIMIT = exports.roleKey = exports.UserRoles = exports.cloneAcl = exports.syncImageAcl = exports.implementACL = exports.BaseModel = exports.applyMongoValidators = exports.getValidators = exports.validateOrThrow = exports.validateObject = exports.classNames = exports.RouteRegistry = exports.Route = exports.CronRegistry = exports.CronSchedule = exports.Cron = exports.TriggerRegistry = exports.AfterEvent = exports.BeforeSubscribe = exports.BeforeConnect = exports.AfterSaveConfig = exports.BeforeSaveConfig = exports.AfterFindFile = exports.BeforeFindFile = exports.AfterDeleteFile = exports.BeforeDeleteFile = exports.AfterSaveFile = exports.BeforeSaveFile = exports.BeforePasswordResetRequest = exports.AfterLogout = exports.AfterLogin = exports.BeforeLogin = exports.AfterFind = exports.BeforeFind = exports.AfterDelete = exports.BeforeDelete = exports.AfterSave = exports.BeforeSave = exports.CloudFunctionRegistry = exports.onFunctionRegistered = exports.ProtectedCloudFunction = exports.CloudFunction = exports.onFieldRegistered = exports.onClassRegistered = exports.getSchemaDefinition = exports.ParseField = exports.ParseClass = void 0;
5
- exports.setupSwagger = exports.getSwaggerJson = exports.generateSwaggerSpec = exports.SwaggerRegistry = exports.NO_AMBIENT_TRANSACTION = exports.withAmbientSession = exports.CONFLICT_MESSAGE = exports.CONFLICT = exports.useTransactionAdapter = exports.currentSession = exports.inTransaction = exports.withTransaction = exports.Transactional = exports.VERSION_CONFLICT_MESSAGE = exports.VERSION_CONFLICT = exports.createVersionedMongoAdapter = exports.VersionRegistry = exports.ParseVersionField = exports.getFieldIndexes = exports.getCompoundIndexes = exports.getUniqueIndexes = exports.applyUniqueIndexes = exports.applyAllIndexes = exports.createSchemaConfig = exports.checkRateLimit = exports.conditionalJsonMiddleware = exports.removeResultMiddleware = exports.restrictRoutes = exports.validateEntityRoutes = exports.validateFunctionRoutes = exports.roleCacheStats = exports.roleCacheEnabled = exports.invalidateRoles = exports.configureRoleCache = exports.importFiles = exports.sleep = exports.generateRandomString = exports.generateRandomInteger = exports.generateRandomPassword = exports.formatCount = exports.getUsersRoles = void 0;
5
+ exports.generateSwaggerSpec = exports.SwaggerRegistry = exports.NO_AMBIENT_TRANSACTION = exports.withAmbientSession = exports.CONFLICT_MESSAGE = exports.CONFLICT = exports.useTransactionAdapter = exports.currentSession = exports.inTransaction = exports.withTransaction = exports.Transactional = exports.VERSION_CONFLICT_MESSAGE = exports.VERSION_CONFLICT = exports.createVersionedMongoAdapter = exports.VersionRegistry = exports.ParseVersionField = exports.getFieldIndexes = exports.getCompoundIndexes = exports.getUniqueIndexes = exports.applyUniqueIndexes = exports.applyAllIndexes = exports.createSchemaConfig = exports.ACL_PREFIX = exports.aclDirectoryFor = exports.aclOwnerOf = exports.isAclGoverned = exports.attachAclFile = exports.aclFileRule = exports.applyFileAccessHeaders = exports.evaluateFileAccess = exports.clearFileAccessRules = exports.getFileAccessRules = exports.registerFileAccessRule = exports.checkRateLimit = exports.conditionalJsonMiddleware = exports.removeResultMiddleware = exports.restrictRoutes = exports.validateEntityRoutes = exports.validateFunctionRoutes = exports.roleCacheStats = exports.roleCacheEnabled = exports.invalidateRoles = exports.configureRoleCache = exports.importFiles = exports.sleep = exports.generateRandomString = exports.generateRandomInteger = exports.generateRandomPassword = exports.formatCount = exports.getUsersRoles = void 0;
6
+ exports.setupSwagger = exports.getSwaggerJson = void 0;
6
7
  // ── Decorators ──
7
8
  var parseDecorators_1 = require("./decorators/parseDecorators");
8
9
  Object.defineProperty(exports, "ParseClass", { enumerable: true, get: function () { return parseDecorators_1.ParseClass; } });
@@ -96,6 +97,23 @@ Object.defineProperty(exports, "removeResultMiddleware", { enumerable: true, get
96
97
  Object.defineProperty(exports, "conditionalJsonMiddleware", { enumerable: true, get: function () { return middleware_1.conditionalJsonMiddleware; } });
97
98
  var rateLimit_1 = require("./middleware/rateLimit");
98
99
  Object.defineProperty(exports, "checkRateLimit", { enumerable: true, get: function () { return rateLimit_1.checkRateLimit; } });
100
+ // ── File access ──────────────────────────────────────────────────────────────
101
+ // Parse has no per-file permission; these turn a file's PATH into one. The kit
102
+ // owns the single `beforeFind(Parse.File)` slot so that rules compose instead
103
+ // of silently replacing each other, and nothing is registered until a rule is
104
+ // added — so upgrading changes no behaviour for a project that does not opt in.
105
+ var fileAccess_1 = require("./files/fileAccess");
106
+ Object.defineProperty(exports, "registerFileAccessRule", { enumerable: true, get: function () { return fileAccess_1.registerFileAccessRule; } });
107
+ Object.defineProperty(exports, "getFileAccessRules", { enumerable: true, get: function () { return fileAccess_1.getFileAccessRules; } });
108
+ Object.defineProperty(exports, "clearFileAccessRules", { enumerable: true, get: function () { return fileAccess_1.clearFileAccessRules; } });
109
+ Object.defineProperty(exports, "evaluateFileAccess", { enumerable: true, get: function () { return fileAccess_1.evaluateFileAccess; } });
110
+ Object.defineProperty(exports, "applyFileAccessHeaders", { enumerable: true, get: function () { return fileAccess_1.applyFileAccessHeaders; } });
111
+ Object.defineProperty(exports, "aclFileRule", { enumerable: true, get: function () { return fileAccess_1.aclFileRule; } });
112
+ Object.defineProperty(exports, "attachAclFile", { enumerable: true, get: function () { return fileAccess_1.attachAclFile; } });
113
+ Object.defineProperty(exports, "isAclGoverned", { enumerable: true, get: function () { return fileAccess_1.isAclGoverned; } });
114
+ Object.defineProperty(exports, "aclOwnerOf", { enumerable: true, get: function () { return fileAccess_1.aclOwnerOf; } });
115
+ Object.defineProperty(exports, "aclDirectoryFor", { enumerable: true, get: function () { return fileAccess_1.aclDirectoryFor; } });
116
+ Object.defineProperty(exports, "ACL_PREFIX", { enumerable: true, get: function () { return fileAccess_1.ACL_PREFIX; } });
99
117
  // ── Database ──
100
118
  var schema_1 = require("./database/schema");
101
119
  Object.defineProperty(exports, "createSchemaConfig", { enumerable: true, get: function () { return schema_1.createSchemaConfig; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,+DAA+D;;;;AAE/D,mBAAmB;AACnB,gEAAiI;AAAxH,6GAAA,UAAU,OAAA;AAAE,6GAAA,UAAU,OAAA;AAAE,sHAAA,mBAAmB,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAAE,oHAAA,iBAAiB,OAAA;AAG1F,8DAA0G;AAAjG,+GAAA,aAAa,OAAA;AAAE,wHAAA,sBAAsB,OAAA;AAAE,sHAAA,oBAAoB,OAAA;AACpE,4DAAmE;AAA1D,sHAAA,qBAAqB,OAAA;AAE9B,kEAKoF;AAL3E,8GAAA,UAAU,OAAA;AAAE,6GAAA,SAAS,OAAA;AAAE,gHAAA,YAAY,OAAA;AAAE,+GAAA,WAAW,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,6GAAA,SAAS,OAAA;AAC9E,+GAAA,WAAW,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,+GAAA,WAAW,OAAA;AAAE,8HAAA,0BAA0B,OAAA;AAChE,kHAAA,cAAc,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,oHAAA,gBAAgB,OAAA;AAAE,mHAAA,eAAe,OAAA;AAChE,kHAAA,cAAc,OAAA;AAAE,iHAAA,aAAa,OAAA;AAC7B,oHAAA,gBAAgB,OAAA;AAAE,mHAAA,eAAe,OAAA;AACjC,iHAAA,aAAa,OAAA;AAAE,mHAAA,eAAe,OAAA;AAAE,8GAAA,UAAU,OAAA;AAC5C,gEAA+D;AAAtD,kHAAA,eAAe,OAAA;AAExB,4DAA8E;AAArE,qGAAA,IAAI,OAAA;AAAE,6GAAA,YAAY,OAAA;AAAE,6GAAA,YAAY,OAAA;AAEzC,8DAAmE;AAA1D,uGAAA,KAAK,OAAA;AAAE,+GAAA,aAAa,OAAA;AAE7B,cAAc;AACd,8DAA4D;AAAnD,yGAAA,UAAU,OAAA;AASnB,mBAAmB;AACnB,sEAA0H;AAAjH,iHAAA,cAAc,OAAA;AAAE,kHAAA,eAAe,OAAA;AAAE,gHAAA,aAAa,OAAA;AAAE,uHAAA,oBAAoB,OAAA;AAE7E,eAAe;AACf,gDAA+C;AAAtC,sGAAA,SAAS,OAAA;AAElB,0BAA0B;AAC1B,mCAA2C;AAAlC,mGAAA,YAAY,OAAA;AACrB,6CAA0D;AAAjD,wGAAA,YAAY,OAAA;AAAE,oGAAA,QAAQ,OAAA;AAC/B,+CAAwE;AAA/D,sGAAA,SAAS,OAAA;AAAE,oGAAA,OAAO,OAAA;AAAE,4GAAA,eAAe,OAAA;AAG5C,kBAAkB;AAClB,yCACqG;AAD5F,oGAAA,UAAU,OAAA;AAAE,sGAAA,YAAY,OAAA;AAAE,uGAAA,aAAa,OAAA;AAAE,qGAAA,WAAW,OAAA;AAC3D,gHAAA,sBAAsB,OAAA;AAAE,+GAAA,qBAAqB,OAAA;AAAE,8GAAA,oBAAoB,OAAA;AAAE,+FAAA,KAAK,OAAA;AAC5E,uDAAoD;AAA3C,4GAAA,WAAW,OAAA;AAEpB,4BAA4B;AAC5B,+CAA0G;AAAjG,+GAAA,kBAAkB,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAG9E,mBAAmB;AACnB,sDACqF;AAD5E,oHAAA,sBAAsB,OAAA;AAAE,kHAAA,oBAAoB,OAAA;AAAE,4GAAA,cAAc,OAAA;AACnE,oHAAA,sBAAsB,OAAA;AAAE,uHAAA,yBAAyB,OAAA;AACnD,oDAAwD;AAA/C,2GAAA,cAAc,OAAA;AAEvB,iBAAiB;AACjB,4CAAuD;AAA9C,4GAAA,kBAAkB,OAAA;AAE3B,8CAAgI;AAAvH,0GAAA,eAAe,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,6GAAA,kBAAkB,OAAA;AAAE,0GAAA,eAAe,OAAA;AAEnG,2BAA2B;AAC3B,8DAAgF;AAAvE,oHAAA,iBAAiB,OAAA;AAAE,kHAAA,eAAe,OAAA;AAC3C,0EAA2H;AAAlH,oIAAA,2BAA2B,OAAA;AAAE,yHAAA,gBAAgB,OAAA;AAAE,iIAAA,wBAAwB,OAAA;AAGhF,qBAAqB;AACrB,kDACoF;AAD3E,wGAAA,aAAa,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,wGAAA,aAAa,OAAA;AAAE,yGAAA,cAAc,OAAA;AACpE,gHAAA,qBAAqB,OAAA;AAAE,mGAAA,QAAQ,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAEnD,gEAA2F;AAAlF,oHAAA,kBAAkB,OAAA;AAAE,wHAAA,sBAAsB,OAAA;AAEnD,gBAAgB;AAChB,6DAA4D;AAAnD,kHAAA,eAAe,OAAA;AAExB,qDAA4E;AAAnE,kHAAA,mBAAmB,OAAA;AAAE,6GAAA,cAAc,OAAA;AAC5C,yCAA+C;AAAtC,qGAAA,YAAY,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@90soft/parse-server-kit",
3
- "version": "2.11.0",
3
+ "version": "2.13.0",
4
4
  "description": "Complete decorator-driven development kit for Parse Server — models, cloud functions, triggers, cron, routing, ACL, Swagger, and more.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -68,7 +68,7 @@
68
68
  "mongodb-memory-server": "^11.2.0",
69
69
  "node-cron": "^4.0.0",
70
70
  "parse": "^8.6.2",
71
- "parse-server": "^9.10.1",
71
+ "parse-server": "^9.10.4",
72
72
  "reflect-metadata": "^0.2.2",
73
73
  "swagger-ui-express": "^5.0.1",
74
74
  "ts-jest": "^29.2.0",