spiderly 19.8.9 → 19.8.10
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/agent/docs/angular-customization/references/controls.generated.md +1 -1
- package/agent/docs/authorization/index.md +10 -7
- package/agent/docs/backend-hooks/index.md +2 -1
- package/agent/docs/entity-design/references/attributes.generated.md +1 -1
- package/agent/docs/file-storage/index.md +23 -9
- package/agent/skills/api-keys/SKILL.md +7 -8
- package/agent/skills/verify-ui/SKILL.md +19 -1
- package/fesm2022/spiderly.mjs +10 -4
- package/fesm2022/spiderly.mjs.map +1 -1
- package/lib/controls/spiderly-editor/spiderly-editor.component.d.ts +3 -1
- package/package.json +1 -1
|
@@ -12,7 +12,7 @@ Every control also accepts the shared `BaseControl` inputs: `control`, `controlV
|
|
|
12
12
|
| `spiderly-checkbox` | `SpiderlyCheckboxComponent` | `fakeLabel`, `initializeToFalse`, `inlineLabel` |
|
|
13
13
|
| `spiderly-colorpicker` | `SpiderlyColorPickerComponent` | `showInputTextField` |
|
|
14
14
|
| `spiderly-dropdown` | `SpiderlyDropdownComponent` | `isBooleanPicker` |
|
|
15
|
-
| `spiderly-editor` | `SpiderlyEditorComponent` | `uploadImageMethod`, `objectId` |
|
|
15
|
+
| `spiderly-editor` | `SpiderlyEditorComponent` | `uploadImageMethod`, `objectId`, `acceptedFileTypes` |
|
|
16
16
|
| `spiderly-file` | `SpiderlyFileComponent` | `objectId`, `fileData`, `acceptedFileTypes`, `required`, `multiple`, `isUrlFileData`, `imageWidth`, `imageHeight`, `maxFileSize`, `files` |
|
|
17
17
|
| `spiderly-markdown` | `SpiderlyMarkdownComponent` | `uploadImageMethod`, `objectId` |
|
|
18
18
|
| `spiderly-multiautocomplete` | `SpiderlyMultiAutocompleteComponent` | — |
|
|
@@ -243,18 +243,21 @@ public virtual async Task AuthorizeProductDeleteAndThrow(long id)
|
|
|
243
243
|
|
|
244
244
|
## Registering Principal Kinds
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
without a `principal_kind` claim (it is the sole, default kind)
|
|
246
|
+
The `User` principal kind is registered for you by `spiderly.AddSecurity<User, UserExternalLogin, AuthorizationService>(…)`
|
|
247
|
+
in `Startup.cs` — a single-principal app needs nothing more. The kind-dispatched authorization then resolves
|
|
248
|
+
`User` without a `principal_kind` claim (it is the sole, default kind).
|
|
249
|
+
|
|
250
|
+
To add **another** principal kind (a machine/service account, …), register it with `AddSpiderlyPrincipal<T>`
|
|
251
|
+
in `AddAppServices`:
|
|
249
252
|
|
|
250
253
|
```csharp
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
// services.AddSpiderlyPrincipal<ServiceAccount>("ServiceAccount");
|
|
254
|
+
// The User kind is already registered by AddSecurity — add a line per *additional* kind:
|
|
255
|
+
services.AddSpiderlyPrincipal<ServiceAccount>("ServiceAccount");
|
|
254
256
|
```
|
|
255
257
|
|
|
256
258
|
Each kind is any entity implementing `ISecurityPrincipal` with its own `Roles` into the shared
|
|
257
|
-
Role/Permission catalog.
|
|
259
|
+
Role/Permission catalog. API keys have a dedicated sub-builder — `AddSecurity<…>(s => s.AddApiKeys<ApiKey>())` —
|
|
260
|
+
which registers the `ApiKey` kind and its auth scheme together (see the API-keys docs).
|
|
258
261
|
|
|
259
262
|
## Checking Permissions in Custom Code
|
|
260
263
|
|
|
@@ -107,7 +107,8 @@ public virtual async Task OnBefore{Property}BlobFor{Entity}UploadIsAuthorized(
|
|
|
107
107
|
public virtual async Task<byte[]> OnBefore{Property}BlobFor{Entity}IsUploaded(
|
|
108
108
|
Stream stream, IFormFile file, {IdType} id) { }
|
|
109
109
|
|
|
110
|
-
// Image-specific hooks (called by OnBefore*IsUploaded for image
|
|
110
|
+
// Image-specific hooks (called by OnBefore*IsUploaded for raster image content
|
|
111
|
+
// types only — Helper.IsOptimizableImage; SVG/video/PDF pass through raw)
|
|
111
112
|
public virtual async Task ValidateImageFor{Property}Of{Entity}(
|
|
112
113
|
Stream stream, IFormFile file, {IdType} id) { }
|
|
113
114
|
public virtual async Task<byte[]> OptimizeImageFor{Property}Of{Entity}(
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
| Attribute | Target | Description |
|
|
7
7
|
| --- | --- | --- |
|
|
8
|
-
| `[AcceptedFileTypes]` | Property | Specifies the accepted file types for a blob property.
|
|
8
|
+
| `[AcceptedFileTypes]` | Property | Specifies the accepted file types for a blob property. Mandatory on every blob property (build error SPIDERLY014 when missing) alongside any StorageAttribute subclass (e.g. [S3PublicStorage]). Entry semantics: — MIME entries ("image/png") are enforced server-side (declared type whitelist + content inspection) and passed to the UI file picker. — Type wildcards ("image/*") match any declared type with that prefix. — Extension entries (".pdf" — leading dot, no '/') only widen the UI file picker; the server validates the MIME entries, so always pair an extension with its MIME type. — "image/svg+xml" is validated structurally (XML with an <svg> root, active content rejected) since SVG has no magic bytes, and is uploaded as-is (no ImageSharp optimization). |
|
|
9
9
|
| `[AuthGuard]` | Class, Method | Provides authentication protection for API endpoints by validating JWT tokens in the request. |
|
|
10
10
|
| `[CascadeDelete]` | Property | Implements cascade delete behavior in many-to-one relationships. When the referenced entity is deleted, all entities that reference it will automatically be deleted as well. This attribute is useful when: - Child entities should not exist without their parent |
|
|
11
11
|
| `[ComplexManyToManyList]` | Property | Generates an editable list UI for complex many-to-many relationships (junction tables with additional fields). Shows ALL entities from the "other side" with editable junction fields. No add/remove/reorder controls. Warning: This loads all "other side" entities into the form. Suitable for small sets (e.g., 3 warehouses), not for large sets (e.g., thousands of entities). |
|
|
@@ -154,14 +154,28 @@ Generated methods per blob property:
|
|
|
154
154
|
```
|
|
155
155
|
1. Upload{Property}For{Entity}(IFormFile file) ← Controller endpoint
|
|
156
156
|
2. → OnBefore{Property}BlobFor{Entity}UploadIsAuthorized(file, id)
|
|
157
|
-
3. →
|
|
158
|
-
4.
|
|
159
|
-
5.
|
|
160
|
-
6. →
|
|
161
|
-
7.
|
|
162
|
-
8.
|
|
157
|
+
3. → Helper.ValidateFileSize + Helper.ValidateFileSignature (whitelist + content inspection)
|
|
158
|
+
4. → OnBefore{Property}BlobFor{Entity}IsUploaded(stream, file, id)
|
|
159
|
+
5. → For raster image content types (Helper.IsOptimizableImage — image/* except SVG):
|
|
160
|
+
6. → ValidateImageFor{Property}Of{Entity}(stream, file, id)
|
|
161
|
+
7. → OptimizeImageFor{Property}Of{Entity}(stream, file, id)
|
|
162
|
+
8. → Everything else (SVG, video, PDF, …) passes through raw
|
|
163
|
+
9. → storageService.UploadFileAsync(...) ← resolved per [*Storage] attribute
|
|
164
|
+
10. → Returns file key/URL (semantics depend on the adapter)
|
|
163
165
|
```
|
|
164
166
|
|
|
167
|
+
### File-type validation semantics
|
|
168
|
+
|
|
169
|
+
`Helper.ValidateFileSignature` enforces the `[AcceptedFileTypes]` whitelist server-side:
|
|
170
|
+
|
|
171
|
+
- MIME entries (`"image/png"`) must match the declared content type — exactly, or by prefix for type wildcards (`"image/*"`).
|
|
172
|
+
- Extension entries (`".pdf"` — leading dot, no `/`) only widen the UI file picker; always pair them with the MIME type.
|
|
173
|
+
- Binary types are then verified against the stream's **magic bytes** (Mime-Detective), so a spoofed `Content-Type` header is rejected.
|
|
174
|
+
- `image/svg+xml` has no magic bytes — it is validated **structurally** instead: must parse as XML with an `<svg>` root, and active content (script elements, `on*` event attributes, `javascript:` hrefs, `foreignObject`) is rejected. SVG also bypasses ImageSharp optimization (step 5) and uploads as-is.
|
|
175
|
+
- Failures throw `BusinessException` (HTTP 400, localized message shown to the user — keys `FileTypeNotAllowed`, `FileContentDoesNotMatchType`, `FileContainsActiveContent`, `FileIsEmpty`, `FileSizeExceeded`).
|
|
176
|
+
|
|
177
|
+
Inline editor image uploads (`Upload{Property}ImageFor{Entity}` for `[Editor]`/`[Markdown]` + `[S3PublicStorage]` properties) run the same size + type validation against the property's `[AcceptedFileTypes]`. Raster images are optimized and return their pixel dimensions; SVG uploads as-is and its dimensions are best-effort (`Helper.GetSvgDimensions`: root width/height, then viewBox, else `(0, 0)` — the editor omits width/height attributes when unknown).
|
|
178
|
+
|
|
165
179
|
On entity save (Update/Insert):
|
|
166
180
|
|
|
167
181
|
```
|
|
@@ -184,8 +198,8 @@ public override async Task OnBeforeMainImageBlobForProductUploadIsAuthorized(
|
|
|
184
198
|
public override async Task<byte[]> OnBeforeMainImageBlobForProductIsUploaded(
|
|
185
199
|
Stream stream, IFormFile file, long id)
|
|
186
200
|
{
|
|
187
|
-
// For images: validate then optimize
|
|
188
|
-
if (file.ContentType
|
|
201
|
+
// For raster images: validate then optimize (SVG/video/PDF pass through raw)
|
|
202
|
+
if (Helper.IsOptimizableImage(file.ContentType))
|
|
189
203
|
{
|
|
190
204
|
await ValidateImageForMainImageOfProduct(stream, file, id);
|
|
191
205
|
stream.Position = 0;
|
|
@@ -233,7 +247,7 @@ public static async Task ValidateImageDimensions(
|
|
|
233
247
|
)
|
|
234
248
|
```
|
|
235
249
|
|
|
236
|
-
Throws `
|
|
250
|
+
Throws `BusinessException` (localized, shown to the user) if dimensions don't match exactly.
|
|
237
251
|
|
|
238
252
|
## Cleanup methods
|
|
239
253
|
|
|
@@ -20,8 +20,8 @@ Modeling the key as its own `ISecurityPrincipal` (rather than "resolve to the ow
|
|
|
20
20
|
|
|
21
21
|
## Step 0 — Prerequisites
|
|
22
22
|
|
|
23
|
-
1. **Spiderly version** — confirm the compiled mechanism is present. It must expose `Spiderly.Security.Authentication.IApiKeyAuthenticator`, `
|
|
24
|
-
2. **The app must already use Spiderly auth** —
|
|
23
|
+
1. **Spiderly version** — confirm the compiled mechanism is present. It must expose `Spiderly.Security.Authentication.IApiKeyAuthenticator`, the `AddApiKeys<TApiKey>()` sub-builder on `SpiderlySecurityBuilder`, and `PrincipalKinds.ApiKey`. If those don't resolve, the app is on too old a Spiderly version — upgrade first (see the `spiderly-upgrade` skill).
|
|
24
|
+
2. **The app must already use Spiderly auth** — its `Startup.cs` calls `spiderly.AddSecurity<User, UserExternalLogin, AuthorizationService>(…)`, which registers the human `User` principal and enables authentication. API keys add a *second* principal kind alongside it.
|
|
25
25
|
|
|
26
26
|
Decide with the user up front: **do they want an admin UI** to manage keys, or **API/agent-only** (drive generation via the REST endpoints / a management skill)? The backend is identical either way; the UI is the optional Step 8.
|
|
27
27
|
|
|
@@ -90,7 +90,7 @@ Add the inverse M2M nav `public virtual List<ApiKey> ApiKeys { get; } = new(); /
|
|
|
90
90
|
|
|
91
91
|
The framework ships `DefaultApiKeyAuthenticator<TApiKey>`, which does the hash→active-id lookup over your `ApiKey` entity generically (it reads it via the `IApiKey` interface, so it stays decoupled from your namespace). You get it for free from the Step 4 registration — there is **no authenticator to hand-write**.
|
|
92
92
|
|
|
93
|
-
Only implement your own `IApiKeyAuthenticator` for a non-standard lookup (e.g. an external key store). If you do, register it **before** `
|
|
93
|
+
Only implement your own `IApiKeyAuthenticator` for a non-standard lookup (e.g. an external key store). If you do, register it **before** the `AddApiKeys<ApiKey>()` call (Step 4) — the framework adds the default with `TryAdd`, so your registration wins.
|
|
94
94
|
|
|
95
95
|
## Step 3 — `ApiKeyService` hooks (generate + hash + show-once)
|
|
96
96
|
|
|
@@ -149,16 +149,15 @@ public class ApiKeyService : ApiKeyServiceGenerated
|
|
|
149
149
|
|
|
150
150
|
`ApiKeyHelper` is `Spiderly.Security.Authentication`. If the app's authorization service has a different class name, inject that.
|
|
151
151
|
|
|
152
|
-
## Step 4 — Register the
|
|
152
|
+
## Step 4 — Register the key (API-keys sub-builder)
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
Pass an `s => s.AddApiKeys<ApiKey>()` configurator to the existing `spiderly.AddSecurity<…>(…)` call in `Startup.cs`:
|
|
155
155
|
|
|
156
156
|
```csharp
|
|
157
|
-
|
|
158
|
-
services.AddSpiderlyApiKeyAuthentication<ApiKey>();
|
|
157
|
+
spiderly.AddSecurity<User, UserExternalLogin, AuthorizationService>(s => s.AddApiKeys<ApiKey>());
|
|
159
158
|
```
|
|
160
159
|
|
|
161
|
-
`
|
|
160
|
+
`AddApiKeys<ApiKey>()` registers the `ApiKey` principal kind **and** the authentication scheme in one call — the `ApiKey` scheme plus a forwarding policy scheme set as the default, so `X-Api-Key` requests route to the key handler and everything else to JWT, with no per-endpoint `[Authorize(AuthenticationSchemes=...)]` churn. Registering a second principal kind makes the app multi-principal; the JWT login already stamps `PrincipalKinds.User`, the key handler stamps `PrincipalKinds.ApiKey`, so both satisfy the now-required `principal_kind` claim.
|
|
162
161
|
|
|
163
162
|
## Step 5 — The issuance guard (authorization service)
|
|
164
163
|
|
|
@@ -10,7 +10,25 @@ The Spiderly admin panel sits entirely behind a passwordless **email-code** logi
|
|
|
10
10
|
|
|
11
11
|
**This is for ad-hoc visual verification, not test authoring.** If you're writing a Playwright suite or debugging a failing CI run, use the `e2e-testing` skill — it owns the canonical login helper and the PrimeNG selector quirks.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Two auth flows — check which one first
|
|
14
|
+
|
|
15
|
+
- **Header flow** (`Login` + `RefreshTokenWithHeaders`): tokens in localStorage → recipe below.
|
|
16
|
+
- **Cookie flow** (`LoginWithCookies` + `RefreshTokenWithCookies`): the refresh token is an HttpOnly cookie — localStorage injection can't work. Symptom: bearer fetches return 200, yet every load bounces to `/login` with a 401 on `RefreshTokenWithCookies`. Log in **from inside the browser** with `credentials: 'include'` instead:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
// eval on the admin origin; email must have the Admin role
|
|
20
|
+
const browserId = 'verify-ui';
|
|
21
|
+
localStorage.setItem('browser_id', browserId); // must match the login below
|
|
22
|
+
const { verificationCode } = await (await fetch(API + '/api/Security/SendLoginVerificationEmail', {
|
|
23
|
+
method: 'POST', headers: {'Content-Type':'application/json'}, credentials: 'include',
|
|
24
|
+
body: JSON.stringify({ email, browserId }) })).json();
|
|
25
|
+
await fetch(API + '/api/Security/LoginWithCookies', { method: 'POST',
|
|
26
|
+
headers: {'Content-Type':'application/json'}, credentials: 'include',
|
|
27
|
+
body: JSON.stringify({ verificationCode, email, browserId }) });
|
|
28
|
+
// reload — boot's RefreshTokenWithCookies now finds the cookie
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## The core idea — header flow (driver-agnostic)
|
|
14
32
|
|
|
15
33
|
In development, `SecurityService.SendLoginVerificationEmail` returns the verification code **in the response body** (when `ShouldShowVerificationCodeInNotification()` is true — i.e. `IWebHostEnvironment.IsDevelopment()` **and** SMTP is not fully configured, so `emailingService.IsConfigured()` is false). So no email inbox is needed. The flow is always:
|
|
16
34
|
|
package/fesm2022/spiderly.mjs
CHANGED
|
@@ -1128,7 +1128,7 @@ class SpiderlyEditorComponent extends BaseControl {
|
|
|
1128
1128
|
imageHandler(quill) {
|
|
1129
1129
|
const input = document.createElement('input');
|
|
1130
1130
|
input.setAttribute('type', 'file');
|
|
1131
|
-
input.setAttribute('accept', 'image/*');
|
|
1131
|
+
input.setAttribute('accept', this.acceptedFileTypes?.join(',') ?? 'image/*');
|
|
1132
1132
|
input.click();
|
|
1133
1133
|
input.onchange = async () => {
|
|
1134
1134
|
const file = input.files[0];
|
|
@@ -1140,8 +1140,12 @@ class SpiderlyEditorComponent extends BaseControl {
|
|
|
1140
1140
|
quill.insertEmbed(range.index, 'image', result.url);
|
|
1141
1141
|
// Quill 2's built-in Image blot recognizes width/height as native attributes,
|
|
1142
1142
|
// so formatText writes them directly onto the rendered <img>. Storefront uses
|
|
1143
|
-
// these to size the image up-front and prevent CLS.
|
|
1144
|
-
|
|
1143
|
+
// these to size the image up-front and prevent CLS. (0, 0) means the server
|
|
1144
|
+
// couldn't determine an intrinsic size (e.g. an SVG without width/viewBox) —
|
|
1145
|
+
// omit the attributes entirely rather than render an invisible 0×0 image.
|
|
1146
|
+
if (result.width && result.height) {
|
|
1147
|
+
quill.formatText(range.index, 1, { width: result.width, height: result.height });
|
|
1148
|
+
}
|
|
1145
1149
|
quill.setSelection(range.index + 1);
|
|
1146
1150
|
});
|
|
1147
1151
|
}
|
|
@@ -1154,7 +1158,7 @@ class SpiderlyEditorComponent extends BaseControl {
|
|
|
1154
1158
|
};
|
|
1155
1159
|
}
|
|
1156
1160
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyEditorComponent, deps: [{ token: i1.TranslocoService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1157
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyEditorComponent, isStandalone: true, selector: "spiderly-editor", inputs: { uploadImageMethod: "uploadImageMethod", objectId: "objectId" }, viewQueries: [{ propertyName: "editor", first: true, predicate: Editor, descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 0 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n (onInit)=\"onEditorInit($event)\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n <small *ngIf=\"control?.errors && control?.dirty\" class=\"spiderly-error-message\">\n {{ getValidationErrrorMessages() }}\n </small>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i4$8.Editor, selector: "p-editor", inputs: ["style", "styleClass", "placeholder", "formats", "modules", "bounds", "scrollingContainer", "debug", "readonly"], outputs: ["onInit", "onTextChange", "onSelectionChange"] }, { kind: "component", type: RequiredComponent, selector: "required" }] }); }
|
|
1161
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.13", type: SpiderlyEditorComponent, isStandalone: true, selector: "spiderly-editor", inputs: { uploadImageMethod: "uploadImageMethod", objectId: "objectId", acceptedFileTypes: "acceptedFileTypes" }, viewQueries: [{ propertyName: "editor", first: true, predicate: Editor, descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Can't put (onBlur) in this control -->\n\n<div style=\"display: flex; flex-direction: column; gap: 0.5rem; padding: 0 1px\">\n <div *ngIf=\"getTranslatedLabel() != '' && getTranslatedLabel() != null\">\n <label>{{ getTranslatedLabel() }}</label>\n <required *ngIf=\"control?.required && showRequired\"></required>\n </div>\n\n <!-- Disable doesn't work on this control -->\n <p-editor\n *ngIf=\"control\"\n [formControl]=\"control\"\n [readonly]=\"control.disabled\"\n [class]=\"control.invalid && control.dirty ? 'control-error-border' : ''\"\n [id]=\"control.label\"\n [placeholder]=\"placeholder\"\n (click)=\"onClick()\"\n (onInit)=\"onEditorInit($event)\"\n [style]=\"{ height: '320px' }\"\n ></p-editor>\n <small *ngIf=\"control?.errors && control?.dirty\" class=\"spiderly-error-message\">\n {{ getValidationErrrorMessages() }}\n </small>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: EditorModule }, { kind: "component", type: i4$8.Editor, selector: "p-editor", inputs: ["style", "styleClass", "placeholder", "formats", "modules", "bounds", "scrollingContainer", "debug", "readonly"], outputs: ["onInit", "onTextChange", "onSelectionChange"] }, { kind: "component", type: RequiredComponent, selector: "required" }] }); }
|
|
1158
1162
|
}
|
|
1159
1163
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImport: i0, type: SpiderlyEditorComponent, decorators: [{
|
|
1160
1164
|
type: Component,
|
|
@@ -1172,6 +1176,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.13", ngImpo
|
|
|
1172
1176
|
type: Input
|
|
1173
1177
|
}], objectId: [{
|
|
1174
1178
|
type: Input
|
|
1179
|
+
}], acceptedFileTypes: [{
|
|
1180
|
+
type: Input
|
|
1175
1181
|
}] } });
|
|
1176
1182
|
|
|
1177
1183
|
/**
|